【JNI知识三】引用Java对象

  原始类型,如整形、字符型等,都是可以在Java和本地代码之间进行复制的。另一方面,任意的Java对象都是通过引用进行传递的。虚拟机必须跟踪已传递给本地代码中的所有对象,以便于这些对象不被垃圾回收器释放。反过来,本地代码必须有一个方式通知虚拟机它不再需要这些对象。此外,垃圾收集器必须能够将本机代码引用的对象移除。

全局和局部引用

  JNI将本地代码使用的对象引用分为两类:局部引用和全局引用。局部引用在本地方法的调用过程中是有效的,并在本地方法返回时自动释放。全局引用一直有效,直到他们被显式释放。

  对象被传递到本地方法并作为局部引用。JNI函数所返回的所有Java对象都是局部引用。JNI允许程序员从局部引用创建全局引用。期望Java对象的JNI函数可接受全局和局部引用。本地方法会返回局部或全局引用给VM作为结果。

  在大多数情况下,程序员应该依靠虚拟机在本地方法返回后释放所有的局部引用。不过,有些时候程序员也应该显式释放局部引用。考虑一下,比如,在以下情况下:

  • 本地方法访问一个大型Java对象,从而创建了一个该Java对象的本地引用。然后,本地方法执行额外的计算,然后再返回给调用者。大型Java对象的局部引用会防止对象被垃圾回收,即使该对象在剩余部分运算中不再被使用。
  • 本地方法创建了大量的局部引用,虽然他们中的所有并不是在同一时间被使用。由于虚拟机需要一定的空间来保存一个本地引用,创建太多的局部引用可能会导致系统运行内存溢出。例如,一个本地方法遍历一个大的对象数组,获取元素作为局部引用,并在每次迭代中操作一个元素。每次迭代之后,程序员就不再需要这些数组元素的本地引用。

  在本地方法内部,JNI允许程序员在任何时候手动删除本地引用。为了确保程序员可以手动释放局部引用,JNI函数不被允许创建额外的局部引用,它们作为结果返回的引用除外。

  局部引用仅在他们被创建的线程中是有效的。本地代码不能传递本地引用从一个线程到另一个线程。

实现局部引用

  为了实现局部引用,Java虚拟机为每个从Java到本地方法的控制权的转移创建了一个注册表。注册表映射了Java对象的不可移动的本地引用,并保持对象不被垃圾回收。所有传递给本地方法的Java对象(包括那些作为JNI函数的调用结果被返回的)都会被自动添加到注册表中。在本地方法返回后注册表将会被删除,使所有记录可以被垃圾回收

  有不同的方法来实现注册表,如使用表,链表,或哈希表。虽然可使用引用计数,以避免在注册表中重复的项目,JNI的实现不一定必须要检测和合并重复的条目。

  注意,局部变量通过谨慎地扫描本地栈不能被如实地实现,本机代码可以存储局部引用到全局或堆数据结构。

 

原文地址:https://www.cnblogs.com/wzfyang/p/2654765.html