美文网首页
JNI引用管理

JNI引用管理

作者: 程序员札记 | 来源:发表于2022-06-08 23:29 被阅读0次

JNI中有四种引用类型,JNIInvalidRefType无效引用,JNILocalRefType本地引用,JNIGlobalRefType全局引用,JNIWeakGlobalRefType全局弱引用,本地引用类似于方法调用中局部变量对应的对象引用,全局引用类似于类静态变量对应的对象引用,全局弱引用对应Java中的PhantomReferences虚引用。Java代码的解释执行过程中并没有所谓的引用类型,为啥本地代码需要引入引用类型了?Java代码中引用或本地代码中的引用究竟有啥区别?本地引用在本地方法结束后自动销毁,全局引用必须手动调用DeleteGlobalRef方法才能释放对象引用,这是怎么实现的?

JNI引用操作API

JNI引用操作的API的用法和示例在 JNIEnv API详解都有说明,这里重点关注其源码实现,具体实现在hotspot/src/share/vm/prims/jni.cpp中,核心代码说明如下:

JNI_ENTRY(jobject, jni_NewGlobalRef(JNIEnv *env, jobject ref))
 
  //resolve方法是从ref解析出oop
  Handle ref_handle(thread, JNIHandles::resolve(ref));
  //创建全局引用
  jobject ret = JNIHandles::make_global(ref_handle);
 
  return ret;
JNI_END
 
JNI_ENTRY_NO_PRESERVE(void, jni_DeleteGlobalRef(JNIEnv *env, jobject ref))
  //删除全局引用
  JNIHandles::destroy_global(ref);
 
JNI_END
 
JNI_ENTRY(jobject, jni_NewLocalRef(JNIEnv *env, jobject ref))
 
  //创建本地引用,resolve方法同上
  jobject ret = JNIHandles::make_local(env, JNIHandles::resolve(ref));
 
  return ret;
JNI_END
 
JNI_QUICK_ENTRY(void, jni_DeleteLocalRef(JNIEnv *env, jobject obj))
 
  //删除本地引用
  JNIHandles::destroy_local(obj);
 
JNI_END
 
JNI_ENTRY(jweak, jni_NewWeakGlobalRef(JNIEnv *env, jobject ref))
 
  //同上,创建全局弱引用
  Handle ref_handle(thread, JNIHandles::resolve(ref));
  jweak ret = JNIHandles::make_weak_global(ref_handle);
 
  return ret;
JNI_END
 
JNI_ENTRY(void, jni_DeleteWeakGlobalRef(JNIEnv *env, jweak ref))
  //删除全局弱引用
  JNIHandles::destroy_weak_global(ref);
 
JNI_END
 
JNI_LEAF(jobjectRefType, jni_GetObjectRefType(JNIEnv *env, jobject obj))
 
  //条件判断不同的引用类型
  jobjectRefType ret;
  if (JNIHandles::is_local_handle(thread, obj) ||
      JNIHandles::is_frame_handle(thread, obj))
    ret = JNILocalRefType;
  else if (JNIHandles::is_global_handle(obj))
    ret = JNIGlobalRefType;
  else if (JNIHandles::is_weak_global_handle(obj))
    ret = JNIWeakGlobalRefType;
  else
    ret = JNIInvalidRefType;
 
  return ret;
JNI_END
 
JNI_ENTRY(jint, jni_PushLocalFrame(JNIEnv *env, jint capacity))
 
 //只是校验下参数是否合法
  if (capacity < 0 ||
      ((MaxJNILocalCapacity > 0) && (capacity > MaxJNILocalCapacity))) {
    return JNI_ERR;
  }
  //原来的active_handles
  JNIHandleBlock* old_handles = thread->active_handles();
  //创建新的JNIHandleBlock
  JNIHandleBlock* new_handles = JNIHandleBlock::allocate_block(thread);
  assert(new_handles != NULL, "should not be NULL");
  //将原来的active_handles设置为的新的JNIHandleBlock的pop_frame_link,即通过pop_frame_link可以不断往上
  //追溯至最开始的active_handles
  new_handles->set_pop_frame_link(old_handles);
  //将新创建的JNIHandleBlock设置为active_handles
  thread->set_active_handles(new_handles);
  jint ret = JNI_OK;
 
  return ret;
