Netty源码—七、内存释放
Netty本身在内存分配上支持堆内存和直接内存,我们一般选用直接内存,这也是默认的配置。所以要理解Netty内存的释放我们得先看下直接内存的释放。
Java直接内存释放
我们先来看下直接内存是怎么使用的
ByteBuffer.allocateDirect(capacity)
申请的过程是其实就是创建一个DirectByteBuffer对象的过程,DirectByteBuffer对象只相当于一个holder,包含一个address,这个是直接内存的指针。
- 调用native方法申请内存
- 初始化cleaner
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
DirectByteBuffer(int cap) { // package-private
// 省略中间代码...
// 创建一个cleaner,最后会调用Deallocator.run来释放内存
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
att = null;
}
Cleaner这个类继承自PhantomReference,也就是所谓的虚引用,这种类型引用的特点是:
- 使用get方法不能获取到对象
- 只要引用的对象除了PhantomReference之外没有其他引用了,JVM随时可以将PhantomReference引用的对象回收。
JVM在回前会将将要被回收的对象放在一个队列中,由于Cleaner继承自PhantomReference,队列的实现是使用cleaner的
private static final ReferenceQueue<Object> dummyQueue = new ReferenceQueue<>();
这个队列在PhantomReference的父类Reference中使用到了,Reference这个类在初始化的时候会启动一个线程来调用cleaner.clean方法,在Reference的静态代码块中启动线程
// java.lang.ref.Reference
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
*/
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
// 启动ReferenceHandler线程
handler.start();
// 省略中间代码...
}
该线程的主要作用就是调用tryHandlePending
// java.lang.ref.Reference#tryHandlePending
static boolean tryHandlePending(boolean waitForNotify) {
Reference<Object> r;
Cleaner c;
try {
synchronized (lock) {
if (pending != null) {
r = pending;
// 'instanceof' might throw OutOfMemoryError sometimes
// so do this before un-linking 'r' from the 'pending' chain...
c = r instanceof Cleaner ? (Cleaner) r : null;
// unlink 'r' from 'pending' chain
pending = r.discovered;
r.discovered = null;
} else {
// The waiting on the lock may cause an OutOfMemoryError
// because it may try to allocate exception objects.
if (waitForNotify) {
lock.wait();
}
// retry if waited
return waitForNotify;
}
}
} catch (OutOfMemoryError x) {
// Give other threads CPU time so they hopefully drop some live references
// and GC reclaims some space.
// Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above
// persistently throws OOME for some time...
Thread.yield();
// retry
return true;
} catch (InterruptedException x) {
// retry
return true;
}
// Fast path for cleaners
if (c != null) {
// 调用clean方法
c.clean();
return true;
}
ReferenceQueue<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
return true;
}
System.gc不能回收堆外内存,但是会回收已经没有使用了DirectByteBuffer对象,该对象被回收的时候会将cleaner对象放入队列中,在Reference的线程中调用clean方法来回收堆外内存 。cleaner.run执行的是传入参数的thunk.run方法,这里thunk是Deallocator,所以最后执行的Deallocator.run方法
public void run() {
if (address == 0) {
// Paranoia
return;
}
// 释放内存
unsafe.freeMemory(address);
address = 0;
Bits.unreserveMemory(size, capacity);
}
所以最后通过unsafe.freeMemory释放了申请到的内存。
总结一下,在申请内存的时候调用的是java.nio.ByteBuffer#allocateDirect
会new DirectByteBuffer,通过Cleaner.create创建Cleaner,同时传入Deallocator作为Runnable参数,在Cleaner.clean的时候会调用该Deallocator.run来处理
Cleaner继承自PhantomReference,包含一个ReferenceQueue,在DirectByteBuffer不再使用的时候,该对象是处于Java堆的,除了该PhantomReference引用了DirectByteBuffer外,没有其他引用的时候,jvm会把cleaner对象放入ReferenceQueue队列中。
PhantomReference继承了Reference,Reference会启动一个线程(java.lang.ref.Reference.ReferenceHandler#run)去调用队列中的cleaner.clean方法。
Netty内存释放
Netty使用的直接内存的释放方式和JDK的释放方式略有不同。Netty开始释放内存的时候是调用free方法的时候
io.netty.buffer.PoolArena#free
io.netty.buffer.PoolArena.DirectArena#destroyChunk
最终释放内存的方法有两种
- 利用反射获取unsafe,调用Unsafe#freeMemory
- 利用反射获取DirectByteBuffer#cleaner,通过反射调用cleaner.clean方法
两种不同的方式依赖的条件不同,使用场景也不同
使用反射调用cleaner.clean
要满足以下条件之一的时候使用这种方式
- 没有可使用的直接内存
- 不能获取unsafe
- directBuffer没有传入long、int的构造方法
使用unsafe
不能使用上面这种方式的都使用unsafe
Netty源码—七、内存释放的更多相关文章
- Netty源码分析--内存模型(上)(十一)
前两节我们分别看了FastThreadLocal和ThreadLocal的源码分析,并且在第八节的时候讲到了处理一个客户端的接入请求,一个客户端是接入进来的,是怎么注册到多路复用器上的.那么这一节我们 ...
- Netty源码分析--内存模型(下)(十二)
这一节我们一起看下分配过程 PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacit ...
- Netty源码解析 -- 内存池与PoolArena
我们知道,Netty使用直接内存实现Netty零拷贝以提升性能, 但直接内存的创建和释放可能需要涉及系统调用,是比较昂贵的操作,如果每个请求都创建和释放一个直接内存,那性能肯定是不能满足要求的. 这时 ...
- Netty源码解析 -- 内存对齐类SizeClasses
在学习Netty内存池之前,我们先了解一下Netty的内存对齐类SizeClasses,它为Netty内存池中的内存块提供大小对齐,索引计算等服务方法. 源码分析基于Netty 4.1.52 Nett ...
- Netty源码分析第5章(ByteBuf)---->第8节: subPage级别的内存分配
Netty源码分析第五章: ByteBuf 第八节: subPage级别的内存分配 上一小节我们剖析了page级别的内存分配逻辑, 这一小节带大家剖析有关subPage级别的内存分配 通过之前的学习我 ...
- Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)---->第7节: 获取异线程释放的对象
Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler 第七节: 获取异线程释放的对象 上一小节分析了异线程回收对象, 原理是通过与stack关联的WeakOrder ...
- Netty源码分析 (七)----- read过程 源码分析
在上一篇文章中,我们分析了processSelectedKey这个方法中的accept过程,本文将分析一下work线程中的read过程. private static void processSele ...
- netty源码解析(4.0)-27 ByteBuf内存池:PoolArena-PoolThreadCache
前面两章分析的PoolChunk和PoolSubpage,从功能上来说已经可以直接拿来用了.但直接使用这个两个类管理内存在高频分配/释放内存场景下会有性能问题,PoolChunk分配内存时算法复杂度最 ...
- Netty源码分析第5章(ByteBuf)---->第7节: page级别的内存分配
Netty源码分析第五章: ByteBuf 第六节: page级别的内存分配 前面小节我们剖析过命中缓存的内存分配逻辑, 前提是如果缓存中有数据, 那么缓存中没有数据, netty是如何开辟一块内存进 ...
随机推荐
- Postgresql中临时表(temporary table)的特性和用法
熟悉Oracle的人,相比对临时表(temporary table)并不陌生,很多场景对解决问题起到不错的作用,开源库Postgresql中,也有临时表的概念,虽然和Oracle中临时表名字相同,使用 ...
- NGINX按天生成日志文件的简易配置
NGINX按天生成日志文件的简易配置 0x01 最近后端童鞋遇到一个小需求,拆分nginx生成的log文件,最好是按天生成,看着她还有很多bug待改的状态,我说这个简单啊,我来吧.曾经搞node后端的 ...
- asp.net图片上传及删除
方法一:UploadImage cetFU = new UploadImage(); if (cetFU.UpFile(FileUpload1, "teacher_picture" ...
- C语言下double转char*或者std::string,可以精确转换不含多余的0
char* GetDoubleStr(double value) { char buf[32]={0};//长度可以自定义 sprintf(buf,"%.8f",value);// ...
- 数据库连接池dbcp和c3po的区别
1 DBCP DBCP是 apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件. 2.C3P0 是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate ...
- Spring Boot 1.4测试的简单理解
首先maven要引入spring-boot-starter-test这个包. 先看一段代码 @RunWith(SpringRunner.class) @SpringBootTest(webEnviro ...
- Python Selenium 之生成Beautiful可视化报告
提到自动化测试,少不了自动化生成测试报告,更少不了漂亮的测试报告呀!刚好看到在github上有个大神分享了BeautifulReport,与unittest测试框架完美的结合起来,就能生成Beauti ...
- c# xml操作(二)
c# xml操作(二) 此博文包含图片 (-- ::)转载▼ 标签: 杂谈 分类: c# 上次,我们介绍了增加和删除xml的一些操作,这次我们将介绍如何更改和读取xml特定节点.我们依然以上次的xml ...
- Android 手势检测实战 打造支持缩放平移的图片预览效果(下)
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39480503,本文出自:[张鸿洋的博客] 上一篇已经带大家实现了自由的放大缩小图 ...
- anguments
anguments是一个对象,长得很像数组的对象,但不是数组,而是伪数组. arguments的内容是函数运行时的实参列表 (function(d, e, f) { console.log(argum ...