Netty之内存泄露
直接内存是IO框架的绝配,但直接内存的分配销毁不易,所以使用内存池能大幅提高性能。
1.为什么要有引用计数器
Netty里四种主力的ByteBuf,其中UnpooledHeapByteBuf底下的byte[]能够依赖JVM GC自然回收;而UnpooledDirectByteBuf底下是DirectByteBuffer,如Java堆外内存扫盲贴所述,除了等JVM GC,最好也能主动进行回收;而PooledHeapByteBuf和PooledDirectByteBuf,则必须要主动将用完的byte[]/ByteBuffer放回池里,否则内存就要爆掉。所以,Netty ByteBuf需要在JVM的GC机制之外,有自己的引用计数器和回收过程。
一下又回到了C的冰冷时代,自己malloc对象要自己free。但和C时代又不完全一样,内有引用计数器,外有JVM的GC,情况更为复杂。
2.引用计数器常识
计数器基于AtomicIntegerFieldUpdater,为什么不直接用AtomicInteger?因为ByteBuf对象很多,如果都把int包一层AtomicInteger花销较大,而AtomicIntegerFieldUpdater只需要一个全局的静态变量。
所有ByteBuf的引用计数器初始值为1。
调用release(),将计数器减1,等于零时,deallocate()被调用,各种回收。
调用retain(),将计数器加1,即使ByteBuf在别的地方被人release()了,在本Class没喊cut之前,不要把它释放掉。
由duplicate(), slice()和order(ByteOrder)所创建的ByteBuf,与原对象共享底下的buffer,也共享引用计数器,所以它们经常需要调用retain()来显示自己的存在。
当引用计数器为0,底下的buffer已被回收,即使ByteBuf对象还在,对它的各种访问操作都会抛出异常。
3.谁来负责Release
在C时代,我们喜欢让malloc和free成对出现,而在Netty里,因为Handler链的存在,ByteBuf经常要传递到下一个Hanlder去而不复还,所以规则变成了谁是最后使用者,谁负责释放。
另外,更要注意的是各种异常情况,ByteBuf没有成功传递到下一个Hanlder,还在自己地界里的话,一定要进行释放。
多层的异常处理机制,有些异常处理的地方不一定准确知道ByteBuf之前释放了没有,可以在释放前加上引用计数大于0的判断避免异常;
有时候不清楚ByteBuf被引用了多少次,但又必须在此进行彻底的释放,可以循环调用reelase()直到返回true。
4.内存泄漏检测
所谓内存泄漏,主要是针对池化的ByteBuf。ByteBuf对象被JVM GC掉之前,没有调用release()去把底下的DirectByteBuffer或byte[]归还到池里,会导致池越来越大。而非池化的ByteBuf,即使像DirectByteBuf那样可能会用到System.gc(),但终归会被release掉的,不会出大事。
Netty担心大家一定会不小心就搞出个大新闻来,因此提供了内存泄漏的监测机制。
Netty默认就会从分配的ByteBuf里抽样出大约1%的来进行跟踪。如果泄漏,会有如下语句打印:
引用
LEAK: ByteBuf.release() was not called
before it's garbage-collected. Enable advanced leak reporting to find out where
the leak occurred. To enable advanced leak reporting, specify the JVM option '-Dio.netty.leakDetectionLevel=advanced'
or call ResourceLeakDetector.setLevel()
这句话报告有泄漏的发生,提示你用-D参数,把防漏等级从默认的simple升到advanced,具体看到被泄漏的ByteBuf创建的地方和被访问的地方。
禁用(DISABLED)-完全禁止泄露检测,省点消耗。
简单(SIMPLE)-默认等级,告诉我们取样的1%的ByteBuf是否发生了泄露,但总共一次只打印一次,看不到就没有了。
高级(ADVANCED)-告诉我们取样的1%的ByteBuf发生泄露的地方。每种类型的泄漏(创建的地方与访问路径一致)只打印一次。
偏执(PARANOID)-跟高级选项类似,但此选项检测所有ByteBuf,而不仅仅是取样的那1%。在高压力测试时,对性能有明显影响。
实现细节
每当各种ByteBufAllocator创建ByteBuf时,都会问问是否需要采样,Simple和Advanced级别下,就是以113这个素数来取模,命中了就创建一个Java堆外内存扫盲贴里说的PhantomReference。然后创建一个Wrapper,包住ByteBuf和Reference。
Simple级别下,wrapper只在执行release()时调用Reference.clear()把Reference清理掉,Advanced级别下则会记录每一个创建和访问的动作。
当GC发生,还没有被clear()的Reference就会被JVM放入到之前设定的ReferenceQueue里。
在每次创建PhantomReference时,都会顺便看看有没有因为忘记执行release()把Reference给clear掉,在GC时被放进了ReferenceQueue的对象,有则以"io.netty.util.ResourceLeakDetector”为logger name,写出前面例子里的Error级别的日日志。顺便说一句,Netty能自动匹配日志框架,先找Slf4j,再找Log4j,最后找JDK logger。
问题排查
一定要盯紧log里有没有出现"LEAK: "字样,因为Simple级别下它只会出现一次,所以不要依赖自己的眼睛,要依赖grep。如果出现了,而且你用的是PooledBuf,那一定是问题,不要有任何的侥幸,立刻用"-Dio.netty.leakDetectionLevel=advanced"再跑一次,看清楚它创建和最后访问的地方。
功能测试时,最好开着"-Dio.netty.leakDetectionLevel=paranoid"
但是,怎么测试都可能有没覆盖到的分支,如果内存尚够,可以适当把-XX:MaxDirectMemorySize调大,反正只是max,平时也不会真用了你的。然后监控其使用量,及时报警。
Netty之内存泄露的更多相关文章
- Netty堆外内存泄露排查与总结
导读 Netty 是一个异步事件驱动的网络通信层框架,用于快速开发高可用高性能的服务端网络框架与客户端程序,它极大地简化了 TCP 和 UDP 套接字服务器等网络编程. Netty 底层基于 JDK ...
- Netty如何监控内存泄露
目录 Netty如何监控内存泄露 前言 JDK的弱引用和引用队列 Netty的实现思路 代码实现 分配监控对象 追踪和检查泄露 Netty如何监控内存泄露 前言 一般而言,在Netty程序中都会采用池 ...
- 从一次netty 内存泄露问题来看netty对POST请求的解析
背景 最近生产环境一个基于 netty 的网关服务频繁 full gc 观察内存占用,并把时间维度拉的比较长,可以看到可用内存有明显的下降趋势 出现这种情况,按往常的经验,多半是内存泄露了 问题定位 ...
- 抓到 Netty 一个隐藏很深的内存泄露 Bug | 详解 Recycler 对象池的精妙设计与实现
欢迎关注公众号:bin的技术小屋,如果大家在看文章的时候发现图片加载不了,可以到公众号查看原文 本系列Netty源码解析文章基于 4.1.56.Final版本 最近在 Review Netty 代码的 ...
- 《从HBase offheap到Netty的内存管理》
JVM中的堆外内存(off-heap memory)与堆内内存(on-heap memory) 1. 堆内内存(on-heap memory) 1.1 什么是堆内内存 Java 虚拟机在执行Jav ...
- java: web应用中不经意的内存泄露
前面有一篇讲解如何在spring mvc web应用中一启动就执行某些逻辑,今天无意发现如果使用不当,很容易引起内存泄露,测试代码如下: 1.定义一个类App package com.cnblogs. ...
- 查看w3wp进程占用的内存及.NET内存泄露,死锁分析
一 基础知识 在分析之前,先上一张图: 从上面可以看到,这个w3wp进程占用了376M内存,启动了54个线程. 在使用windbg查看之前,看到的进程含有 *32 字样,意思是在64位机器上已32位方 ...
- C++11 shared_ptr 智能指针 的使用,避免内存泄露
多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...
- 基于HTML5的WebGL应用内存泄露分析
上篇(http://www.hightopo.com/blog/194.html)我们通过定制了CPU和内存展示界面,体验了HT for Web通过定义矢量实现图形绘制与业务数据的代码解耦及绑定联动, ...
随机推荐
- 短网址资料-nginx非root用户启动-systemctl启动脚本-分割root权限
https://www.cnblogs.com/aspnethot/articles/3492191.htmlhttps://www.cnblogs.com/aspnethot/articles/34 ...
- redis(五)---- 简单消息队列
消息队列一个消息的链表,是一个异步处理的数据处理引擎.不仅能够提高系统的负荷,还能够改善因网络阻塞导致的数据缺失.一般用于邮件发送.手机短信发送,数据表单提交.图片生成.视频转换.日志储存等. red ...
- 尝试解决 : Microsoft Visual C++ 14.0 is required 的问题
当在pycharm 中安装 gevent 的时候 发生了 错误 晚上搜索的时候发现 解决问题有两种 方法 1 是 下载 whl 文件 通过二进制的方式 导入模块的包 想了想 ...
- zabbix监控日志关键字
1 添加zabbix监控项目 A.选择类型为“”zabbix客户端(主动式)“” B.键值: xx_log.log 为日志的绝对路径 connectException 为关键字 ---需根据自己需要定 ...
- codeblocks无法找到编译器问题的三个原因
出现如下提示: 查看了网上的资料, 1.编译器选择错误 查看路径:setting→compiler Selected compiler选取你已经安装且准备使用的编译器. 2.工程创建时配置错误 查看路 ...
- JavaScript—暂告,小节
Javastript 的学习告一段落了.虽然还有更多的:爬虫 什么的 但是和我在学校的相比 要扎实许多.知道了兼容性 面向对象,闭包.....一些细节的用法. 虽然以后可能很少用到.可是我觉得 ...
- 并发与高并发(七)-线程安全性-原子性-atomic
一.线程安全性定义 定义:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程 ...
- h5-立方体
1.制作一个立方体:首先要有6个面 <div class="box"> <div class="front">front</div ...
- sklearn.metrics中的评估方法介绍(accuracy_score, recall_score, roc_curve, roc_auc_score, confusion_matrix)
1 accuracy_score:分类准确率分数是指所有分类正确的百分比.分类准确率这一衡量分类器的标准比较容易理解,但是它不能告诉你响应值的潜在分布,并且它也不能告诉你分类器犯错的类型.常常误导初学 ...
- 编程作业3.1:Multi-class classification(One-vs-all)
题目: 在本次练习中,你将使用逻辑回归和神经网络来识别手写数字(从0到9). 今天,自动手写数字识别被广泛使用,从识别信封上的邮政编码到识别银行支票上的金额.这个练习将向你展示如何将你所学的方法用于此 ...