JNI_END
 
 
JNI_ENTRY(jobject, jni_PopLocalFrame(JNIEnv *env, jobject result))
  
  Handle result_handle(thread, JNIHandles::resolve(result));
  
  JNIHandleBlock* old_handles = thread->active_handles();
  //当前JNIHandleBlock上一个的JNIHandleBlock
  JNIHandleBlock* new_handles = old_handles->pop_frame_link();
  if (new_handles != NULL) {
     //active_handles恢复至原来的JNIHandleBlock
    thread->set_active_handles(new_handles);
    //old_handles释放
    old_handles->set_pop_frame_link(NULL);              // clear link we won't release new_handles below
    JNIHandleBlock::release_block(old_handles, thread); // may block
    result = JNIHandles::make_local(thread, result_handle());
  }
 
  return result;
JNI_END

从上述源码可知,JNI引用操作实际使用JNIHandles实现的。

Handle

Handle相当于一个值对象(ValueObj,即目标值的一个包装类),本身可以作为参数或者返回值,通过操作符重载可以像直接使用值使用Handle。该对象主要是在垃圾回收期间保护用来保护oop,即通过Handle实现垃圾回收期间对象复制导致oop发生改变但oop使用方无感知,因此JVM要求oop无论是作为参数还是返回值必须使用Handle来传递。其定义在hotspot/src/share/vm/runtime/handles.hpp中,实现在同目录下的handles.cpp中,具体定义如下:

class Handle VALUE_OBJ_CLASS_SPEC {
 private:
 //目标值,注意oop本身实际是oopDesc*的别名,即此处是oopDesc实例的指针的指针
  oop* _handle;
 
 protected:
  //这两个方法都是返回oop,non_null_obj做了非空校验
  oop     obj() const                            { return _handle == NULL ? (oop)NULL : *_handle; }
  oop     non_null_obj() const                   { assert(_handle != NULL, "resolving NULL handle"); return *_handle; }
 
 public:
  //构建空对象
  Handle()                                       { _handle = NULL; }
  //底层实现会获取当前线程
  Handle(oop obj);
  //底层实现会校验thread是否是当前线程
  Handle(Thread* thread, oop obj);
 
  //操作符重载
  oop     operator () () const                   { return obj(); }
  oop     operator -> () const                   { return non_null_obj(); }
  bool    operator == (oop o) const              { return obj() == o; }
  bool    operator == (const Handle& h) const          { return obj() == h.obj(); }
 
  //非空
  bool    is_null() const                        { return _handle == NULL; }
  bool    not_null() const                       { return _handle != NULL; }
 
  // Debugging
  void    print()                                { obj()->print(); }
  
  //_handle不用从当前线程分配内存,直接赋值成目标值,这个方法很少使用
  //dummy是无效参数,避免同Handle(oop obj)混淆
  Handle(oop *handle, bool dummy)                { _handle = handle; }
  
  //直接访问_handle,因为C是值复制,所以这种方式无法保证原来的值是有效的,使用不安全
  oop* raw_value()                               { return _handle; }
  //获取原始的oop
  static oop raw_resolve(oop *handle)            { return handle == NULL ? (oop)NULL : *handle; }
};

不同oop对应的Handle不同,Handle是所有类型的Handle的父类,其他子类都是通过宏定义实现的,如下图:


image.png

即Handle的子类包括instanceHandle,arrayHandle,objArrayHandle,typeArrayHandle,子类的构造方法跟Handle一致,只是加了一步oop类型校验。

Handle的实现在hotspot/src/share/vm/runtime/handles.inline.hpp中,相关源码说明如下:

