最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?JNI的高級用法示例詳解

 更新時間:2025年11月26日 08:53:33   作者:猩火燎猿  
JNI是Java高級應(yīng)用中不可或缺的技術(shù)之一,它允許Java程序與本地代碼進(jìn)行交互,大大拓寬了Java應(yīng)用的范圍和性能,這篇文章主要介紹了Java?JNI高級用法的相關(guān)資料,需要的朋友可以參考下

1. native 層多線程與 JVM 交互

1.1 native 層啟動線程

在 JNI 中,native 層可以創(chuàng)建自己的線程(如 pthread、std::thread),但這些線程不是 JVM 線程,不能直接訪問 JVM 資源。
必須 attach 到 JVM,才能安全調(diào)用 Java 對象或方法。

1.2 attach/detach 線程

JavaVM* jvm; // 全局保存 JavaVM 指針

// 線程函數(shù)
void* thread_func(void* arg) {
    JNIEnv* env;
    // 線程 attach 到 JVM
    (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL);

    // 可以安全訪問 Java 對象和方法
    // ...

    // 線程結(jié)束前 detach
    (*jvm)->DetachCurrentThread(jvm);
    return NULL;
}

注意

  • attach 后才能用 JNI API。
  • detach 前必須釋放所有局部引用。
  • 多線程下要小心全局引用的并發(fā)安全(加鎖)。

1.3 native 層回調(diào) Java(多線程)

  • 通過全局引用保存回調(diào)對象,native 線程 attach 后用 CallVoidMethod/CallStaticMethod 調(diào)用 Java 方法。
  • 回調(diào)后及時刪除局部引用,防止內(nèi)存泄漏。

2. 復(fù)雜對象的 JNI 傳遞與構(gòu)造

2.1 Java 傳對象給 native

Java:

public class Person {
    public int age;
    public String name;
}
public native void processPerson(Person p);

C:

jclass cls = (*env)->GetObjectClass(env, person);
jfieldID fid_age = (*env)->GetFieldID(env, cls, "age", "I");
jint age = (*env)->GetIntField(env, person, fid_age);

jfieldID fid_name = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
jstring jname = (jstring)(*env)->GetObjectField(env, person, fid_name);
// 轉(zhuǎn)換為 C 字符串
const char* cname = (*env)->GetStringUTFChars(env, jname, NULL);
// ...
(*env)->ReleaseStringUTFChars(env, jname, cname);

2.2 native 構(gòu)造 Java 對象并返回

C:

jclass cls = (*env)->FindClass(env, "Person");
jmethodID ctor = (*env)->GetMethodID(env, cls, "<init>", "()V");
jobject obj = (*env)->NewObject(env, cls, ctor);

jfieldID fid_age = (*env)->GetFieldID(env, cls, "age", "I");
(*env)->SetIntField(env, obj, fid_age, 25);

// 返回 obj 給 Java
return obj;

2.3 復(fù)雜數(shù)組/集合的傳遞

  • 對于 Java List/Map,native 層一般用反射方式訪問元素,效率較低。
  • 性能敏感時建議用數(shù)組(如 int[]、Object[]),native 層用 GetObjectArrayElement 操作。

3. Java Lambda 與 JNI

3.1 Java Lambda 本質(zhì)

Lambda 是編譯期自動生成的匿名類對象,實現(xiàn)了目標(biāo)接口(如 Runnable、Function)。
JNI 層看到的是普通的 Java 對象。

3.2 JNI 層調(diào)用 Lambda

  • JNI 層可接收 lambda 作為參數(shù),只要用接口類型聲明。
  • JNI 通過反射/接口調(diào)用,調(diào)用 lambda 的方法(如 run、apply)。

示例:

public native void useCallback(Runnable r);
useCallback(() -> System.out.println("Hello from Lambda!"));

C:

jclass cls = (*env)->GetObjectClass(env, runnable);
jmethodID mid = (*env)->GetMethodID(env, cls, "run", "()V");
(*env)->CallVoidMethod(env, runnable, mid);

3.3 局限與注意

  • Lambda 不能直接在 native 層定義或?qū)崿F(xiàn)。
  • native 層只能把 lambda 當(dāng)作普通對象處理,不能享受 Java 層類型推斷等語法糖。
  • 性能敏感場景下,頻繁回調(diào) lambda 會有 JNI 橋接開銷。

4. JNI 性能基準(zhǔn)測試與優(yōu)化

