句柄Handle的释放(8)
本篇首先介绍几个与句柄分配与释放密切相关的类,然后重点介绍句柄的释放。
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的源代码
关注公众号,有HotSpot源码剖析系列文章!
句柄Handle的释放(8)的更多相关文章
- 【linux】lsof命令和{Linux下文件删除、句柄与空间释放问题}
导读: 一.用事实说话 二.关于LSOF命令的其它用法: 三.参考文档: 正文: lsof:Finding open files with lsof 作用:查看文件被哪些进程打开 一.用事实说 ...
- 句柄(Handle)
1.句柄是什么? 在windows中,句柄是和对象一一对应的32位无符号整数值.对象可以映射到唯一的句柄,句柄也可以映射到唯一的对象.2.为什么我们需要句柄? 更准确地说,是window ...
- [转]Windows中的句柄(handle)
1.句柄是什么? 在windows中,句柄是和对象一一对应的32位无符号整数值.对象可以映射到唯一的句柄,句柄也可以映射到唯一的对象.2.为什么我们需要句柄? 更准确地说,是windows需要 ...
- 操作句柄Handle(7)
可以将Handle理解成访问对象的一个“句柄”.垃圾回收时对象可能被移动(对象地址发生改变),通过Handle访问对象可以对使用者屏蔽垃圾回收细节. Handle涉及到的相关类的继承关系如下图所示. ...
- 【windows 操作系统】线程句柄HANDLE与线程ID的关系
什么是句柄 句柄是一种指向指针的指针.我们知道,所谓指针是一种内存地址.应用程序启动后,组成这个程序的各对象是住留在内存的.如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访 ...
- Nodejs事件引擎libuv源码剖析之:句柄(handle)结构的设计剖析
声明:本文为原创博文,转载请注明出处. 句柄(handle)代表一种对持有资源的索引,句柄的叫法在window上较多,在unix/linux等系统上大多称之为描述符,为了抽象不同平台的差异,libuv ...
- 【java+selenium3】多窗口window切换及句柄handle获取(四)
一 .页面准备 1.html <html> <head> <title>主页面 1</title> </head> <body> ...
- [磁盘空间]lsof处理文件恢复、句柄以及空间释放问题
曾经在生产上遇到过一个df 和 du出现的结果不一致的问题,为了排查到底是哪个进程占用了文件句柄,导致空间未释放,首先在linux上面,一切皆文件,这个问题可以使用lsof这个BT的命令来处理(这个哈 ...
- Java主类的装载
在JavaMain()函数中调用LoadMainClass()函数加载Java主类.LoadMainClass()函数的实现如下: /* * Loads a class and verifies th ...
随机推荐
- controlfile的情景恢复
控制文件测试 百度百科:控制文件(Control File)是Oracle的物理文件之一,它记录了数据库的名字.数据文件的位置等信息.控制文件的重要性在于,一旦控制文件损坏,数据库将会宕机.如果没有数 ...
- 一行代码让你的python运行速度提高100倍
转自:https://www.cnblogs.com/xihuineng/p/10630116.html 加上之后运行速度快了十倍,我的天呐. python一直被病垢运行速度太慢,但是实际上pytho ...
- ca12a_c++顺序容器的操作5_访问容器中的数据元素
ca12a_c++顺序容器的操作5_访问容器中的数据元素访问元素:c.back()..显示最后一个数据c.front() ..显示第一个数据c[n],n就是下标,适合vector与dequec.at( ...
- cb06a_c++_顺序容器的定义
/*cb06a_c++_顺序容器的定义顺序容器:vector,数组,尾端操作数据,快速随机访问list 链表,快速插入数据deque数组,双端-首尾操作数据,方便两端的数据访问 顺序容器适配器:sta ...
- python中那些鲜为人知的功能特性
经常逛GitHub的可能关注一个牛叉的项目,叫 What the f*ck Python! 这个项目列出了几乎所有python中那些鲜为人知的功能特性,有些功能第一次遇见时,你会冒出 what the ...
- debug PostgreSQL 9.6.18 using Eclipse IDE on CentOS7
目录 debug PostgreSQL 9.6.18 using Eclipse IDE on CentOS7 1.概览 2.建立用户 3.编译postgre 4.启动Eclipse 5.设置环境变量 ...
- RockeMQ安装与入门
淘宝内部的交易系统使用了淘宝自主研发的Notify消息中间件,使用Mysql作为消息存储媒介,可完全水平扩容,为了进一步降低成本,淘宝开发团队认为存储部分可以进一步优化,2011年初,Linkin开源 ...
- Day11-微信小程序实战-交友小程序-附近的人(地图的形式)及位置获取
回顾:在下面的tabbar中,我们已经实现了首页 消息 我的,就剩下”附近“页面了 ”附近“的页面主要是用地图来进行展示的(可以显示我的位置,也可以显示周围附近的人的位置) (在地图里面点击它的头像的 ...
- 九、深度优先 && 广度优先
原文地址 一.什么是"搜索"算法? 算法是作用于具体数据结构之上的,深度优先搜索算法和广度优先搜索算法都是基于"图"这种数据结构的. 因为图这种数据结构的表达能 ...
- 【原】二进制部署 k8s 1.18.3
二进制部署 k8s 1.18.3 1.相关前置信息 1.1 版本信息 kube_version: v1.18.3 etcd_version: v3.4.9 flannel: v0.12.0 cored ...