inline Handle::Handle(oop obj) {
  if (obj == NULL) {
    _handle = NULL;
  } else {
     //handle_area()方法返回当前线程的_handle_area属性,该属性是HandleArea实例指针
     //然后通过HandleArea 分配Handle,
    _handle = Thread::current()->handle_area()->allocate_handle(obj);
  }
}
 
 
inline Handle::Handle(Thread* thread, oop obj) {
  //校验thread是否是当前线程
  assert(thread == Thread::current(), "sanity check");
  if (obj == NULL) {
    _handle = NULL;
  } else {
    _handle = thread->handle_area()->allocate_handle(obj);
  }
}
 
private:
  oop* real_allocate_handle(oop obj) {
  //oopSize就是指针的字节数,8,即分配8字节的内存
    oop* handle = (oop*) Amalloc_4(oopSize);
    //让handle指向obj
    *handle = obj;
    return handle;
  }
 
 public:
  oop* allocate_handle(oop obj) { return real_allocate_handle(obj); }

Metadata Handles

Handle是针对各种类型的oop的,Metadata Handles是针对元数据的,元数据因RedefineClasses而改变,但借助Handle上层对此无感知。其定义也是在hotspot/src/share/vm/runtime/handles.hpp中,如下图:

image.png

方法跟Handle基本一致,此类型的Handle只有两个,methodHandle和constantPoolHandle,注意这两是继承自StackObj,并非Handle的子类;Method类和ConstantPool类都继承自表示元数据的Metadata类。

实现在hotspot/src/share/vm/runtime/handles.inline.hpp中,跟类定义一样同样基于宏的方式,如下:

#define DEF_METADATA_HANDLE_FN(name, type) \
inline name##Handle::name##Handle(type* obj) : _value(obj), _thread(NULL) {       \
  if (obj != NULL) {
   //校验元数据是否有效                                                   \
    assert(((Metadata*)obj)->is_valid(), "obj is valid");              \
    _thread = Thread::current();
    //校验当前线程是否位于调用栈上                                       \
    assert (_thread->is_in_stack((address)this), "not on stack?"); 
    //将元数据放到目标对象的_metadata_handles属性中,该属性是一个专门存储元数据指针的自动增长的数组GrowableArray<Metadata*>*
    _thread->metadata_handles()->push((Metadata*)obj);                 \
  }                                                                    \
}                                                                      \
inline name##Handle::name##Handle(Thread* thread, type* obj) : _value(obj), _thread(thread) { \
  if (obj != NULL) {                                                   \
    assert(((Metadata*)obj)->is_valid(), "obj is valid");              \
    assert(_thread == Thread::current(), "thread must be current");    \
    assert (_thread->is_in_stack((address)this), "not on stack?");     \
    _thread->metadata_handles()->push((Metadata*)obj);                 \
  }                                                                    \
}
 
//复制目标Handle的属性,如果目标Handle的_value属性不为空需要将其保存到当前线程的 _metadata_handles属性中                                                                    \
inline name##Handle::name##Handle(const name##Handle &h) {             \
  _value = h._value;                                                   \
  if (_value != NULL) {                                                \
    assert(_value->is_valid(), "obj is valid");                        \
    if (h._thread != NULL) {                                           \
      assert(h._thread == Thread::current(), "thread must be current");\
      _thread = h._thread;                                             \
    } else {                                                           \
      _thread = Thread::current();                                     \
    }                                                                  \
    assert (_thread->is_in_stack((address)this), "not on stack?");     \
    _thread->metadata_handles()->push((Metadata*)_value);              \
  } else {                                                             \
    _thread = NULL;                                                    \
  }                                                                    \
}           
//=号操作符重载,逻辑同上                                                           \
inline name##Handle& name##Handle::operator=(const name##Handle &s) {  \
  remove();                                                            \
  _value = s._value;                                                   \
  if (_value != NULL) {                                                \
    assert(_value->is_valid(), "obj is valid");                        \
    if (s._thread != NULL) {                                           \
      assert(s._thread == Thread::current(), "thread must be current");\
      _thread = s._thread;                                             \
    } else {                                                           \
      _thread = Thread::current();                                     \
    }                                                                  \
    assert (_thread->is_in_stack((address)this), "not on stack?");     \
    _thread->metadata_handles()->push((Metadata*)_value);              \
  } else {                                                             \
    _thread = NULL;                                                    \
  }                                                                    \
  return *this;                                                        \
}
 
