Translate

Mostrando las entradas con la etiqueta jni. Mostrar todas las entradas
Mostrando las entradas con la etiqueta jni. Mostrar todas las entradas

domingo, 14 de junio de 2026

FFM API en Java: Accediendo a Memoria Nativa y Código Nativo sin JNI


La FFM API (Foreign Function & Memory API) es una de las incorporaciones más interesantes de Java moderno.


Su objetivo es permitir que una aplicación Java pueda:

  • Llamar funciones escritas en C.
  • Acceder a memoria fuera del heap de la JVM.
  • Reemplazar gran parte del uso de JNI.
  • Interactuar con librerías nativas de forma más segura y eficiente.


La FFM API forma parte del proyecto Project Panama.

Pero demos un paso para atras,  ¿Por qué existía JNI?


Durante años, si una aplicación Java necesitaba:

  • acceder a una librería en C,
  • usar código del sistema operativo,
  • trabajar con drivers,
  • o ejecutar operaciones de bajo nivel,

la única opción era usar JNI (Java Native Interface)


El problema es que JNI:

  • es complejo,
  • verboso,
  • inseguro,
  • difícil de debuggear,
  • y propenso a crashes de la JVM.


Además, obliga a escribir:

  • código Java,
  • código C,
  • headers,
  • compilación nativa,
  • glue code.


Puff si no habre puteado con JNI.


¿Qué propone la FFM API?

La FFM API permite hacer esto:

printf("Hola desde C!\n");


...directamente desde Java.


Y además:

  • manejar memoria nativa,
  • mapear estructuras,
  • trabajar con punteros,
  • invocar funciones dinámicamente.


Todo usando una API moderna.


La FFM API tiene dos pilares:

1. Foreign Function


Permite invocar funciones nativas.


Por ejemplo:

  • printf
  • strlen
  • malloc
  • funciones de librerías C


2. Foreign Memory

Permite manejar memoria fuera del heap de Java.


Esto es importante porque:

  • la JVM normalmente controla toda la memoria,
  • pero muchas librerías nativas usan memoria propia.


La FFM API permite trabajar con esa memoria de manera segura.


Primer ejemplo: llamar a printf


import java.lang.foreign.*;

import java.lang.invoke.MethodHandle;

import static java.lang.foreign.ValueLayout.*;


public class Main {


    public static void main(String[] args) throws Throwable {

        Linker linker = Linker.nativeLinker();

        SymbolLookup stdlib = linker.defaultLookup();


        MemorySegment printfAddress =

                stdlib.find("printf")

                      .orElseThrow();


        FunctionDescriptor printfSignature =

                FunctionDescriptor.of(JAVA_INT, ADDRESS);


        MethodHandle printf = linker.downcallHandle(

                printfAddress,

                printfSignature

        );


        try (Arena arena = Arena.ofConfined()) {

            MemorySegment text =

                    arena.allocateFrom("Hola desde C!\n");


            printf.invoke(text);

        }

    }

}


¿Qué está pasando acá?


Linker linker = Linker.nativeLinker();

Obtiene un linker capaz de interactuar con funciones nativas.


SymbolLookup stdlib = linker.defaultLookup();

Busca símbolos exportados por librerías nativas.


stdlib.find("printf")

Obtiene la dirección de memoria de la función.

Muy parecido a: void* ptr = dlsym(...)


FunctionDescriptor.of(JAVA_INT, ADDRESS)

Describe la firma de la función.

En este caso: int printf(char*)


linker.downcallHandle(...)

Crea un MethodHandle que permite invocar la función nativa.


Manejo de memoria con Arena

Uno de los conceptos más importantes es:

Un Arena administra memoria nativa.


try (Arena arena = Arena.ofConfined()) {


}


Cuando el bloque termina:

  • la memoria se libera automáticamente.
  • Esto evita muchísimos memory leaks.


La memoria nativa se representa con: MemorySegment


