jni如何判斷兩個jobject是否爲同一個java對象
jni開發時有時候需要將java對象緩存到native層,方便native層通過jni的反射方法進行回調操作。通常我們會將回調接口callback在native層存放爲global reference全局引用,熟悉jni開發的都知道,jni傳入到native 層的jobject生命週期僅僅是函數的生命週期,當jni函數返回後對應的jobject對象就會失效,不能再操作,所以就必須申請爲global reference。
NewGlobalRef 將變量申請爲全局引用,此時java虛擬機會保留jobject所指向的對象防止被垃圾回收器回收。 DeleteGlobalRef 釋放全局引用,允許java虛擬機回收該引用指向的java對象,對於不需要使用的global reference必須調用該方法,否則會引起java虛擬機內存泄漏。
既然global reference的實質是指向java對象,那麼我們在將某個變量聲明爲global reference時如何判斷該jobject是否已經是當前設置的global reference呢。經過一番查詢,看到了一下方法
IsSameObject 如果兩個jobject指向同一個java 對象那麼返回true,否則返回false。
既然有方法了,接下來就是驗證工作了。
Hello.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Hello */
#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Hello
* Method: native_setObjectToGlobalRef
* Signature: (LHello/Book;)V
*/
JNIEXPORT void JNICALL Java_Hello_native_1setObjectToGlobalRef
(JNIEnv *, jclass, jobject);
/*
* Class: Hello
* Method: native_release
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_Hello_native_1release
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
Hello.cpp
#ifdef __cplusplus
extern "C" {
#endif
#include <cstdio>
#include "Hello.h"
// 全局變量,用於存儲全局引用的
jobject g_book_ref = NULL;
/*
* Class: Hello
* Method: native_setObjectToGlobalRef
* Signature: (LHello/Book;)V
*/
JNIEXPORT void JNICALL Java_Hello_native_1setObjectToGlobalRef
(JNIEnv *env, jclass jclazz, jobject jbook) {
// 如果輸入參數爲null,那麼不需要繼續操作
if (NULL == jbook) {
printf("native_setObjectToGlobalRef jbook is NUll and g_book_ref is NOT NULL. we need to delete reference of old g_book_ref\n");
if (NULL != g_book_ref) {
env->DeleteGlobalRef(g_book_ref); // 刪除老的全局引用
g_book_ref = NULL;
}
return;
}
// 如果全局引用已經設置了,那麼需要判斷jni函數參數的jbook是否與當前的全局引用指向同一個java對象
if (NULL != g_book_ref) {
// 如果指向同一個對象,那麼不需要再次爲該jbook申請全局引用
if (env->IsSameObject(g_book_ref, jbook)) {
printf("native_setObjectToGlobalRef isSameObject: true, g_book_ref: %p, jbook: %p\n", g_book_ref, jbook);
} else {
// 如果指向不同對象,那麼先釋放老的全局引用,再爲jbook申請全局引用
printf("native_setObjectToGlobalRef isSameObject: false\n");
printf("native_setObjectToGlobalRef g_book_ref and jbook is not same object. we need to delete reference of old g_book_ref\n");
env->DeleteGlobalRef(g_book_ref);
printf("native_setObjectToGlobalRef create global reference to g_book_ref\n");
g_book_ref = env->NewGlobalRef(jbook);
}
} else {
printf("native_setObjectToGlobalRef g_book_ref is NULL\n");
printf("native_setObjectToGlobalRef create global reference to g_book_ref\n");
g_book_ref = env->NewGlobalRef(jbook);
}
}
/*
* Class: Hello
* Method: native_release
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_Hello_native_1release
(JNIEnv *env, jclass jclazz) {
printf("native_release g_book_ref: %p\n", g_book_ref);
if (NULL != g_book_ref) {
env->DeleteGlobalRef(g_book_ref);
g_book_ref = NULL;
}
}
#ifdef __cplusplus
}
#endif
Hello.java
public class Hello {
static {
try {
String sysName = System.getProperty("os.name");
if (sysName.contains("Linux")) {
System.load(System.getProperty("user.dir") + "/libHello.so");
} else if (sysName.contains("Drawin") || sysName.contains("Mac")) {
System.load(System.getProperty("user.dir") + "/libHello.dylib");
}
} catch (java.lang.UnsatisfiedLinkError e) {
e.printStackTrace();
System.err.println("load so failed.");
System.exit(1);
}
}
static class Book {
public String name;
public Book() {}
public Book(String name) {
this.name = name;
}
}
public native static void native_setObjectToGlobalRef(Book book);
public native static void native_release();
public static void main(String args[]) {
Book b1 = new Book();
Book b2 = new Book();
System.out.println("set b1");
native_setObjectToGlobalRef(b1);
System.out.println("set b2");
native_setObjectToGlobalRef(b2);
System.out.println("##set b2");
native_setObjectToGlobalRef(b2);
System.out.println("##set null");
native_setObjectToGlobalRef(null);
native_release();
}
}
makefile
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
main:
gcc -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/darwin" Hello.cpp -dynamiclib -o libHello.dylib
endif
ifeq ($(UNAME_S),Linux)
main:
gcc -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" Hello.cpp -shared -fPIC -o libHello.so
endif
運行命令
$ make
$ javac Hello.java
運行程序
$ java -Djava.library.path=".:${JAVA_HOME}" Hello
輸出結果
set b1
native_setObjectToGlobalRef g_book_ref is NULL
native_setObjectToGlobalRef create global reference to g_book_ref
set b2
native_setObjectToGlobalRef isSameObject: false
native_setObjectToGlobalRef g_book_ref and jbook is not same object. we need to delete reference of old g_book_ref
native_setObjectToGlobalRef create global reference to g_book_ref
##set b2
native_setObjectToGlobalRef isSameObject: true, g_book_ref: 0x7fb16ae08e90, jbook: 0x70000ce4a900
##set null
native_setObjectToGlobalRef jbook is NUll and g_book_ref is NOT NULL. we need to delete reference of old g_book_ref
native_release g_book_ref: 0x0
1. set b1時
因爲是首次設置,此時native的g_book_ref還是null,沒有被指向java對象,所以需要用全局引用爲jbook保存java對象引用;
2. set b2時
isSameObject返回false,證明g_book_ref和jbook指向的對象不一樣(此時jbook指向b2,而g_book_ref指向步驟1設置的b1),所以需要先刪除老的b1全局引用再爲b2申請全局引用;
3. 再次設置b2時
isSameObject返回true,證明g_book_ref和jbook指向的對象一樣(此時jbook和g_book_ref都指向b2,注意兩者的native地址不一樣),爲了節省操作,所以不需要在爲jbook申請全局引用;
4. 設置爲null
因爲jbook爲null,所以需要釋放全局引用g_book_ref。
5. native釋放
在Book native釋放時因爲步驟4已經提前釋放了全局引用,所以不需要再釋放了。