//从当前线程的_metadata_handles属性中移除当前Handle保存的元数据                                                                      \
inline void name##Handle::remove() {                                   \
  if (_value != NULL) {                                                \
    int i = _thread->metadata_handles()->find_from_end((Metadata*)_value); \
    assert(i!=-1, "not in metadata_handles list");                     \
    _thread->metadata_handles()->remove_at(i);                         \
  }                                                                    \
}                                                                      \
inline name##Handle::~name##Handle () { remove(); }                    \
DEF_METADATA_HANDLE_FN(method, Method)
DEF_METADATA_HANDLE_FN(constantPool, ConstantPool)

KlassHandle

提供KlassHandle是因为定义Metadata Handles的宏不能适用于Klass,无法提供Klass* <-> Klass* 的转换,同Metadata Handles,KlassHandle也是为了实现RedefineClasses导致Klass发生变更但上层代码无感知,其定义和实现都在hotspot/src/share/vm/runtime/handles.hpp中,是最简单的,如下图:


image.png

JNIHandles

JNIHandles的定义位于hotspot/src/share/vm/memory/allocation.hpp中,提供了JNI中本地和全局引用操作的所有接口,该类定义的关键属性只有三个静态属性:

  • _global_handles:保存全局引用的JNIHandleBlock链表的头元素
  • _weak_global_handles:保存全局弱引用的JNIHandleBlock链表的头元素
  • _deleted_handle:表示已删除的Handle,Handle就是引用

本地引用操作

jobject JNIHandles::make_local(JNIEnv* env, oop obj) {
 if (obj == NULL) {
   return NULL;                // ignore null handles
 } else {
   //获取当前JNIEnv关联的JavaThread
   JavaThread* thread = JavaThread::thread_from_jni_environment(env);
   //校验obj是否指向保留区域
   assert(Universe::heap()->is_in_reserved(obj), "sanity check");
   //active_handles方法返回JavaThread的_active_handles属性,该属性是JNIHandleBlock*
   return thread->active_handles()->allocate_handle(obj);
 }
}

inline void JNIHandles::destroy_local(jobject handle) {
 if (handle != NULL) {
   //jobject_ref返回该handle关联的oop
   //deleted_handle方法返回_deleted_handle属性
   //这里实际将目标handle的oop置成_deleted_handle
   jobject_ref(handle) = deleted_handle();
 }
}

inline oop& JNIHandles::jobject_ref(jobject handle) {
 assert(!is_jweak(handle), "precondition");
 return *reinterpret_cast<oop*>(handle);
}

 static oop deleted_handle()   { return _deleted_handle; }

本地引用能够在方法调用结束后就自动销毁,其关键就在于JavaThread的_active_handles属性,该属性的相关方法如下:

image.png

搜索set_active_handles方法的调用,其结果如下:

image.png

其中JavaCallWrapper的两次调用就是自动销毁实现的关键,Java代码中每次方法调用前都需要创建一个新的JavaCallWrapper,这个过程会为本次方法调用创建一个新的JNIHandleBlock并保存调用前的调用栈状态,方法调用结束后该对象通过C++的析构函数销毁,销毁时会释放之前创建的JNIHandleBlock然后恢复调用栈到调用前的状态。其部分关键代码如下:

JavaCallWrapper::JavaCallWrapper(methodHandle callee_method, Handle receiver, JavaValue* result, TRAPS) {
 
  // 创建一个新的JNIHandleBlock
  JNIHandleBlock* new_handles = JNIHandleBlock::allocate_block(thread);
 
  //准备调用的方法和方法返回值的接受者
  _callee_method = callee_method();
  _receiver = receiver();
 
 
  //保存原线程的active_handles
  _thread       = (JavaThread *)thread;
  _handles      = _thread->active_handles();    // save previous handle block & Java frame linkage
 
  //设置新的active_handles
  _thread->set_active_handles(new_handles);     // install new handle block and reset Java frame linkage
 
 
}
 