4.1 性能測試方法

  • 使用 JMH(Java Microbenchmark Harness)寫基準(zhǔn)測試,比較 Java 方法、JNI 方法、JNA 方法的調(diào)用耗時。
  • 典型測試:循環(huán)調(diào)用百萬次,測量總耗時。

JMH 示例:

@Benchmark
public void testJavaAdd() {
    int a = 1, b = 2;
    int c = a + b;
}

@Benchmark
public void testJNIAdd() {
    nativeAdd(1, 2);
}

4.2 常見性能瓶頸

  • JNI 方法調(diào)用有固定的橋接開銷(幾十到幾百納秒)。
  • 數(shù)據(jù)類型轉(zhuǎn)換(如字符串、數(shù)組)開銷大。
  • 頻繁創(chuàng)建/釋放引用、反射操作等會拖慢性能。

4.3 優(yōu)化建議

  • 減少 JNI 調(diào)用次數(shù):能批量處理就批量,避免頻繁小操作。
  • 緩存 class/methodID:避免每次都查找。
  • 用直接緩沖區(qū)(DirectByteBuffer):大數(shù)據(jù)傳遞更高效。
  • 避免不必要的數(shù)據(jù)拷貝:如數(shù)組可用 GetPrimitiveArrayCritical。
  • 合理管理引用:用完及時釋放局部/全局引用。
  • 選擇合適的傳遞方式:復(fù)雜結(jié)構(gòu)建議序列化或用 protobuf/C結(jié)構(gòu)體映射。

5. 典型面試題與實戰(zhàn)解析

  • native 層多線程調(diào)用 Java 方法有什么注意事項?

    • 線程必須 attach/detach,回調(diào)對象需全局引用,注意并發(fā)安全。
  • 如何從 native 層構(gòu)造 Java 對象并返回?

    • 用 FindClass、GetMethodID、NewObject、SetXXXField。
  • lambda 作為回調(diào)參數(shù)傳給 native,JNI 如何調(diào)用?

    • 當(dāng)作接口對象,反射調(diào)用相應(yīng)方法(如 run/apply)。
  • JNI 性能瓶頸主要在哪?如何優(yōu)化?

    • 橋接和數(shù)據(jù)轉(zhuǎn)換開銷大,建議批量處理、緩存 methodID、用直接緩沖區(qū)。

6. native 層多線程最佳實踐

6.1 多線程安全 attach/detach

  • 獲取 JavaVM 指針:在 JNI_OnLoad 里全局保存 JavaVM 指針,供所有 native 線程使用。
  • 線程 attach:每個 native 線程首次需要 attach 到 JVM,獲得 JNIEnv 指針。
  • 線程 detach:線程退出前 detach,避免 JVM 資源泄露。

示例:

JavaVM* g_jvm = NULL;

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    g_jvm = vm;
    return JNI_VERSION_1_6;
}

void* worker(void* arg) {
    JNIEnv* env;
    if ((*g_jvm)->AttachCurrentThread(g_jvm, (void**)&env, NULL) == 0) {
        // 使用 env 調(diào)用 Java 方法
        // ...
        (*g_jvm)->DetachCurrentThread(g_jvm);
    }
    return NULL;
}

6.2 并發(fā)安全的回調(diào)

  • 回調(diào)對象需用 NewGlobalRef 創(chuàng)建全局引用,避免被 GC 回收。
  • 回調(diào)時加鎖(如 pthread_mutex),確保多線程安全。
  • 回調(diào)后及時 DeleteGlobalRef,避免內(nèi)存泄漏。

7. 復(fù)雜對象高效傳遞與映射

7.1 批量數(shù)據(jù)結(jié)構(gòu)傳遞

  • 對于大量數(shù)據(jù)(如點、向量、結(jié)構(gòu)體),建議用 ByteBuffer 或直接數(shù)組傳遞,native 層按內(nèi)存結(jié)構(gòu)解析。
  • 可用 Java 的 DirectByteBuffer,native 層用 GetDirectBufferAddress 獲得指針,零拷貝高性能。

Java:

ByteBuffer buf = ByteBuffer.allocateDirect(1024);
nativeProcessBuffer(buf);

C:

void JNICALL nativeProcessBuffer(JNIEnv* env, jobject obj, jobject buffer) {
    void* ptr = (*env)->GetDirectBufferAddress(env, buffer);
    // 直接操作內(nèi)存
}

