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

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. Spring Cloud 系列之 Alibaba Nacos 配置中心

    Nacos 介绍 Nacos 是 Alibaba 公司推出的开源工具,用于实现分布式系统的服务发现与配置管理.英文全称 Dynamic Naming and Configuration Service ...

  2. 【JMeter_11】JMeter逻辑控制器__Switch控制器<Switch Controller>

    Switch控制器<Switch Controller> 业务逻辑: 取得switch value的值,通过对节点下所有取样器.逻辑控制器的下标.名称匹配去执行,switch value的 ...

  3. beego register db `default`, sql: unknown driver "mysql" (forgotten import?)

    首先先去你的目录下找找这个文件里有没有东西,或者有没有这个文件 如果没有 执行下面两个命令: 下载:go get github.com/Go-SQL-Driver/MySQL 安装:go instal ...

  4. c++11新特性注意点

    本文记录下一些c++11新特性需要注意的方面,供日后参考 一.auto auto可以当成“占位符”,根据右边的类型自动推导出变量的类型.需要注意的是 auto不能解决溢出的问题. auto可以与指针和 ...

  5. SpringMVC拦截器使用

    源码地址 拦截器interceptor 拦截器是URL请求的第一道门,所有请求会先经过拦截器interceptor,然后再进入controller: 下面,记录一种通过注解方法拦截所有需要登录才能发起 ...

  6. 傻瓜式教学--win10 + frp + rdpwrap + 阿里云服务器 --实现win10 多用户同时远程登录内网机

    概述: 使用win10 专业版 + frp + RDPwrap + 阿里云服务器 的组合实现win10 多用户同时远程登录内网机.使用frp 做内网穿透,将内网机的指定端口暴露在外网,通过ip+por ...

  7. 【解读】TCP协议

    本文内容如下:      1)TCP协议概念      2)TCP头部结构和字段介绍      3)TCP流量控制            滑动窗口      4)TCP拥塞控制           慢 ...

  8. yqq命令

    用 apt-get install 安装时 ,会有一个提问,问是否继续,需要输入 yes.使用 yqq 的话,就没有这个提问了,自动 yes . 例如下面的更新和安装nginx: apt-get -y ...

  9. Executors框架之ScheduledExecutorService实现定时任务

    一.简介 An ExecutorService that can schedule commands to run after a given delay, or to execute periodi ...

  10. 【Spring】内嵌Tomcat&去Xml&调试Mvc

    菜瓜:今天听到个名词“父子容器”,百度了一下,感觉概念有点空洞,这是什么核武器? 水稻:你说的是SpringMvc和Spring吧,其实只是一个概念而已,用来将两个容器做隔离,起到解耦的作用,其中子容 ...