JavaCallWrapper::~JavaCallWrapper() {
  assert(_thread == JavaThread::current(), "must still be the same thread");
 
  //当前线程的active_handles
  JNIHandleBlock *_old_handles = _thread->active_handles();
  //_handles是方法调用前的active_handles,此处将其恢复
  _thread->set_active_handles(_handles);
 
  //释放当前线程的active_handles
  JNIHandleBlock::release_block(_old_handles, _thread);
}

全局引用操作

jobject JNIHandles::make_global(Handle obj) {
  //校验是否处于GC中
  assert(!Universe::heap()->is_gc_active(), "can't extend the root set during GC");
  jobject res = NULL;
  //校验oop非空
  if (!obj.is_null()) {
    //因为是对全局唯一的_global_handles属性操作,所以此处需要加锁
    MutexLocker ml(JNIGlobalHandle_lock);
    //校验obj是否指向保留内存区域
    assert(Universe::heap()->is_in_reserved(obj()), "sanity check");
    //通过静态属性_global_handles分配handler
    res = _global_handles->allocate_handle(obj());
  } else {
    //根据配置清理没有被引用的oop
    CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops());
  }
 
  return res;
}
 
void JNIHandles::destroy_global(jobject handle) {
  if (handle != NULL) {
    //校验目标handle是否是global handle
    assert(is_global_handle(handle), "Invalid delete of global JNI handle");
    //将目标handle关联的oop置成deleted_handle
    jobject_ref(handle) = deleted_handle();
  }
}

从源码分析可知,全局引用实际由JNIHandles的静态属性_global_handle保存,从而可以确保这部分引用不会随着方法调用结束而销毁,只能手动调用DeleteGlobalRef才能释放。

全局弱引用

jobject JNIHandles::make_weak_global(Handle obj) {
  assert(!Universe::heap()->is_gc_active(), "can't extend the root set during GC");
  jobject res = NULL;
  if (!obj.is_null()) {
    {
      MutexLocker ml(JNIGlobalHandle_lock);
      assert(Universe::heap()->is_in_reserved(obj()), "sanity check");
      //通过_weak_global_handles分配handle
      res = _weak_global_handles->allocate_handle(obj());
    }
    //将得到的handle打标,加上weak_tag_value,打标是为了直接从指针上区分是否是全局弱引用
    assert(is_ptr_aligned(res, weak_tag_alignment), "invariant");
    char* tptr = reinterpret_cast<char*>(res) + weak_tag_value;
    res = reinterpret_cast<jobject>(tptr);
  } else {
    CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops());
  }
  return res;
}
 
void JNIHandles::destroy_weak_global(jobject handle) {
  if (handle != NULL) {
    //将目标handle关联的oop设置成deleted_handle
    jweak_ref(handle) = deleted_handle();
  }
}
 
 
inline oop& JNIHandles::jweak_ref(jobject handle) {
  assert(is_jweak(handle), "precondition");
  //恢复成原样
  char* ptr = reinterpret_cast<char*>(handle) - weak_tag_value;
  return *reinterpret_cast<oop*>(ptr);
}

可以看出其实现基本跟全局引用一样,不多多了一道指针的转化。

引用类型判断

bool JNIHandles::is_local_handle(Thread* thread, jobject handle) {
  JNIHandleBlock* block = thread->active_handles();
 
  // Look back past possible native calls to jni_PushLocalFrame.
  while (block != NULL) {
   //先判断当前active_handles是否包含目标handle
    if (block->chain_contains(handle)) {
      return true;
    }
    //在pop_frame_link中尝试查找,即执行PushLocalFrame前的线程active_handles
    block = block->pop_frame_link();
  }
  return false;
}
 
 
bool JNIHandles::is_frame_handle(JavaThread* thr, jobject obj) {
  //判断目标对象的地址是否在当前调用栈的地址范围内,如果在说明属于frame handle
  return (thr->has_last_Java_frame() &&
         (void*)obj < (void*)thr->stack_base() &&
         (void*)obj >= (void*)thr->last_Java_sp());
}
 
 
bool JNIHandles::is_global_handle(jobject handle) {
  //_global_handles中查找
  return _global_handles->chain_contains(handle);
}
 
 
bool JNIHandles::is_weak_global_handle(jobject handle) {
 //在_weak_global_handles中查找
  return _weak_global_handles->chain_contains(handle);
}

