本篇首先介绍几个与句柄分配与释放密切相关的类,然后重点介绍句柄的释放。

1、HandleArea、Area与Chunk

句柄都是在HandleArea中分配并存储的,类的定义如下:

// Thread local handle area
class HandleArea: public Arena {
friend class HandleMark;
...
HandleArea* _prev; // HandleArea通过_prev连接成单链表
public:
// Constructor
HandleArea(HandleArea* prev) : Arena(Chunk::tiny_size) {
_prev = prev;
} // Handle allocation
private:
oop* real_allocate_handle(oop obj) { // 分配内存并存储obj对象
oop* handle = (oop*) Amalloc_4(oopSize);
*handle = obj;
return handle;
} // ...
};

real_allocate_handle()用来在HandleArea中分配内存并存储obj对象,方法会调用父类Arena中定义的Amalloc_4()函数。HandleArea的父类Arena的定义如下:

// Fast allocation of memory
class Arena: public CHeapObj {
protected:
...
Chunk *_first; // First chunk
Chunk *_chunk; // current chunk
char *_hwm, *_max; // High water mark and max in current chunk
void* grow(size_t x); // Get a new Chunk of at least size x
size_t _size_in_bytes; // Size of arena (used for memory usage tracing)
public:
Arena();
Arena(size_t init_size);
Arena(Arena *old);
~Arena() { _first->chop(); }
char* hwm() const { return _hwm; } // Fast allocate in the arena. Common case is: pointer test + increment.
// Further assume size is padded out to words
// Warning: in LP64, Amalloc_4 is really Amalloc_8
void *Amalloc_4(size_t x) {
// 保证在64位上,x是一个字的整倍数
assert( (x&(sizeof(char*)-1)) == 0, "misaligned size" );
if (_hwm + x > _max) {
return grow(x);
} else {
char *old = _hwm;
_hwm += x;
return old;
}
} ...
};  

Amalloc_4()函数会在当前的Chunk块中分配内存,如果当前块的内存不够,则会调用grow()方法分配新的Chunk块,然后在新的Chunk块中分配内存。

这个类通过_first、_chunk等管理着一个连接成单链表的Chunk,其中 _first指向单链表的第一个Chunk,而_chunk指向的是当前可提供内存分配的Chunk,通常为单链表的最后一个块Chunk。_hwm与_max指示当前可分配内存的Chunk的一些分配信息。

Chunk类的定义如下:

// Linked list of raw memory chunks
class Chunk: public CHeapObj {
public:
...
Chunk* _next; // Next Chunk in list
size_t _len; // Size of this Chunk // Boundaries of data area (possibly unused)
char* bottom() const { return ((char*) this) + sizeof(Chunk); }
char* top() const { return bottom() + _len; }
};

HandleArea与Chunk类之间的关系如下图所示。

2、HandleMark

每一个Java线程都有一个私有的句柄区_handle_area来存储其运行过程中句柄信息,这个句柄区是随着Java线程的栈帧变化的。Java线程每调用一个Java方法就会创建一个对应HandleMark来保存已经的对象句柄,然后等调用返回后恢复。

HandleMark主要用于记录当前线程的HandleArea的内存地址top,当相关的作用域执行完成后,当前作用域之内的HandleMark实例自动销毁,在HandleMark的析构函数中会将HandleArea的当前内存地址到方法调用前的内存地址top之间的所有分配的地址中存储的内容都销毁掉,然后恢复当前线程的HandleArea的内存地址top到方法调用前的状态。

C++的析构函数专门用来释放内存,这绝对是一个需要好好学习的知识点。

HandleMark一般情况下直接在线程栈内存上分配,应该继承自StackObj,但是部分情况下HandleMark也需要在堆内存上分配,所以没有继承自StackObj,并且为了支持在堆内存上分配,重载了new和delete方法。

类的定义如下:

class HandleMark {
private:
Thread *_thread; // thread that owns this mark
HandleArea *_area; // saved handle area
Chunk *_chunk; // saved arena chunk,Chunk和Area配合,获得准确的内存地址
char *_hwm, *_max; // saved arena info
size_t _size_in_bytes; // size of handle area
// Link to previous active HandleMark in thread
HandleMark* _previous_handle_mark; void initialize(Thread* thread); // common code for constructors
void set_previous_handle_mark(HandleMark* mark) { _previous_handle_mark = mark; }
HandleMark* previous_handle_mark() const { return _previous_handle_mark; } size_t size_in_bytes() const { return _size_in_bytes; }
public:
HandleMark(); // see handles_inline.hpp
HandleMark(Thread* thread) {
initialize(thread);
}
~HandleMark(); ...
};

handleMark也会通过_previous_handle_mark属性形成一条单链表。 

在HandleMark的构造方法中会调用initialize()方法,方法的实现如下:

void HandleMark::initialize(Thread* thread) {
_thread = thread;
// Save area
_area = thread->handle_area();
// Save current top
_chunk = _area->_chunk;
_hwm = _area->_hwm;
_max = _area->_max;
_size_in_bytes = _area->_size_in_bytes; // Link this in the thread
// 将当前HandleMark实例同线程关联起来
HandleMark* hm = thread->last_handle_mark();
set_previous_handle_mark(hm);
thread->set_last_handle_mark(this); // 注意,线程中的_last_handle_mark属性来保存HandleMark对象
}

方法主要初始化一些属性。Thread中定义的_last_handle_mark属性的定义如下:

