1,内存泄露级别
DISABLED, SIMPLE, ADVANCED, PARANOID;
DISABLED(禁用);: 不进行内存泄露的检测;
SIMPLE(操作简单): 抽样检测,且只对部分方法调用进行记录,消耗较小,有泄漏时可能会延迟报告,默认级别;
ADVANCED(高级): 抽样检测,记录对象最近几次的调用记录,有泄漏时可能会延迟报告;
PARANOID(偏执): 每次创建一个对象时都进行泄露检测,且会记录对象最近的详细调用记录。是比较激进的内存泄露检测级别,消耗最大,建议只在测试时使用。
如果需要修改默认的检测级别,可以通过:1、调用静态方法setLevel进行修改;2、设置启动参数io.netty.leakDetectionLevel
版权声明:本文为博主原创文章,未经博主允许不得转载。
netty中用到内存泄露检测的地方主要有:1、CompositeByteBuf;2、HashedWheelTimer;3、继承AbstractByteBufAllocator的几个类。
下面我们看看netty里的内存检测类ResourceLeakDetector的具体实现:
netty的内存泄露检测分为四级:
DISABLED: 不进行内存泄露的检测;
SIMPLE: 抽样检测,且只对部分方法调用进行记录,消耗较小,有泄漏时可能会延迟报告,默认级别;
ADVANCED: 抽样检测,记录对象最近几次的调用记录,有泄漏时可能会延迟报告;
PARANOID: 每次创建一个对象时都进行泄露检测,且会记录对象最近的详细调用记录。是比较激进的内存泄露检测级别,消耗最大,建议只在测试时使用。
如果需要修改默认的检测级别,可以通过:1、调用静态方法setLevel进行修改;2、设置启动参数io.netty.leakDetectionLevel。
由于内存泄露主要是对某一类资源的检测,因此对于同一类的对象,只需实例化一个ResourceLeakDetector, 否则起不到检测的作用。
- public class HashedWheelTimer implements Timer {
- ...
- private static final ResourceLeakDetector<HashedWheelTimer> leakDetector =
- new ResourceLeakDetector<HashedWheelTimer>(
- HashedWheelTimer.class, 1, Runtime.getRuntime().availableProcessors() * 4);
- ...
- public ResourceLeakDetector(String resourceType, int samplingInterval, long maxActive) {
- if (resourceType == null) {
- throw new NullPointerException("resourceType");
- }
- if (samplingInterval <= 0) {
- throw new IllegalArgumentException("samplingInterval: " + samplingInterval + " (expected: 1+)");
- }
- if (maxActive <= 0) {
- throw new IllegalArgumentException("maxActive: " + maxActive + " (expected: 1+)");
- }
- this.resourceType = resourceType;
- // 抽样间隔,当基本为SIMPLE或ADVANCED时,每创建samplingInterval个对象进行一次记录。
- this.samplingInterval = samplingInterval;
- // 最大活跃对象数,超过这个值就会进行对应处理(如报警或主动关闭资源)
- this.maxActive = maxActive;
- head.next = tail;
- tail.prev = head;
- }
- public ResourceLeak open(T obj) {
- Level level = ResourceLeakDetector.level;
- // 关闭检测
- if (level == Level.DISABLED) {
- return null;
- }
- if (level.ordinal() < Level.PARANOID.ordinal()) {
- // 小于PARANOID及ADVANCED和SIMPLE, 每创建samplingInterval个对象调用一次reportLeak
- if (leakCheckCnt ++ % samplingInterval == 0) {
- reportLeak(level);
- return new DefaultResourceLeak(obj);
- } else {
- return null;
- }
- } else {
- // PARANOID级别每次都调用reportLeak
- reportLeak(level);
- return new DefaultResourceLeak(obj);
- }
- }
- <pre name="code" class="java">private void reportLeak(Level level) {
- // 内存泄露的主要报告方式为日志,因此如果日志级别不够,则只进行数据处理,不走具体的报告分支
- if (!logger.isErrorEnabled()) {
- for (;;) {
- // 这个refQueue里面主要是被垃圾回收的对象,垃圾回收过的对象如果保存到refQueue不是本次讨论的重点,有兴趣可以搜索PhantomReference,ReferenceQueue相关文章
- @SuppressWarnings("unchecked")
- DefaultResourceLeak ref = (DefaultResourceLeak) refQueue.poll();
- if (ref == null) {
- break;
- }
- // 对回收的对象调用close方法,这个方法会减少active的记数
- ref.close();
- }
- return;
- }
- // 非PARANOID级别每隔sampleInterval记录一次,因此这里又乘以sampleInterval,使抽样的情况下偏差尽可能小
- int samplingInterval = level == Level.PARANOID? 1 : this.samplingInterval;
- if (active * samplingInterval > maxActive && loggedTooManyActive.compareAndSet(false, true)) {
- // 活跃数大于maxActive则进行报警,且只报一次
- logger.error("LEAK: You are creating too many " + resourceType + " instances. " +
- resourceType + " is a shared resource that must be reused across the JVM," +
- "so that only a few instances are created.");
- }
- // Detect and report previous leaks.
- for (;;) {
- @SuppressWarnings("unchecked")
- DefaultResourceLeak ref = (DefaultResourceLeak) refQueue.poll();
- if (ref == null) {
- break;
- }
- ref.clear();
- // 调用close方法,确保该资源被移除,如果返回true,表明资源虽然被垃圾回收掉了,但是没有在应用中显式的调用close方法,后面会在日志中警告用户通过更高的基本来进行更详细的分析
- if (!ref.close()) {
- continue;
- }
- String records = ref.toString();
- if (reportedLeaks.putIfAbsent(records, Boolean.TRUE) == null) {
- if (records.isEmpty()) {
- logger.error("LEAK: {}.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 '-D{}={}' or call {}.setLevel() " +
- "See http://netty.io/wiki/reference-counted-objects.html for more information.",
- resourceType, PROP_LEVEL, Level.ADVANCED.name().toLowerCase(), simpleClassName(this));
- } else {
- logger.error(
- "LEAK: {}.release() was not called before it's garbage-collected. " +
- "See http://netty.io/wiki/reference-counted-objects.html for more information.{}",
- resourceType, records);
- }
- }
- }
- }
- private void record0(Object hint, int recordsToSkip) {
- if (creationRecord != null) {
- // 得到当前的调用栈信息,由于这里的执行比较耗时,所以频繁调用对应用是有明显的性能损耗的,因此netty中的默认级别是SIMPLE
- String value = newRecord(hint, recordsToSkip);
- // 将信息记录到lastRecords中,并保持最多MAX_RECORDS条记录,该信息会在检测到内存泄露时打印到日志中
- synchronized (lastRecords) {
- int size = lastRecords.size();
- if (size == 0 || !lastRecords.getLast().equals(value)) {
- lastRecords.add(value);
- }
- if (size > MAX_RECORDS) {
- lastRecords.removeFirst();
- }
- }
- }
- }
- public boolean close() {
- // 保证一个对象只执行一次close方法
- if (freed.compareAndSet(false, true)) {
- synchronized (head) {
- active --;
- prev.next = next;
- next.prev = prev;
- prev = null;
- next = null;
- }
- return true;
- }
- return false;
- }
内存相关泄露的检测实现本身比较简单,即打开资源时增加一个活跃对象数,释放一次资源时减少一个活跃对象数,如果活跃对象数超过阈值则报告异常。但需要注意的是如果要做到有意义的内存检测则需要遵循新建对象调用ResourceLeakDetector.open方法,释放对象调用ResourceLeak.close,否则可能会出现误报的情况。同时激进的内存检测对性能有很大影响,在生产环境下尽量不要打开。
好了,ResourceLeakDetector的分析到这里基本上结束了,但是还存在几个问题,leakCheckCnt、active、head、tail的操作都是线程不安全的,而netty使用到该类的时候,都是一个类new一个detector对象,操作的时候并没有加锁或排队之类的机制,如果有多个线程同时操作会发生什么情况。默认情况下由于操作的LEVEL是SIMPLE,默认的interval也比较大(113),因此对active、prev、next并发操作的情况会相对减少;leakCheckCnt是一个递增的操作,即使出错,也并不会造成严重的影响。有空的时候需要再来测下并发情况下会产生什么情况
相关推荐
本书中的案例涵盖了Netty的启动和停止、内存、并发多线程、性能、可靠性、安全等方面,囊括了Netty绝大多数常用的功能及容易让人犯错的地方。在案例的分析过程中,还穿插讲解了Netty的问题定位思路、方法、技巧,...
84_Netty引用计数注意事项与内存泄露检测方式;85_Netty编解码器剖析与入站出站处理器详解;86_Netty自定义编解码器与TCP粘包拆包问题;87_Netty编解码器执行流程深入分析;88_ReplayingDecoder源码分析与特性解读;...
84_Netty引用计数注意事项与内存泄露检测方式 85_Netty编解码器剖析与入站出站处理器详解 86_Netty自定义编解码器与TCP粘包拆包问题 87_Netty编解码器执行流程深入分析 88_ReplayingDecoder源码分析与特性解读 89_...
第84讲:Netty引用计数注意事项与内存泄露检测方式 第85讲:Netty编解码器剖析与入站出站处理器详解 第86讲:Netty自定义编解码器与TCP粘包拆包问题 第87讲:Netty编解码器执行流程深入分析 第88讲:...
测试工具参数配置灵活,可满足一般性能测试、延迟测试、最大连接数测试、吞吐量测试、压力测试、长时间稳定性测试、内存泄漏测试等场景。测试工具基于频繁的业务测试不断优化改进,稳定可靠、实用性强。简介:基于...
82_Netty引用计数原子更新揭秘与AtomicIntegerFieldUpdater深度剖析 83_AtomicIntegerFieldUpdater实例演练与volatile关键字分析 84_Netty引用计数注意事项与内存泄露检测方式 85_Netty编解码器剖析与入站出站处理器...
首先写一个测试方法,直接向ByteBuf写入中国万岁!,然后如果是堆内存直接打印即可。源码如下: @Test public void testHeapBuffer2() { //取得堆内存 (但是默认是 directByDefault=true) By
akka-persistence-inmemory.zip,akka persistence inmemoryakka persistence inmemory是akka persistence的一个插件,它存储日志和快照消息内存,在测试持久参与者、持久fsm和akka集群时非常有用
主要技术:Netty,Kafka,内存+Redis二级缓存/订阅发布 特点:高性能,高并发,高可用,支持K8S同POD多副本集群部署,横向拓展扩容 测试简介:基于4c8g云虚拟主机,IO密集型机器实测单节点TPS稳定高达13000/s。最长压测时间...
支持嵌入式启动使用内存,但不支持集群 支持sprint-boot-jmqtt-starter 支持测试用例 正式文件 快速开始 下载(3. X以上的版本)或clone此项目 在根目录中执行: mvn -Ppackage-all -DskipTests clean install -U ...
6、支持多种通信框架(Mina/Netty/Grizzly),支持多种序列化/反序列化(Java/Hessian/PB); 7、支持自定义通信协议,可完全替换NFS-RPC自带的协议。 淘宝开放平台JAVA版SDK top4java 设计原则 容易维护扩展(不...
内存:包括随机访问内存 (RAM) 和只读存储器 (ROM),用于临时或永久地存储程序和数据供CPU快速访问。 存储设备:如硬盘、固态硬盘 (SSD)、光盘驱动器等,用于长期保存大量的程序和数据。 输入/输出设备:如键盘、...
测试工具参数配置灵活,可满足一般性能测试,延迟测试,最大连接数测试,防爆测试,压力测试,持久稳定性测试,内存泄漏测试等场景。测试工具基于不断的业务测试不断优化改进,稳定可靠,实用性强。 特征: 高性能,...
以下是基准测试结果。 GOMAXPROCS = 1 net / http服务器: $ GOMAXPROCS=1 go test -bench=NetHTTPServerGet -benchmem -benchtime=10s BenchmarkNetHTTPServerGet1ReqPerConn 1000000 12052 ns/op 2297 B/...
React性功能性内存通用类(用于测试,而非用于生产) 杰克逊适配器(Json&Yaml)实用程序 文件阅读器实用程序 jconf是用于读取yaml配置文件的模块(取决于Jackson适配器) 允许使用标记(例如, include: sub_...
是一个小而稳定的内存缓存客户端的尝试。 Folsom 是完全异步的,基于 Netty,并在整个 API 中使用 Java 8 的 CompletionStage。 构建状态 Maven 中心 构建依赖 Java 8 或更高版本 Maven Docker - 运行集成测试。 ...
window笔记本电脑本地测试:单网关、单前置节点,每秒处理并发心跳6000+(根据jmeter本地最新压测统计数据),20W在线终端(长连接保持)内存占用量1G左右 心跳检测 单机版可以通过配置文件个性化配置规约的心跳周期,...
api_runtime_compare 介绍 本项目旨在收集 API 运行时示例以进行比较。... 每个运行时中实现的公共 API...netty on heidegger, 2014-07-09 (4GB heap) 像往常一样,为了最大的方便,我在与运行服务相同的系统上运行测试。
netty展示页面 可定制接口: 蜘蛛人抓取的范围,比如某个域名,蜘蛛人抓取的内容,比如去除#块链接 线程池的配置,线程池可以根据您当前的环境做相应的变化 lucene配置工作,包括分词,索引,存取模型等 前端接口配置,更改...
内存后端内存后端是默认后端,通常用于伪造MongoDB进行集成测试。 它支持大多数CRUD操作,命令和聚合框架。 某些功能尚未实现,例如全文搜索或地图/缩小。 将以下Maven依赖项添加到您的项目中: < dependency> ...