引用遍历

void JNIHandles::oops_do(OopClosure* f) {
  f->do_oop(&_deleted_handle);
  //遍历_global_handles中保存的引用
  _global_handles->oops_do(f);
}
 
 
void JNIHandles::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) {
//遍历_weak_global_handles中保存的引用
  _weak_global_handles->weak_oops_do(is_alive, f);
}
 
 
void JNIHandles::weak_oops_do(OopClosure* f) {
  AlwaysTrueClosure always_true;
  weak_oops_do(&always_true, f);
}

oops_do的调用方就是各垃圾回收器的实现类,如下图:

image.png

JNIHandleBlock

定义

JNIHandleBlock是实际用于保存JNI本地或者全局引用的地方,该类的继承关系如下:


image.png

CHeapObj类表示通过C语言中free & malloc方法管理的对象,JVM中所有此类对象都会继承CHeapObj,该类重载了new和delete运算符。上述截图是在非生产环境下的类继承关系,生产环境下CHeapObj没有父类AllocatedObj,该类主要用于Debug。

CHeapObj的定义位于hotspot/src/share/vm/memory/allocation.hpp中,如下图:

image.png

JNIHandleBlock的定义位于hotspot/src/share/vm/runtime/jniHandles.hpp中,该类提供的重要实例属性如下:

  • _handles:实际保存oop的数组,初始大小为32
  • _top:_handles中下一个未使用的位置的索引
  • _next:下一个JNIHandleBlock指针

下列四个实例属性只是JNIHandleBlock链表中第一个JNIHandleBlock(即链表最前面的)使用,这里为了避免另外定义一个类使得代码复杂化:

  • _last:最近使用的JNIHandleBlock实例
  • _pop_frame_link:调用PopLocalFrame时需要恢复的JNIHandleBlock实例
  • _free_list:_handles中空闲的元素
  • _allocate_before_rebuild:在重建前已经分配的JNIHandleBlock的实例个数
  • _planned_capacity:当前调用栈帧的planned capacity属性,默认大小等于_handles数组的大小

下面是两个静态属性:

  • _block_free_list:还有空闲空间的JNIHandleBlock链表
  • _blocks_allocated:已经分配的JNIHandleBlock实例个数

JNIHandleBlock提供的关键方法如下:

  • zap():将_handles数组中空闲元素填充为bad_handle

  • clear():打标,表明当前Block和后面的任何Block不包含任何oop

  • allocate_handle(oop obj): 分配oop Handle

  • rebuild_free_list(): 整理当前的block,将所有的释放的_handles数组元素用链表串起来

  • chain_contains和contains:判断当前JNIHandleBlock是否包含目标Handle

  • allocate_block和release_block:JNIHandleBlock的创建和销毁

  • pop_frame_link和set_pop_frame_link:对应于JNI中PushLocalFrame/PopLocalFrame方法

  • oops_do和weak_oops_do:垃圾回收使用的方法,以JNIHandleBlock保存的oop为根对象遍历所有引用的对象

  • set_planned_capacity和get_number_of_live_handles:对JNI Check中EnsureLocalCapacity方法提供支持

allocate_block和release_block

其源码如下:

JNIHandleBlock* JNIHandleBlock::allocate_block(Thread* thread)  {
  assert(thread == NULL || thread == Thread::current(), "sanity check");
  JNIHandleBlock* block;
  if (thread != NULL && thread->free_handle_block() != NULL) {
    //直接使用当前线程的空闲的_free_handle_block
    block = thread->free_handle_block();
    //将block的下一个block置为_free_handle_block
    thread->set_free_handle_block(block->_next);
  }
  else {
    创建新的JNIHandleBlock需要加锁
    MutexLockerEx ml(JNIHandleBlockFreeList_lock,
                     Mutex::_no_safepoint_check_flag);
    //没有空闲的block
    if (_block_free_list == NULL) {
      //创建新的block
      block = new JNIHandleBlock();
      //已分配数量加1
      _blocks_allocated++;
      //打印日志
      if (TraceJNIHandleAllocation) {
        tty->print_cr("JNIHandleBlock " INTPTR_FORMAT " allocated (%d total blocks)",
                      block, _blocks_allocated);
      }
      //新block初始化
      if (ZapJNIHandleArea) block->zap();
      
    } else {
      //直接使用空闲block列表中的block
      block = _block_free_list;
      //更新_block_free_list为其下一个block
      _block_free_list = _block_free_list->_next;
    }
  }
  //属性初始化
  block->_top  = 0;
  block->_next = NULL;
  block->_pop_frame_link = NULL;
  block->_planned_capacity = block_size_in_oops;
  
  return block;
}
 
 
void JNIHandleBlock::release_block(JNIHandleBlock* block, Thread* thread) {
  assert(thread == NULL || thread == Thread::current(), "sanity check");
  //获取该block的上一个block,理论上应该永远为空
  JNIHandleBlock* pop_frame_link = block->pop_frame_link();
  //把block归还到当前线程的free_handle_block中,前提是线程未退出
  if (thread != NULL ) {
    //恢复初始状态
    if (ZapJNIHandleArea) block->zap();
    //获取当前线程的free_handle_block,将其置为block
    JNIHandleBlock* freelist = thread->free_handle_block();
    block->_pop_frame_link = NULL;
    thread->set_free_handle_block(block);
 
    //如果线程原来的free block非空,则遍历block直到block的next属性为空,将其置为原来的free block
    if ( freelist != NULL ) {
      while ( block->_next != NULL ) block = block->_next;
      block->_next = freelist;
    }
    block = NULL;
  }
  //即线程已退出,将block归还到JNIHandleBlock的_block_free_list
  if (block != NULL) {
    // Return blocks to free list
    因为是对全局属性_block_free_list操作,需要加锁
    MutexLockerEx ml(JNIHandleBlockFreeList_lock,
                     Mutex::_no_safepoint_check_flag);
    while (block != NULL) {
      //恢复初始 
      if (ZapJNIHandleArea) block->zap();
      //获取下一个block
      JNIHandleBlock* next = block->_next;
      //将block的下一个置为_block_free_list
      block->_next = _block_free_list;
      //将block置为_block_free_list
      _block_free_list = block;
      block = next;
    }
  }
  if (pop_frame_link != NULL) {
    //正常不会调用此代码
    release_block(pop_frame_link, thread);
  }
}

结合源码可知,已分配的JNIHandleBlock是可以不断循环利用的,所谓的释放只是将其标记为free而已,并没有释放其占用的堆存。

allocate_handle和rebuild_free_list

其源码说明如下:

jobject JNIHandleBlock::allocate_handle(oop obj) {
  assert(Universe::heap()->is_in_reserved(obj), "sanity check");
  if (_top == 0) {
    //_top=0说明此block是第一次分配handle
    //依次遍历当前block后面的block,将其恢复成初始化状态
    for (JNIHandleBlock* current = _next; current != NULL;
         current = current->_next) {
      assert(current->_last == NULL, "only first block should have _last set");
      assert(current->_free_list == NULL,
             "only first block should have _free_list set");
      current->_top = 0;
      if (ZapJNIHandleArea) current->zap();
    }
    // Clear initial block
    _free_list = NULL;
    _allocate_before_rebuild = 0;
    _last = this;
    if (ZapJNIHandleArea) zap();
  }
 
  //尝试从最近使用的block中分配,top=0会将_last初始化成当前block
  if (_last->_top < block_size_in_oops) {
    oop* handle = &(_last->_handles)[_last->_top++];
    *handle = obj;
    return (jobject) handle;
  }
 
  //尝试使用_free_list分配
  if (_free_list != NULL) {
    oop* handle = _free_list;
    _free_list = (oop*) *_free_list;
    *handle = obj;
    return (jobject) handle;
  }
  //当前block分配完了,使用下一个block分配
  if (_last->_next != NULL) {
    //next置为_last
    _last = _last->_next;
    return allocate_handle(obj);
  }
 
  //当前block没有足够的空闲空间了,第一次不足时_allocate_before_rebuild等于0触发重建,
  //重建操作修改_allocate_before_rebuild为一个正值,即需要添加的新的block的数量
  //每次新增都将_allocate_before_rebuild减一
  if (_allocate_before_rebuild == 0) {
      //执行重建
      rebuild_free_list();        // updates _allocate_before_rebuild counter
  } else {
    // Append new block
    Thread* thread = Thread::current();
    Handle obj_handle(thread, obj);
    //创建一个新的block,置为当前block的next
    _last->_next = JNIHandleBlock::allocate_block(thread);
    _last = _last->_next;
    _allocate_before_rebuild--;
    obj = obj_handle();
  }
  //再一次重试 
  return allocate_handle(obj);  // retry
}
 