// Point to the last handle mark
HandleMark* _last_handle_mark;

handleMark的析构函数如下:

HandleMark::~HandleMark() {
HandleArea* area = _area; // help compilers with poor alias analysis // Delete later chunks
if( _chunk->next() ) {
// reset arena size before delete chunks. Otherwise, the total
// arena size could exceed total chunk size
assert(area->size_in_bytes() > size_in_bytes(), "Sanity check");
area->set_size_in_bytes(size_in_bytes());
// 删除当前Chunk以后的所有Chunk,即在方法调用期间新创建的Chunk
_chunk->next_chop();
} else {
// 如果没有下一个Chunk,说明未分配新的Chunk,则area的大小应该保持不变
assert(area->size_in_bytes() == size_in_bytes(), "Sanity check");
}
// Roll back arena to saved top markers
// 恢复area的属性到HandleMark构造时的状态
area->_chunk = _chunk;
area->_hwm = _hwm;
area->_max = _max; // Unlink this from the thread
// 解除当前HandleMark跟线程的关联
_thread->set_last_handle_mark(previous_handle_mark());
}

创建一个新的HandleMark以后,新的HandleMark保存当前线程的area的当前chunk,_hwm ,_max等属性,代码执行期间新创建的Handle实例是在当前线程的area中分配内存,这会导致当前线程的area的当前chunk,_hwm ,_max等属性发生变更,因此代码执行完成后需要将这些属性恢复成之前的状态,并把代码执行过程中新创建的Handle实例的内存给释放掉。 

相关文章的链接如下:

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)

11、操作句柄Handle(7)

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

 

  

句柄Handle的释放(8)的更多相关文章

  1. 【linux】lsof命令和{Linux下文件删除、句柄与空间释放问题}

      导读: 一.用事实说话 二.关于LSOF命令的其它用法: 三.参考文档:   正文: lsof:Finding open files with lsof 作用:查看文件被哪些进程打开 一.用事实说 ...

  2. 句柄(Handle)

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

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

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

  4. 操作句柄Handle(7)

    可以将Handle理解成访问对象的一个“句柄”.垃圾回收时对象可能被移动(对象地址发生改变),通过Handle访问对象可以对使用者屏蔽垃圾回收细节. Handle涉及到的相关类的继承关系如下图所示. ...

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

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

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

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

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

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

  8. [磁盘空间]lsof处理文件恢复、句柄以及空间释放问题

    曾经在生产上遇到过一个df 和 du出现的结果不一致的问题,为了排查到底是哪个进程占用了文件句柄,导致空间未释放,首先在linux上面,一切皆文件,这个问题可以使用lsof这个BT的命令来处理(这个哈 ...

  9. Java主类的装载

    在JavaMain()函数中调用LoadMainClass()函数加载Java主类.LoadMainClass()函数的实现如下: /* * Loads a class and verifies th ...

随机推荐

  1. Linux MySQL集群搭建之主从复制

    前期准备 准备两台Linux,一主,一从,具体Linux安装MySQL操作步骤:点我直达 集群搭建 注意事项 一主可以多从 一从只能一主 关闭主从机器的防火墙策略 chkconfig iptables ...

  2. 移动 Ubuntu16.04 桌面左侧的启动器到屏幕底部_设置方法

    通过命令行,对 Launcher 的位置进行一下调整. 按下 Ctrl + Alt + t 键盘组合键,调出终端,在终端中输入以下命令: gsettings set com.canonical.Uni ...

  3. cc31a_demo--CppPrimer_静态成员与继承-在派生类中访问基类中的static成员的方法

    //*基类中的static成员,在整个继承层次中只有一个实例 //*在派生类中访问基类中的static成员的方法 //1.基类名::成员名 //2.子类名::成员名 //3.对象.成员名 //4.指针 ...

  4. 研为电子6轴运动控制卡win10驱动无法安装问题,解决方法

    研为电子6轴运动控制卡win10驱动无法安装问题,解决方法 研为电子6轴运动控制卡win10驱动无法安装问题,解决方法 iMC3xx2E系列运动控制卡使用手册V1.003 IMCdrv_Ins.exe ...

  5. frp多层socks代理+端口映射

    一.首先在公网上配置服务端(frps.ini) [common] bind_addr = xx.xx.xx.xx #公网vps的ip bind_port = 7000   二.配置客户端frpc. i ...

  6. TXT文件的写入及读出

    一.文件的读出: file = open('url/data.txt','r',encoding='utf-8')#打开模式r w a,当文件在当前工作区域直接写文件名:如果不在当前工作区域要写绝对地 ...

  7. react 使用的方法:

    react  使用方法: 第一步: 初始化react 项目 (1)安装node npm (2)npm install --global create-react-app (3)create-react ...

  8. Downloadmanager实现app实现的升级下载使用

    1.app升级下载现在不推荐使用downloadmanager下载: 原因有下面的几个方面: (1)三星note系列部分手机需要手动打开这个权限才能用这个功能,而有些国产手机更加nb了直接个阉割了(d ...

  9. Auto-keras API详解

    在网上找到的Auto-keras API详解,非常全面,防止丢失记录在这! Auto-Keras API详解(1)——安装Auto-Keras https://blog.csdn.net/weixin ...

  10. webpack4.*入门笔记

    全是跟着示例做的.看下面文章 入门 1.nodejs基础 http://www.runoob.com/nodejs/nodejs-tutorial.html 2.NPM 学习笔记整理 https:// ...