可以将Handle理解成访问对象的一个“句柄”。垃圾回收时对象可能被移动(对象地址发生改变),通过Handle访问对象可以对使用者屏蔽垃圾回收细节。

Handle涉及到的相关类的继承关系如下图所示。

HotSpot会通过Handle对Oop和某些Klass进行操作。下图左边显示了直接访问的情况,下图右边显示了间接访问的情况。

可以看到,当对Oop直接引用时,如果Oop的地址发生变化,那么所有的引用都要更新,如图有3处引用,所以都需要更新;当通过Handle对Oop间接引用时,如果Oop的地址发生变化,那么只需要更新Handle中保存的对Oop的引用即可。

每个Oop都有一个对应的Handle,这样通过对应的Handle可直接获取对应的Oop,不需要进行类型转换。为了读者方便阅读,这里再次给出了Oop继承体系,如下图所示。

可以看到Handle继承体系与Oop继承体系类似,实际上也有相应的对应关系,例如通过instanceHandle操作instanceOopDesc,通过objArrayHandle操作objArrayOopDesc。

与Oop类似,Klass也需要通过Handle来间接引用。如下几个Klass有对应的Handle:

Klass -klassHandle
    InstanceKlass - instanceKlassHandle
ConstantPool - constantPoolHandle
Method - methodHandle

现在假设有个Person类,还有这个类的一个Person对象,那么可以像下图这样理解Handle、Oop与Klass之间的关系:

下面具体看一下Handle的定义,如下:

// Base class for all handles. Provides overloading of frequently
// used operators for ease of use. class Handle VALUE_OBJ_CLASS_SPEC {
private:
oop* _handle; // 可以看到是对oop的封装 protected:
oop obj() const {
return _handle == NULL ? (oop)NULL : *_handle;
}
oop non_null_obj() const {
assert(_handle != NULL, "resolving NULL handle");
return *_handle;
}
...
}

调用obj()或non_null_obj()方法获取被封装的oop对象,不过并不会直接调用Handle对象的obj()或non_null_obj()对象,而是通过C++的运算符重载来获取。Handle类重载了()和->运算符,如下:

// General access
oop operator () () const {
return obj();
}
oop operator -> () const {
return non_null_obj();
}

可以这样使用:

oop obj = ...;
Handle h1(obj); // allocate new handle oop obj1 = h1(); // get handle value
h1->print(); // invoking operation on oop  

由于重载了运算符(),所以h1()会调用()运算符的重载方法,重载方法中调用obj()获取到被封装的oop对象。重载了运算符->,所以h1->print()同样会调用oop对象的print()方法。

另外还需要知道,Handle分配在本地线程的HandleArea中,这样在进行垃圾回收时,只需要扫描每个线程的HandleArea即可找出句柄引用的活跃对象。每次创建句柄对象时,都会调用到Handle类的构造函数,其中一个构造函数如下:

inline Handle::Handle(oop obj) {
if (obj == NULL) {
_handle = NULL;
} else {
HandleArea* ha = Thread::current()->handle_area();
_handle = ha->allocate_handle(obj);
}
}

参数obj就是要通过句柄操作的对象。通过调用当前线程的handle_area()函数获取HandleArea,然后调用allocate_handle()在HandleArea中分配存储obj的空间并将obj保存起来。

每个线程都 会有一个_handle_area属性,定义如下:

// Thread local handle area for allocation of handles within the VM
HandleArea* _handle_area;

在创建线程时初始化_handle_area属性,然后通过handle_area()函数获取这个属性的值。 

allocate_handle()函数的实现如下:

oop* real_allocate_handle(oop obj) {
oop* handle = (oop*) Amalloc_4(oopSize);
*handle = obj;
return handle;
}

分配空间并完成obj的存储操作。 

句柄的释放要通过HandleMark来完成,不过在介绍HandleMark之前需要介绍一下FHandleArea、Area及Chunk等类的实现,下一篇会详细分析。

相关文章的链接如下:

1、在Ubuntu 16.04上编译OpenJDK8的源代码

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot的启动过程

5、HotSpot二分模型(1)

6、HotSpot的类模型(2)

7、HotSpot的类模型(3)

8、HotSpot的类模型(4)

9、HotSpot的对象模型(5)

10、HotSpot的对象模型(6)

关注公众号,有HotSpot源码剖析系列文章!

 

最新文章

  1. -[NSNull countByEnumeratingWithState:objects:count:]:
  2. [JavaEE]Java NIO原理图文分析及代码实现
  3. IOS-UIIamge初始化的几种方法的比较
  4. 【jacob word】使用jacob,合并多个word为一个word文件
  5. jmeter随笔(5)--断言中正则表达式的特殊字符问题和中文乱码显示问号的问题
  6. Java:集合框架的工具类
  7. 第二章 Spring MVC入门
  8. MyEclipse下查看Java API帮助文档
  9. 多队列网卡简介以及Linux通过网卡发送数据包源码解读
  10. 虚拟机环境中安装ubuntu下的mysql-cluster7.3.2(单点服务器)
  11. 十大最值得注意的MySQL变量
  12. JAVA的一些小笔记
  13. CentOS7安装完毕,重新开机启动后显示: Initial setup of CentOS Linux 7 (core)
  14. MySQL创建数据库与创建用户以及授权
  15. struts2框架学习之第一天
  16. OpenStack Mitaka/Newton/Ocata/Pike 各版本功能贴整理
  17. Elasticsearch 数据搜索篇·【入门级干货】===转
  18. JVM实践
  19. 发现Boost官方文档的一处错误(numpy的ndarray)
  20. vector erase的错误用法

热门文章

  1. Nginx 如何自定义变量?
  2. 安装mysql报错:遇到缺少vcruntime140_1.dll文件
  3. hystrix信号量和线程池的区别
  4. DataFrame索引和切片
  5. 01[了解] Dubbo
  6. Cannot resolve symbol window
  7. MySQL高级用法
  8. 恕我直言你可能真的不会java第7篇:像使用SQL一样排序集合
  9. Windows Defender might be impacting your build performance
  10. 逻辑式编程语言极简实现(使用C#) - 2. 一道逻辑题:谁是凶手