7.2 復(fù)雜對象序列化

  • 對于復(fù)雜 Java 對象(如 Map/List),可以用 JSON、protobuf、flatbuffers 序列化后傳遞到 native 層,native 層反序列化為 C 結(jié)構(gòu)體。
  • 這樣可以避免 JNI 的反射遍歷,提高性能和靈活性。

8. lambda 在高性能場景下的實戰(zhàn)建議

8.1 場景分析

  • Java 層傳遞 lambda 作為回調(diào),native 層保存并在事件發(fā)生時回調(diào)。
  • 性能敏感時,建議只傳遞接口對象,避免頻繁回調(diào)和反射。

8.2 高效回調(diào)設(shè)計

  • native 層只保存接口對象的全局引用,不做多余的類型檢查。
  • 回調(diào)時直接用 methodID 調(diào)用,不用每次查找。
  • 如果回調(diào)次數(shù)極多,可設(shè)計事件隊列,由 Java 層統(tǒng)一處理,減少 JNI 交互頻率。

9. JNI 性能基準(zhǔn)測試與分析

9.1 JMH 基準(zhǔn)測試示例

@Benchmark
public void javaMethod() {
    // 普通 Java 方法
}

@Benchmark
public void jniMethod() {
    nativeMethod();
}

@Benchmark
public void jniArrayMethod() {
    nativeArrayMethod(new int[1000]);
}

測試結(jié)論

  • 普通 Java 方法最快。
  • JNI 方法有橋接開銷,單次調(diào)用比 Java 慢幾十到幾百納秒。
  • 傳遞數(shù)組/對象時,JNI 開銷更大,建議批量處理。

9.2 優(yōu)化建議

  • 批量處理:如 1000 個點一次傳遞,避免 1000 次 JNI 調(diào)用。
  • 緩存 class/methodID:只查找一次,后續(xù)直接用。
  • DirectByteBuffer/序列化:大數(shù)據(jù)零拷貝或高效解析。
  • 避免反射和頻繁創(chuàng)建引用。

10. JNI 在實際項目中的架構(gòu)建議

10.1 封裝 native 層 API

  • Java 層只暴露簡單的 native 方法,參數(shù)類型盡量基礎(chǔ)(如數(shù)組、ByteBuffer)。
  • native 層用 C/C++ 封裝復(fù)雜邏輯,統(tǒng)一管理線程和資源,避免 Java 層直接暴露復(fù)雜對象。

10.2 資源和異常管理

  • 所有 native 層分配的內(nèi)存和引用都需及時釋放。
  • native 層出錯時及時 ThrowNew Java 異常,不讓 JVM 崩潰。

10.3 跨平臺適配

  • 動態(tài)庫需分別編譯 Windows/Linux/macOS,接口保證一致性。
  • 可用 CMake/autotools 管理多平臺構(gòu)建。

11. 典型面試題解析(補充)

  • 如何用 JNI 實現(xiàn) Java 層的事件回調(diào)?

    • 保存回調(diào)對象的全局引用,native 事件發(fā)生時 attach 線程并調(diào)用 Java 方法。
  • 批量數(shù)據(jù)高效傳遞的最佳實踐?

    • 用 DirectByteBuffer 或 protobuf 序列化,native 層直接解析內(nèi)存。
  • native 多線程并發(fā)安全的關(guān)鍵點?

    • attach/detach 線程,回調(diào)對象用全局引用,操作時加鎖。
  • JNI 性能優(yōu)化的核心原則?

    • 減少調(diào)用次數(shù),批量處理,緩存查找結(jié)果,零拷貝傳遞。

11. 總結(jié)

  • JNI 支持 native 層多線程、復(fù)雜對象交互、Java lambda 回調(diào)等高級用法,但需嚴(yán)格管理線程、引用和資源。
  • 性能優(yōu)化需關(guān)注調(diào)用次數(shù)、數(shù)據(jù)傳遞方式和引用管理。
  • 工程實踐中建議只在必要場景使用 JNI,盡量封裝好接口,簡化 Java 與 native 的交互。
  • JNI 高級用法需關(guān)注多線程安全、復(fù)雜對象高效傳遞、回調(diào)機制、性能基準(zhǔn)與優(yōu)化。
  • 工程實踐中建議封裝好接口,統(tǒng)一管理資源和異常,保證跨平臺一致性和高性能。
  • 面試和項目中,能用 DirectByteBuffer、序列化、全局引用等技巧,往往體現(xiàn)高級水平。

