Java ile Yerel Kütüphaneler Arasındaki Boşluğu Kapatacak Yabancı İşlev ve Bellek API’si

İnceleme tamamlandıktan sonra, JEP 454Yabancı İşlev ve Bellek API’si şu tarihten itibaren tanıtılmıştır: Hedeflenen ile Birleşik JDK 22 için. Bu JEP, bu özelliğin iki tur kuluçka ve üç tur ön izleme sonrasında sonuçlandırılmasını önermektedir: JEP 412, Yabancı İşlev ve Bellek API’si (Kuluçka Makinesi), JDK 17’de teslim edilir; JEP419, Yabancı Fonksiyon ve Bellek API’si (İkinci Kuluçka Merkezi), JDK 18’de teslim edilir; JEP424, Yabancı İşlev ve Bellek API’si (Önizleme), JDK 19’da teslim edilmiştir; JEP434, Yabancı İşlev ve Bellek API’si (İkinci Önizleme), JDK 20’de teslim edilir; ve JEP 442, Yabancı İşlev ve Bellek API’si (Üçüncü Önizleme)JDK 21 sürümünde teslim edildi.

Son sürümden bu yana yapılan iyileştirmeler arasında yeni bir Enable-Native-Access yürütülebilir JAR’lardaki kodun, kısıtlanmış yöntemleri, --enable-native-access bayrak; program aracılığıyla platforma özgü sabitlerden kaçınarak istemcilerin C işlev tanımlayıcıları oluşturmasına izin verin; yerel bellekteki değişken uzunluklu diziler için geliştirilmiş destek; ve yerel dizelerde birden fazla karakter kümesi desteği.

Bu API, Java ile yerel kod arasındaki etkileşimi geliştirmek ve yerel kitaplıklara erişmenin ve yerel belleği yönetmenin daha verimli ve daha güvenli bir yolunu sunmak üzere tasarlanmıştır. API iki ana bileşenden oluşur:

Yabancı Fonksiyon Arayüzü (FFI): API’nin bu bölümü, Java programlarının C ve C++ gibi yerel dillerde yazılmış işlevleri çağırmasına olanak tanır. Gerekli olan standart kodun çoğunu ortadan kaldırır. Java Yerel Arayüzü (JNI), yerel kitaplıklarla etkileşime giren kodun yazılmasını ve bakımını kolaylaştırır.

Bellek Erişimi API’si: Bu bileşen, yerel bellekle etkileşime geçmek için bir dizi araç sağlar. Yerel veri yapılarının hafıza tahsisi, serbest bırakılması ve manipülasyonu için özellikler içerir. API ayrıca yerel kodla çalışırken arabellek taşmaları ve yaygın karşılaşılan tuzaklar gibi sorunları önlemek için güvenlik kontrolleri de sağlar.

Yabancı İşlev ve Bellek (FFM) API’si, java.lang.foreign paket genellikle birkaç temel sınıftan oluşur:

Bağlayıcı: Bu arayüz, Java kodunu belirli bir Uygulama İkili Arayüzüne (ABI) uygun kitaplıklardaki yabancı işlevlere bağlamak için mekanizmalar sağlar. Hem yabancı işlevlere yapılan aşağı çağrıları hem de yabancı işlevlerden Java koduna yapılan çağrıları destekler.

Sembol Arama: Bu arayüz, belirli bir kütüphanedeki bir fonksiyon veya global değişken gibi bir sembolün adresini almak için kullanılır. Kitaplık aramaları, yükleyici aramaları ve bir sunucu tarafından sağlanan varsayılan aramalar dahil olmak üzere çeşitli arama türlerini destekler. Linker.

Bellek Segmenti: Bu arayüz, Java yığınındaki (“yığın bölümü”) veya onun dışındaki (“yerel bölüm”) bitişik bir bellek bölgesine erişim sağlar. Uzamsal ve zamansal sınırları sağlarken verileri okumak ve yazmak için çeşitli erişim işlemleri sunar.

YöntemHandle: Bu sınıf, temeldeki bir yönteme, kurucuya veya alana güçlü bir şekilde yazılan, doğrudan yürütülebilir bir referans görevi görür. İki özel invoker yöntemi sağlar, invokeExact Ve invokeve görünür bir durum olmaksızın değişmezdir. Bir referans MethodHandle aracılığıyla edinilebilir Linker::downcallHandle() yöntemi çağırmak için.

Fonksiyon Tanımlayıcı: Bir işlev tanımlayıcı, yabancı bir işlevin imzasını modeller. Bir işlev tanımlayıcısı, sıfır veya daha fazla bağımsız değişken düzeninden ve sıfır veya bir dönüş düzeninden oluşur. Aşağı çağrı yöntemi tanıtıcıları ve yukarı çağrı taslakları oluşturmak için bir işlev tanımlayıcı kullanılır.

Arena: Java’daki bu arayüz, yerel bellek bölümlerinin yaşam döngüsünü kontrol ederek, bunların belirli kapsamlar dahilinde tahsisi ve serbest bırakılması için yöntemler sağlar. Her biri kullanım ömrü, iş parçacığı erişilebilirliği ve manuel kontrol açısından benzersiz özelliklere sahip farklı türlerde (küresel, otomatik, sınırlı ve paylaşımlı) gelir.

Örneğin, burada bir C kütüphanesi işlevi için bir yöntem tanıtıcısı elde eden Java kodu bulunmaktadır. taban sıralaması ve daha sonra bunu bir Java dizisinde yaşamı başlatan dört dizeyi sıralamak için kullanır.


import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.util.Arrays;

