|
@@ -0,0 +1,546 @@
|
|
|
|
|
+# JNI中的三种引用类型
|
|
|
|
|
+
|
|
|
|
|
+| 特性 | 局部引用 (Local) | 全局引用 (Global) | 弱全局引用 (Weak Global) |
|
|
|
|
|
+| :------------- | :------------------------ | :---------------------------- | :----------------------------- |
|
|
|
|
|
+| **生命周期** | Native 方法执行期间 | 直到手动释放 | 直到手动释放 (或被GC回收对象) |
|
|
|
|
|
+| **跨线程使用** | 是 | 是 | 是 |
|
|
|
|
|
+| **阻止 GC** | 否 | 是 | 否 |
|
|
|
|
|
+| **创建函数** | (大多数 JNI 函数自动创建) | `NewGlobalRef` | `NewWeakGlobalRef` |
|
|
|
|
|
+| **释放函数** | 自动 或 `DeleteLocalRef` | `DeleteGlobalRef` | `DeleteWeakGlobalRef` |
|
|
|
|
|
+| **主要用途** | 绝大多数 JNI 操作 | 缓存类、对象等跨方法/线程使用 | 缓存不阻止 GC 的引用,如观察者 |
|
|
|
|
|
+
|
|
|
|
|
+> 网络上有些资料说局部引用无法跨线程使用
|
|
|
|
|
+> 猜测指的是在C/C++中无法跨线程使用,但在JVM中可以跨线程使用,JNI返回的可能是拷贝后数据,不是原生的C/C++中的数据
|
|
|
|
|
+
|
|
|
|
|
+## 局部引用
|
|
|
|
|
+
|
|
|
|
|
+通过在本地方法内部直接创建的引用都是局部引用,也可以使用`NewLocalRef`函数进行创建,只在本地方法内生效,本地方法调用结束,局部引用会被自动回收,也可以可以使用`DeleteLocalRef`函数进行主动释放
|
|
|
|
|
+
|
|
|
|
|
+创建一个字符串的局部引用
|
|
|
|
|
+
|
|
|
|
|
+```c
|
|
|
|
|
+//字符串局部引用
|
|
|
|
|
+jstring name = (*env)->NewStringUTF(env, "杨逸");
|
|
|
|
|
+//使用NewLocalRef函数显示创建局部引用
|
|
|
|
|
+jobject localRef = (*env)->NewLocalRef(env,name);
|
|
|
|
|
+//显示释放局部引用
|
|
|
|
|
+(*env)->DeleteLocalRef(env,localRef);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 案例:使用局部引用
|
|
|
|
|
+
|
|
|
|
|
+1. 本地方法的声明
|
|
|
|
|
+
|
|
|
|
|
+ ```java
|
|
|
|
|
+ native Person getPerson();
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+2. 本地方法的实现
|
|
|
|
|
+
|
|
|
|
|
+ ```c
|
|
|
|
|
+ #include "space_anyi_jni_reference_LocalReferenceTest.h"
|
|
|
|
|
+ JNIEXPORT jobject JNICALL Java_space_anyi_jni_reference_LocalReferenceTest_getPerson(JNIEnv *env, jobject this){
|
|
|
|
|
+ //1.获取对应的jclass
|
|
|
|
|
+ jclass class_Person = (*env)->FindClass(env, "Lspace/anyi/jni/reference/Person;");
|
|
|
|
|
+ //2.获取对应的构造方法
|
|
|
|
|
+ jmethodID constructor = (*env)->GetMethodID(env, class_Person, "<init>", "(Ljava/lang/String;I)V");
|
|
|
|
|
+ //3.创建对象
|
|
|
|
|
+ jstring name = (*env)->NewStringUTF(env, "杨逸-局部引用");
|
|
|
|
|
+ jobject person = (*env)->NewObject(env, class_Person, constructor,name,18);
|
|
|
|
|
+ return person;
|
|
|
|
|
+ }
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+3. 使用本地方法返回的局部引用
|
|
|
|
|
+
|
|
|
|
|
+ ```java
|
|
|
|
|
+ Person person = localReferenceTest.getPerson();
|
|
|
|
|
+ System.gc();
|
|
|
|
|
+ try {
|
|
|
|
|
+ Thread.sleep(1000*5);
|
|
|
|
|
+ } catch (InterruptedException e) {
|
|
|
|
|
+ e.printStackTrace();
|
|
|
|
|
+ }
|
|
|
|
|
+ System.out.println(Objects.isNull(person));
|
|
|
|
|
+ System.out.println("person = " + person);
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+4. 测试结果
|
|
|
|
|
+
|
|
|
|
|
+ ```tex
|
|
|
|
|
+ false
|
|
|
|
|
+ person = Person{name='杨逸-局部引用', age=18}
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### 案例:测试局部引用能否跨线程使用
|
|
|
|
|
+
|
|
|
|
|
+- 测试代码
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+Person person = localReferenceTest.getPerson();
|
|
|
|
|
+System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
|
|
|
|
|
+new Thread(()->{
|
|
|
|
|
+ //全局引用跨线程使用
|
|
|
|
|
+ System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
|
|
|
|
|
+ System.out.println("person = " + person);
|
|
|
|
|
+}).start();
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+- 测试结果,可以跨线程使用
|
|
|
|
|
+
|
|
|
|
|
+```tex
|
|
|
|
|
+Thread.currentThread().getName() = main
|
|
|
|
|
+Thread.currentThread().getName() = Thread-0
|
|
|
|
|
+person = Person{name='杨逸-局部引用', age=18}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+## 全局引用
|
|
|
|
|
+
|
|
|
|
|
+全局引用需要使用`NewGlobalRef`函数进行创建,全局引用可以返回给JVM使用,全局引用不会被JVM进行垃圾回收,需要手动调用`DeleteGlobalRef`函数进行释放(有内存泄漏的风险)
|
|
|
|
|
+
|
|
|
|
|
+### 案例:在JVM中使用JNI创建的对象
|
|
|
|
|
+
|
|
|
|
|
+1. 定义三个本地方法
|
|
|
|
|
+ - "getPersonHandler":在JNI中创建一个全局引用,并返回句柄
|
|
|
|
|
+ - "getPerson":根据句柄获取全局引用对象
|
|
|
|
|
+ - "freePerson":释放JNI创建的全局引用
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+class GlobalReferenceTest{
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在JNI中创建一个全局引用,并返回句柄
|
|
|
|
|
+ * @return long 全局引用的句柄
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/04 21:29:12
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ native long getPersonHandler();
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据句柄获取全局引用对象
|
|
|
|
|
+ * @param handler 句柄
|
|
|
|
|
+ * @return {@code Person } 全局引用对象
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/04 21:30:09
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ native Person getPerson(long handler);
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 释放JNI创建的全局引用
|
|
|
|
|
+ * @param handler 句柄
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/04 21:30:38
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ native void freePerson(long handler);
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+2. 本地方法实现实现
|
|
|
|
|
+
|
|
|
|
|
+> 注意指针转换时需要使用`intptr_t`类型作为中级类型,避免因为指针在32位系统和63位系统中占用大小不同导致精度丢失
|
|
|
|
|
+
|
|
|
|
|
+```c
|
|
|
|
|
+#include "space_anyi_jni_reference_GlobalReferenceTest.h"
|
|
|
|
|
+JNIEXPORT jlong JNICALL Java_space_anyi_jni_reference_GlobalReferenceTest_getPersonHandler(JNIEnv *env, jobject this){
|
|
|
|
|
+ //1.获取对应的jclass
|
|
|
|
|
+ jclass class_Person = (*env)->FindClass(env, "Lspace/anyi/jni/reference/Person;");
|
|
|
|
|
+ //2.获取对应的构造方法
|
|
|
|
|
+ jmethodID constructor = (*env)->GetMethodID(env, class_Person, "<init>", "(Ljava/lang/String;I)V");
|
|
|
|
|
+ //3.创建对象
|
|
|
|
|
+ jstring name = (*env)->NewStringUTF(env, "杨逸");
|
|
|
|
|
+ jobject person = (*env)->NewObject(env, class_Person, constructor,name,18);
|
|
|
|
|
+ //4.获取全局引用
|
|
|
|
|
+ jobject globalReference = (*env)->NewGlobalRef(env, person);
|
|
|
|
|
+ //5.转换为句柄,使用intptr_t类型作为中间类型,避免精度丢失
|
|
|
|
|
+ jlong handler = (jlong)(intptr_t)globalReference;
|
|
|
|
|
+ return handler;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+JNIEXPORT jobject JNICALL Java_space_anyi_jni_reference_GlobalReferenceTest_getPerson(JNIEnv *env, jobject this, jlong handler){
|
|
|
|
|
+ //1.通过句柄获取全局引用,使用intptr_t类型作为中间类型
|
|
|
|
|
+ jobject person = (jobject)(intptr_t)handler;
|
|
|
|
|
+ //2.返回对象
|
|
|
|
|
+ return person;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+JNIEXPORT void JNICALL Java_space_anyi_jni_reference_GlobalReferenceTest_freePerson(JNIEnv *env, jobject this, jlong handler){
|
|
|
|
|
+ //1.通过句柄释放全局引用的内存
|
|
|
|
|
+ (*env)->DeleteGlobalRef(env, (jobject)(intptr_t)handler);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+3. 测试在JVM中使用JNI创建的对象
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+package space.anyi.jni.reference;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * @ProjectName: JNI_learn
|
|
|
|
|
+ * @FileName: Main
|
|
|
|
|
+ * @Author: 杨逸
|
|
|
|
|
+ * @Data:2026/3/4 21:25
|
|
|
|
|
+ * @Description:
|
|
|
|
|
+ */
|
|
|
|
|
+public class Main {
|
|
|
|
|
+ static {
|
|
|
|
|
+ System.load("E:\\tmp\\JNI_learn\\c\\reference\\JNI_globalReference.dll");
|
|
|
|
|
+ }
|
|
|
|
|
+ public static void main(String[] args) {
|
|
|
|
|
+ GlobalReferenceTest.test();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+class GlobalReferenceTest{
|
|
|
|
|
+ static GlobalReferenceTest globalReferenceTest = new GlobalReferenceTest();
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 测试在JVM中使用JNI创建的对象全局引用
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/05 21:06:10
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ static void test(){
|
|
|
|
|
+ //获取全局引用的句柄
|
|
|
|
|
+ long personHandler = globalReferenceTest.getPersonHandler();
|
|
|
|
|
+
|
|
|
|
|
+ //根据句柄获取全局引用对象
|
|
|
|
|
+ Person person = globalReferenceTest.getPerson(personHandler);
|
|
|
|
|
+ System.out.println("person.getName() = " + person.getName());
|
|
|
|
|
+ System.out.println("person.getAge() = " + person.getAge());
|
|
|
|
|
+ System.out.println(person);
|
|
|
|
|
+ //释放全局引用
|
|
|
|
|
+ globalReferenceTest.freePerson(personHandler);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 测试JNI创建的对象全局引用是否会被JVM垃圾回收
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/05 21:06:34
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ static void testJVMGC(){
|
|
|
|
|
+ while (true){
|
|
|
|
|
+ globalReferenceTest.getPersonHandler();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在JNI中创建一个全局引用,并返回句柄
|
|
|
|
|
+ * @return long 全局引用的句柄
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/04 21:29:12
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ native long getPersonHandler();
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据句柄获取全局引用对象
|
|
|
|
|
+ * @param handler 句柄
|
|
|
|
|
+ * @return {@code Person } 全局引用对象
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/04 21:30:09
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ native Person getPerson(long handler);
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 释放JNI创建的全局引用
|
|
|
|
|
+ * @param handler 句柄
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/04 21:30:38
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ native void freePerson(long handler);
|
|
|
|
|
+}
|
|
|
|
|
+class Person{
|
|
|
|
|
+ private String name;
|
|
|
|
|
+ private int age;
|
|
|
|
|
+
|
|
|
|
|
+ public Person(String name, int age) {
|
|
|
|
|
+ this.name = name;
|
|
|
|
|
+ this.age = age;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public String getName() {
|
|
|
|
|
+ return name;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void setName(String name) {
|
|
|
|
|
+ this.name = name;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public int getAge() {
|
|
|
|
|
+ return age;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void setAge(int age) {
|
|
|
|
|
+ this.age = age;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String toString() {
|
|
|
|
|
+ return "Person{" +
|
|
|
|
|
+ "name='" + name + '\'' +
|
|
|
|
|
+ ", age=" + age +
|
|
|
|
|
+ '}';
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+4. 测试结果
|
|
|
|
|
+
|
|
|
|
|
+```tex
|
|
|
|
|
+person.getName() = 杨逸
|
|
|
|
|
+person.getAge() = 18
|
|
|
|
|
+Person{name='杨逸', age=18}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 案例:测试全局引用是否会被JVM垃圾回收
|
|
|
|
|
+
|
|
|
|
|
+限制JVM的内存大小,在Java程序中一直使用JNI创建对象的全局引用,预期会出现OOM错误
|
|
|
|
|
+
|
|
|
|
|
+> 指定JVM最大使用内存的参数`-Xmx10M`
|
|
|
|
|
+>
|
|
|
|
|
+> 打印JVM的GC详细日志参数`-XX:+PrintGCDetails`
|
|
|
|
|
+
|
|
|
|
|
+- Java测试代码
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+while (true){
|
|
|
|
|
+ globalReferenceTest.getPersonHandler();
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- 测试结果,触发OOM错误,JNI全局引用无法被JVM自动回收
|
|
|
|
|
+
|
|
|
|
|
+```tex
|
|
|
|
|
+[0.554s][info ][gc ] GC(32) Pause Full (G1 Compaction Pause) 9M->9M(10M) 16.621ms
|
|
|
|
|
+[0.554s][info ][gc,cpu ] GC(32) User=0.02s Sys=0.00s Real=0.02s
|
|
|
|
|
+[0.554s][info ][gc,ergo ] Attempting maximum full compaction clearing soft references
|
|
|
|
|
+[0.554s][info ][gc,start ] GC(33) Pause Full (G1 Compaction Pause)
|
|
|
|
|
+[0.554s][info ][gc,task ] GC(33) Using 1 workers of 10 for full compaction
|
|
|
|
|
+[0.554s][info ][gc,phases,start] GC(33) Phase 1: Mark live objects
|
|
|
|
|
+
|
|
|
|
|
+Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 案例:测试全局引用能否跨线程使用
|
|
|
|
|
+
|
|
|
|
|
+- 测试代码
|
|
|
|
|
+
|
|
|
|
|
+ ```java
|
|
|
|
|
+ long personHandler = globalReferenceTest.getPersonHandler();
|
|
|
|
|
+ Person person = globalReferenceTest.getPerson(personHandler);
|
|
|
|
|
+ System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
|
|
|
|
|
+ new Thread(()->{
|
|
|
|
|
+ //全局引用跨线程使用
|
|
|
|
|
+ System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
|
|
|
|
|
+ System.out.println("person = " + person);
|
|
|
|
|
+ }).start();
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+- 测试结果,全局引用可以跨线程使用
|
|
|
|
|
+
|
|
|
|
|
+ ```tex
|
|
|
|
|
+ Thread.currentThread().getName() = main
|
|
|
|
|
+ Thread.currentThread().getName() = Thread-0
|
|
|
|
|
+ person = Person{name='杨逸', age=18}
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+## 全局弱引用
|
|
|
|
|
+
|
|
|
|
|
+全局弱引用需要使用`NewWeakGlobalRef`函数进行创建,也可以返回给JVM使用,但是在JVM内存不足时会被垃圾回收,也可以使用`DeleteWeakGlobalRef`函数进行主动释放
|
|
|
|
|
+
|
|
|
|
|
+用法与全局引用类型,注意JVM内存不足时会被JVM进行GC
|
|
|
|
|
+
|
|
|
|
|
+### 案例:测试全局弱引用是否会被JVM垃圾回收
|
|
|
|
|
+
|
|
|
|
|
+1. 定义本地方法
|
|
|
|
|
+
|
|
|
|
|
+ ```java
|
|
|
|
|
+ class WeakGlobalReferenceTest {
|
|
|
|
|
+ static WeakGlobalReferenceTest globalWeakReferenceTest = new WeakGlobalReferenceTest();
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 测试在JVM中使用JNI创建的对象全局弱引用
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/05 21:06:10
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ static void test(){
|
|
|
|
|
+ //获取全局弱引用的句柄
|
|
|
|
|
+ long personHandler = globalWeakReferenceTest.getPersonHandler();
|
|
|
|
|
+
|
|
|
|
|
+ //根据句柄获取全局引用对象
|
|
|
|
|
+ Person person = globalWeakReferenceTest.getPerson(personHandler);
|
|
|
|
|
+ System.out.println("person.getName() = " + person.getName());
|
|
|
|
|
+ System.out.println("person.getAge() = " + person.getAge());
|
|
|
|
|
+ System.out.println(person);
|
|
|
|
|
+ //释放全局弱引用
|
|
|
|
|
+ globalWeakReferenceTest.freePerson(personHandler);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 测试JNI创建的对象全局引用是否会被JVM垃圾回收
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/05 21:06:34
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ static void testJVMGC(){
|
|
|
|
|
+ while (true){
|
|
|
|
|
+ globalWeakReferenceTest.getPersonHandler();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在JNI中创建一个全局引用,并返回句柄
|
|
|
|
|
+ * @return long 全局弱引用的句柄
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/04 21:29:12
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ native long getPersonHandler();
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据句柄获取全局弱引用对象
|
|
|
|
|
+ * @param handler 句柄
|
|
|
|
|
+ * @return {@code Person } 全局引用弱对象
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/04 21:30:09
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ native Person getPerson(long handler);
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 释放JNI创建的全局弱引用
|
|
|
|
|
+ * @param handler 句柄
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2026/03/04 21:30:38
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ native void freePerson(long handler);
|
|
|
|
|
+ }
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+2. 实现本地方法
|
|
|
|
|
+
|
|
|
|
|
+ ```c
|
|
|
|
|
+ #include "space_anyi_jni_reference_WeakGlobalReferenceTest.h"
|
|
|
|
|
+ JNIEXPORT jlong JNICALL Java_space_anyi_jni_reference_WeakGlobalReferenceTest_getPersonHandler(JNIEnv *env, jobject this){
|
|
|
|
|
+ //1.获取对应的jclass
|
|
|
|
|
+ jclass class_Person = (*env)->FindClass(env, "Lspace/anyi/jni/reference/Person;");
|
|
|
|
|
+ //2.获取对应的构造方法
|
|
|
|
|
+ jmethodID constructor = (*env)->GetMethodID(env, class_Person, "<init>", "(Ljava/lang/String;I)V");
|
|
|
|
|
+ //3.创建对象
|
|
|
|
|
+ jstring name = (*env)->NewStringUTF(env, "杨逸-弱引用");
|
|
|
|
|
+ jobject person = (*env)->NewObject(env, class_Person, constructor,name,18);
|
|
|
|
|
+ //4.获取全局弱引用
|
|
|
|
|
+ jobject weakGlobalReference = (*env)->NewWeakGlobalRef(env, person);
|
|
|
|
|
+ //5.转换为句柄,使用intptr_t类型作为中间类型,避免精度丢失
|
|
|
|
|
+ jlong handler = (jlong)(intptr_t)weakGlobalReference;
|
|
|
|
|
+ return handler;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JNIEXPORT jobject JNICALL Java_space_anyi_jni_reference_WeakGlobalReferenceTest_getPerson(JNIEnv *env, jobject this, jlong handler){
|
|
|
|
|
+ //1.通过句柄获取全局弱引用,使用intptr_t类型作为中间类型
|
|
|
|
|
+ jobject person = (jobject)(intptr_t)handler;
|
|
|
|
|
+ return person;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JNIEXPORT void JNICALL Java_space_anyi_jni_reference_WeakGlobalReferenceTest_freePerson(JNIEnv *env, jobject this, jlong handler){
|
|
|
|
|
+ //1.通过句柄获取全局弱引用,使用intptr_t类型作为中间类型
|
|
|
|
|
+ jobject weakGlobalReference = (jobject)(intptr_t)handler;
|
|
|
|
|
+ //2.释放全局弱引用
|
|
|
|
|
+ (*env)->DeleteWeakGlobalRef(env, weakGlobalReference);
|
|
|
|
|
+ }
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+3. 测试是否会被JVM进行GC
|
|
|
|
|
+
|
|
|
|
|
+ > - 限制JVM的最大使用内存为10m`-Xmx10m`
|
|
|
|
|
+ >
|
|
|
|
|
+ > - 打印GC日志`-XX:+PrintGC `
|
|
|
|
|
+
|
|
|
|
|
+ ```java
|
|
|
|
|
+ while (true){
|
|
|
|
|
+ globalReferenceTest.getPersonHandler();
|
|
|
|
|
+ }
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+4. 测试结果,触发GC是JNI创建的全局弱引用会被垃圾回收
|
|
|
|
|
+
|
|
|
|
|
+ ```tex
|
|
|
|
|
+ C:\Users\杨逸\.jdks\corretto-17.0.17\bin\java.exe -Xmx10M -XX:+PrintGC "-javaagent:D:\english lujing\IntelliJ IDEA 2021.3.3\lib\idea_rt.jar=26706:D:\english lujing\IntelliJ IDEA 2021.3.3\bin" -Dfile.encoding=UTF-8 -classpath E:\tmp\JNI_learn\target\classes space.anyi.jni.reference.Main
|
|
|
|
|
+ [0.003s][warning][gc] -XX:+PrintGC is deprecated. Will use -Xlog:gc instead.
|
|
|
|
|
+ [0.012s][info ][gc] Using G1
|
|
|
|
|
+ [0.122s][info ][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 4M->1M(10M) 1.864ms
|
|
|
|
|
+ [0.164s][info ][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 4M->0M(10M) 1.348ms
|
|
|
|
|
+ ...
|
|
|
|
|
+ [21.186s][info ][gc] GC(290) Pause Young (Normal) (G1 Evacuation Pause) 6M->1M(10M) 12.976ms
|
|
|
|
|
+ [21.261s][info ][gc] GC(291) Pause Young (Normal) (G1 Evacuation Pause) 6M->1M(10M) 13.076ms
|
|
|
|
|
+ [21.337s][info ][gc] GC(292) Pause Young (Normal) (G1 Evacuation Pause) 6M->1M(10M) 12.756ms
|
|
|
|
|
+
|
|
|
|
|
+ Process finished with exit code 130
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### 案例:测试弱全局引用能否跨线程使用
|
|
|
|
|
+
|
|
|
|
|
+- 测试代码
|
|
|
|
|
+
|
|
|
|
|
+ ```java
|
|
|
|
|
+ long personHandler = weakGlobalReferenceTest.getPersonHandler();
|
|
|
|
|
+ Person person = weakGlobalReferenceTest.getPerson(personHandler);
|
|
|
|
|
+ System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
|
|
|
|
|
+ new Thread(()->{
|
|
|
|
|
+ //全局引用跨线程使用
|
|
|
|
|
+ System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
|
|
|
|
|
+ System.out.println("person = " + person);
|
|
|
|
|
+ }).start();
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+- 测试结果,弱全局引用可以跨线程使用
|
|
|
|
|
+
|
|
|
|
|
+ ```tex
|
|
|
|
|
+ Thread.currentThread().getName() = main
|
|
|
|
|
+ Thread.currentThread().getName() = Thread-0
|
|
|
|
|
+ person = Person{name='杨逸-弱引用', age=18}
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+## 参考资料
|
|
|
|
|
+
|
|
|
|
|
+[案例代码仓库](https://git.anyi.space/yangyi/JNI_learn)
|