Es básicamente:

  • un bloque de memoria,
  • con límites,
  • seguro,
  • y controlado.


Y para reservar memoria hacemos: 

MemorySegment segment = arena.allocate(4);

Por ejemplo escribir y leer un entero: 

segment.set(JAVA_INT, 0, 42);

int value = segment.get(JAVA_INT, 0);


Supongamos esta estructura:

struct Point {

    int x;

    int y;

};


Podemos modelarla en Java.

StructLayout POINT = MemoryLayout.structLayout(

        JAVA_INT.withName("x"),

        JAVA_INT.withName("y")

);


Y para reservar memoria hacemos:

MemorySegment point = arena.allocate(POINT);


Escribir los campos:


point.set(JAVA_INT, 0, 10);

point.set(JAVA_INT, 4, 20);


¿Qué ventajas tiene?

Menos complejidad


No hace falta:

  • generar headers,
  • compilar JNI,
  • usar glue code.


¿Puede FFM API reemplaza completamente JNI?

Todavía no en todos los casos.

Hay escenarios avanzados donde JNI sigue siendo necesario.


Pero para muchísimos casos:

  • bindings simples,
  • acceso a librerías,
  • llamadas nativas,
  • estructuras,


la FFM API es mucho mejor.



domingo, 15 de noviembre de 2009

JNA

JNA es un framework que nos facilita las tareas con dll; es decir nos hace más fácil jni.
Alguien que leyo, vio, trabajo o se pregunto que es jni; debe haberse dado cuenta que es complejo. Esta complijida se ve disminuida casi a 0 con JNA.

Parece publicidad barata pero es verdad. Si te digo que con una interfaz podes usar funciones que se encuentran en una dll?

Mira, tengo la siguiente dll:

// dllParaPruba.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"


extern "C"
{
//---------------------------------------------------------------------------
double __declspec(dllexport) multiplicar(double a, double b)
{
return a * b;
}
//---------------------------------------------------------------------------
double __declspec(dllexport) sumar(double a, double b)
{
return a + b;
}

//---------------------------------------------------------------------------

double __declspec(dllexport) potencia(double a, double n)
{
double acumulador = 1;
for (int i = 0; i <>
acumulador*=a;
}

return acumulador;
}
}

Y yo desde java quiero leerla y usarla, bueno tengo que escribir la siguiente interfaz:

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

/**
*
* @author Emanuel
*/
public interface EjemploJNA extends Library {

EjemploJNA INSTANCE = (EjemploJNA)
Native.loadLibrary("dllParaPruba",
EjemploJNA.class);

double multiplicar(double a, double b);

double sumar(double a, double b);

double potencia(double a, double b);
}

Bueno ahora vamos hacer un test:

import org.junit.Test;
import static org.junit.Assert.*;
/**
*
* @author Emanuel
*/
public class EjemploJNATest {

static {
System.load("pathDondeSeEncuentraMiDll\dllParaPruba.dll");
}

@Test
public void sumar(){
double resultado = EjemploJNA.INSTANCE.sumar(2.3, 4.5);
assertEquals(new Double(resultado), new Double(6.8));
}

@Test
public void multiplicar(){
double resultado = EjemploJNA.INSTANCE.multiplicar(2.3, 4.5);

assertEquals(new Double(resultado), new Double(10.35));
}

@Test
public void potencia(){
double resultado = EjemploJNA.INSTANCE.potencia(2, 4);

assertEquals(new Double(resultado), new Double(16));
}

}

Listo!!

Que lo pario dijo mendieta!!

Una cosa me queda por aclarar es que compile la dll con el compilador de borland y no me fue muy bien, explotaba (no se bien por que) use el de microsoft y no hubo problema.

El que quiera más info dejo el link: https://jna.dev.java.net/

Y para toda la gente linda que usa maven la entrada en el pom:

<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>3.2.2</version>
</dependency>

Saludos!!!