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.

No hay comentarios.:
Publicar un comentario