public class RadixSortExample {
    public static void main(String[] args) {
        RadixSortExample radixSorter = new RadixSortExample();
        String[] javaStrings = {"mouse", "cat", "dog", "car"};

        System.out.println("radixsort input: " + Arrays.toString(javaStrings));

        // Perform radix sort on input array of strings
        javaStrings = radixSorter.sort(javaStrings);

        System.out.println("radixsort output: " + Arrays.toString(javaStrings));
    }

    private String[] sort(String[] strings) {
        // Find foreign function on the C library path
        Linker linker = Linker.nativeLinker();
        SymbolLookup stdlib = linker.defaultLookup();
        MemorySegment radixSort = stdlib.find("radixsort").orElseThrow();
        MethodHandle methodHandle = linker.downcallHandle(radixSort, FunctionDescriptor.ofVoid(
                ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_CHAR
        ));

        // Use try-with-resources to manage the lifetime of off-heap memory
        try (Arena arena = Arena.ofConfined()) {
            // Allocate a region of off-heap memory to store pointers
            MemorySegment pointers = arena.allocateArray(ValueLayout.ADDRESS, strings.length);

            // Copy the strings from on-heap to off-heap
            for (int i = 0; i < strings.length; i++) {
                MemorySegment cString = arena.allocateUtf8String(strings[i]);
                pointers.setAtIndex(ValueLayout.ADDRESS, i, cString);
            }

            // Sort the off-heap data by calling the foreign function
            methodHandle.invoke(pointers, strings.length, MemorySegment.NULL, '

import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.util.Arrays;

public class RadixSortExample {
public static void main(String[] args) {
RadixSortExample radixSorter = new RadixSortExample();
String[] javaStrings = {"mouse", "cat", "dog", "car"};

System.out.println("radixsort input: " + Arrays.toString(javaStrings));

// Perform radix sort on input array of strings
javaStrings = radixSorter.sort(javaStrings);

System.out.println("radixsort output: " + Arrays.toString(javaStrings));
}

private String[] sort(String[] strings) {
// Find foreign function on the C library path
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MemorySegment radixSort = stdlib.find("radixsort").orElseThrow();
MethodHandle methodHandle = linker.downcallHandle(radixSort, FunctionDescriptor.ofVoid(
ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_CHAR
));

// Use try-with-resources to manage the lifetime of off-heap memory
try (Arena arena = Arena.ofConfined()) {
// Allocate a region of off-heap memory to store pointers
MemorySegment pointers = arena.allocateArray(ValueLayout.ADDRESS, strings.length);

// Copy the strings from on-heap to off-heap
for (int i = 0; i < strings.length; i++) {
MemorySegment cString = arena.allocateUtf8String(strings[i]);
pointers.setAtIndex(ValueLayout.ADDRESS, i, cString);
}

// Sort the off-heap data by calling the foreign function
methodHandle.invoke(pointers, strings.length, MemorySegment.NULL, '\0');

// Copy the (reordered) strings from off-heap to on-heap
for (int i = 0; i < strings.length; i++) {
MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i);
cString = cString.reinterpret(Long.MAX_VALUE);
strings[i] = cString.getUtf8String(0);
}
} catch (Throwable e) {
throw new RuntimeException(e);
}

return strings;
}
}
'); // Copy the (reordered) strings from off-heap to on-heap for (int i = 0; i < strings.length; i++) { MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i); cString = cString.reinterpret(Long.MAX_VALUE); strings[i] = cString.getUtf8String(0); } } catch (Throwable e) { throw new RuntimeException(e); } return strings; } }

Yukarıdaki kodu çalıştırabilmek için geliştiricinin, aracılığıyla kolayca indirilebilen JDK 22’yi yüklemesi gerekir. SDKman.

Geleneksel olarak, Java’da yığın dışı belleğin işlenmesi zor olmuştur. Daha önce Java geliştiricileri yalnızca Java’yı kullanmakla sınırlıydı. ByteBuffer yığın dışı bellek işlemleri için nesneler. Ancak FFM API’si şunları sunar: MemorySegment Yığın dışı belleğin tahsisi ve tahsisinin kaldırılması üzerinde daha fazla kontrole izin veren nesneler. Dahası, MemorySegment::asByteBuffer Ve MemorySegment::ofBuffer yöntemler, geleneksel bayt arabellekleri ile yeni bellek bölümü nesneleri arasındaki köprüyü daha da güçlendirir.

FFM API’si aşağıdakilerle uyumludur: java.nio.kanalları Yığın dışı bayt arabelleklerini serbest bırakmak için belirleyici bir yol sağlayan API. Bu, çağırma gibi standart olmayan, deterministik olmayan tekniklere güvenme ihtiyacını ortadan kaldırır. sun.misc.Unsafe::invokeCleanerbellek yönetimine daha güvenilir ve standartlaştırılmış bir yaklaşımın önünü açıyor.

FFM API’sindeki geliştirmeler, Java platformunu daha güvenli, daha verimli ve birlikte çalışabilir hale getirmeye yönelik bir adımdır. Odak noktası yalnızca Java yerel etkileşimlerini kolaylaştırmak değil, aynı zamanda onları korumaktır. API, yerel kitaplıklarla çalışmaya yönelik daha kusursuz, Java deyimsel bir yaklaşım sunarak JNI’nin karmaşıklıklarına ve güvenlik sorunlarına sağlam bir alternatif sunar.

API’nin, Java’nın yerel kütüphanelerle etkileşiminde devrim yaratması bekleniyor ve platformu daha güvenli ve daha verimli hale getirmeyi amaçlayan daha geniş Java yol haritasıyla uyum sağlıyor. Java ve yerel kütüphanelerle çalışan geliştiriciler için bu, daha güvenli ve daha verimli kod sağlarken karmaşıklıkları basitleştirmeyi vaat eden heyecan verici bir gelişmedir.



Source link

Yorum yapın