到此這篇關(guān)于Java JNI的高級用法的文章就介紹到這了,更多相關(guān)Java JNI高級用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JAVA中Collections工具類sort()排序方法

    JAVA中Collections工具類sort()排序方法

    這篇文章主要介紹了JAVA中Collections工具類sort()排序方法,非常具有實用價值,需要的朋友可以參考下。
    2016-11-11
  • SpringBoot應(yīng)用部署到Tomcat中無法啟動的解決方法

    SpringBoot應(yīng)用部署到Tomcat中無法啟動的解決方法

    這篇文章主要介紹了SpringBoot應(yīng)用部署到Tomcat中無法啟動的解決方法,需要的朋友可以參考下
    2017-09-09
  • SpringBoot整合resilience4j實現(xiàn)接口限流

    SpringBoot整合resilience4j實現(xiàn)接口限流

    最近在開發(fā)項目的時候,需要用到限流的功能,本文主要介紹了SpringBoot整合resilience4j實現(xiàn)接口限流,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • Java使用正則提取字符串中的內(nèi)容的詳細(xì)步驟

    Java使用正則提取字符串中的內(nèi)容的詳細(xì)步驟

    這篇文章主要介紹了Java中使用正則表達(dá)式提取字符串內(nèi)容的方法,通過Pattern和Matcher類實現(xiàn),涵蓋編譯正則、查找匹配、分組捕獲、數(shù)字與郵箱提取場景,以及命名分組、非貪婪匹配等技巧,需要的朋友可以參考下
    2025-08-08
  • maven?傳遞依賴的實現(xiàn)

    maven?傳遞依賴的實現(xiàn)

    本文主要介紹了Maven中傳遞依賴的處理方式,如scope=compile影響依賴傳遞,使用排除不需要的依賴,以及如何通過查看依賴樹解決運行時錯誤,特別是在依賴排除后可能導(dǎo)致的運行時缺少必需包的問題,感興趣的可以了解一下
    2024-10-10
  • java list隨機抽取元素的案例

    java list隨機抽取元素的案例

    這篇文章主要介紹了java list隨機抽取元素的案例,具有很好的參考,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • 詳解SpringBean基于XML的裝配

    詳解SpringBean基于XML的裝配

    Bean的裝配可以理解為依賴關(guān)系注入,Bean的裝配方式也就是Bean 的依賴注入方式.Spring容器支持多種形式的Bean的裝配方式,如基于XML的Bean裝配,基于Annotation的Bean裝配和自動裝配等.本文就帶大家了解SpringBean基于XML的裝配,需要的朋友可以參考下
    2021-05-05
  • Java中的ReentrantLock使用解析

    Java中的ReentrantLock使用解析

    這篇文章主要介紹了Java中的ReentrantLock使用解析,ReentrandLock即可重入鎖,可重入鎖解決的是重入鎖定的問題,重入鎖定指的是當(dāng)一個線程執(zhí)行邏輯時,需要兩次獲取鎖,而該鎖不可重入就會導(dǎo)致內(nèi)部嵌套無法獲取鎖導(dǎo)致Reentrance Lockout發(fā)生,需要的朋友可以參考下
    2023-11-11
  • JavaWeb實現(xiàn)表單提交的示例詳解

    JavaWeb實現(xiàn)表單提交的示例詳解

    這篇文章主要介紹了如何利用JavaWeb實現(xiàn)表單提交功能,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)JavaWeb有一定幫助,感興趣的可以了解一下
    2022-03-03
  • java TreeUtil菜單遞歸工具類

    java TreeUtil菜單遞歸工具類

    這篇文章主要為大家詳細(xì)介紹了java TreeUtil菜單遞歸工具類,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-08-08

最新評論

绥芬河市| 余姚市| 水城县| 清原| 毕节市| 井冈山市| 东宁县| 宽甸| 双牌县| 建昌县| 马山县| 邹城市| 彭州市| 隆子县| 巴马| 监利县| 玛多县| 惠安县| 广州市| 周至县| 咸阳市| 阳原县| 石家庄市| 中山市| 高青县| 南漳县| 集贤县| 泽库县| 平江县| 根河市| 永福县| 毕节市| 托克逊县| 合山市| 景洪市| 兴化市| 峨山| 保山市| 肇源县| 孙吴县| 宁武县|