可以将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源码剖析系列文章!

 

操作句柄Handle(7)的更多相关文章

  1. 句柄Handle的释放(8)

    本篇首先介绍几个与句柄分配与释放密切相关的类,然后重点介绍句柄的释放. 1.HandleArea.Area与Chunk 句柄都是在HandleArea中分配并存储的,类的定义如下: // Thread ...

  2. C#操作句柄

    1.直接上例子吧:收集系统信息msinfo32时,会有一个弹窗,现在要隐藏该弹窗,首先看没有通过句柄隐藏弹窗的现象 2.收集系统信息导入到一个位置 代码: Process[] msinfo32proc ...

  3. Nodejs事件引擎libuv源码剖析之:句柄(handle)结构的设计剖析

    声明:本文为原创博文,转载请注明出处. 句柄(handle)代表一种对持有资源的索引,句柄的叫法在window上较多,在unix/linux等系统上大多称之为描述符,为了抽象不同平台的差异,libuv ...

  4. 句柄(Handle)

    1.句柄是什么?    在windows中,句柄是和对象一一对应的32位无符号整数值.对象可以映射到唯一的句柄,句柄也可以映射到唯一的对象.2.为什么我们需要句柄?     更准确地说,是window ...

  5. [转]Windows中的句柄(handle)

    1.句柄是什么?   在windows中,句柄是和对象一一对应的32位无符号整数值.对象可以映射到唯一的句柄,句柄也可以映射到唯一的对象.2.为什么我们需要句柄?   更准确地说,是windows需要 ...

  6. 【windows 操作系统】线程句柄HANDLE与线程ID的关系

    什么是句柄 句柄是一种指向指针的指针.我们知道,所谓指针是一种内存地址.应用程序启动后,组成这个程序的各对象是住留在内存的.如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访 ...

  7. 【java+selenium3】多窗口window切换及句柄handle获取(四)

    一 .页面准备 1.html <html> <head> <title>主页面 1</title> </head> <body> ...

  8. VMware View 要求操作句柄的状态错误

    win10系统安装的VMware-viewclient,版本是

  9. dlopen、dlsym和dlclose的使用

    在dlopen()函数以指定模式打开指定的动态链接库文件,并返回一个句柄给dlsym()的调用进程.使用dlclose()来卸载打开的库. dlopen: dlopen() The function ...

随机推荐

  1. APP自动化1——Appium+pycharm自动化环境搭建全流程

    1. 安装python3,pycharm,可参考之前写的文档:https://www.cnblogs.com/chenweitoag/p/13154815.html 2. 准备以下必要工具: 基于wi ...

  2. mybatis-config.xml核心文件配置

    一.全局配置文件结构 configuration 配置 properties 属性:可以加载properties配置文件的信息 settings 设置:可以设置mybatis的全局属性 typeAli ...

  3. 10.DRF-认证

    Django rest framework源码分析(1)----认证 一.基础 1.1.安装 两种方式: github pip直接安装 pip install djangorestframework ...

  4. PHPWord中文乱码、单元格合并、动态表格模板解决方案合集

    摘要:  最近一个项目开发要用到PHP技术导出Word文档,采用PHPWord插件,版本为0.6.2 beta,CodePlex已停止维护.网上还有另外一个版本的PhpWord,项目类名大小写上略有不 ...

  5. Mysql和Redis数据同步策略

    为什么对缓存只删除不更新 不更新缓存是防止并发更新导致的数据不一致. 所以为了降低数据不一致的概率,不应该更新缓存,而是直接将其删除, 然后等待下次发生cache miss时再把数据库中的数据同步到缓 ...

  6. 【解读】TCP粘包拆包

    一.TCP粘包.拆包图解 假设客户端分别发送了两个数据包D1和D2给服务端,由于服务端一次读取到字节数是不确定的,故可能存在以下四种情况: 1)服务端分两次读取到了两个独立的数据包,分别是D1和D2, ...

  7. Vmware虚拟机克隆以及关闭防火墙

    vmware虚拟机克隆之后,一定要修改克隆机器的mac地址和IP上网地址,不能和之前的机器一样

  8. STM32内存受限情况下摄像头驱动方式与图像裁剪的选择

    1.STM32图像接收接口 使用stm32芯片,128kB RAM,512kB Rom,资源有限,接摄像头采集图像,这种情况下,内存利用制约程序设计. STM32使用DCMI接口读取摄像头,协议如下. ...

  9. JavaScript基础Literal 与 Constructor(008)

    JavaScript支持以字面声名法(Literal)的方式来声名对象和数组,相对于构造函数(constructor)的方式,Literal的声 名方式更简洁,更易读,也更少导致Bug.事实上,JSO ...

  10. HTTPS协议详解(三):PKI 体系

    转自:https://blog.csdn.net/hherima/article/details/52469488 1.RSA身份验证的隐患    身份验证和密钥协商是TLS的基础功能,要求的前提是合 ...