void JNIHandleBlock::rebuild_free_list() {
  assert(_allocate_before_rebuild == 0 && _free_list == NULL, "just checking");
  int free = 0;
  int blocks = 0;
  //从当前block往下遍历,注意allocate_handle分配handle时实际使用_last指向的handle分配
  //最开始的current block一直不变,所以此处会遍历current block往后的所有block
  for (JNIHandleBlock* current = this; current != NULL; current = current->_next) {
    //遍历所有的handl
    for (int index = 0; index < current->_top; index++) {
      oop* handle = &(current->_handles)[index];
      //如果该handler已释放
      if (*handle ==  JNIHandles::deleted_handle()) {
        //将这个handle加入当前空闲handle列表的末尾
        *handle = (oop) _free_list;
        _free_list = handle;
        free++;
      }
    }
    //检查top是否等于block_size_in_oops,相等说明都分配完了,所以才需要重建
    assert(current->_top == block_size_in_oops, "just checking");
    blocks++;
  }
  // Heuristic: if more than half of the handles are free we rebuild next time
  // as well, otherwise we append a corresponding number of new blocks before
  // attempting a free list rebuild again.
  //如果rebuild释放了一半以上的handle则继续rebuild,否则会创建一定数量的新的block
  //计算总的handle
  int total = blocks * block_size_in_oops;
  int extra = total - 2*free;
  //extra大于0说明只释放了很少的handle,说明当前handle不足了,需要重新创建
  if (extra > 0) {
    //需要创建的新的block的数量
    _allocate_before_rebuild = (extra + block_size_in_oops - 1) / block_size_in_oops;
  }
  //打印日志
  if (TraceJNIHandleAllocation) {
    tty->print_cr("Rebuild free list JNIHandleBlock " INTPTR_FORMAT " blocks=%d used=%d free=%d add=%d",
      this, blocks, total-free, free, _allocate_before_rebuild);
  }
}

结合上述源码分析可知,allocate_handle时不仅会从当前的block分配,也会从当前block往后的block分配,如果block不足了会不断的追加新的block。另外已经被释放的handles数组元素会在rebuild的过程中建立一个链表_free_list,分配handle时会尝试从_free_list中分配,从而实现handle的重复利用。

结论

JNI中引入本地引用或者全局引用的目的是为了避免本地代码所引用的对象被垃圾回收器回收掉了,即oop突然变成非法指针了,因为本地代码的执行逻辑同Java代码完全不同,不能直接复用Java代码的引用管理。Java代码中有引用的概念,但没有本地引用和全局引用的区分,本地引用相当于局部变量,由方法的局部变量表保存,方法调用结束局部变量引用的对象自动销毁,全局引用相当于静态变量。Java代码中的引用实际就是一系列的Handle类,通过Handle类屏蔽因对象复制或者RedefineClasses导致对象本身变更,上层代码无感知。同时跟JNIHandle一样,所有分配的Handle都被集中到某个属性中,方便GC时以此为根节点遍历所有引用的对象。

相关文章

网友评论

      本文标题:JNI引用管理

      本文链接:https://www.haomeiwen.com/subject/xltpmrtx.html