(转)Netty开发调试设置定位内存泄漏

Posted jfcat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(转)Netty开发调试设置定位内存泄漏相关的知识,希望对你有一定的参考价值。

使用Netty写的网络代理最近几天总是报OOM,重启不用太久就报错,通过Netty的参数进行检查发现一些问题,在网上找到一些资料,下面这篇比较直接解决问题,转载如下:

偶然一次再测试环境看到netty出现内存泄漏的日志,报错信息如下:

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()

看到这个错误,看不出内存泄漏的位置。但在环境里也没有设置

-Dio.netty.leakDetectionLevel=advanced

这个问题在测试环境出现还好,如果在生产环境那就糟糕了。所以怎么可以今早在开发过程中就发现以及定位到内存泄漏的问题呢?

Netty的内存泄漏检测分为四个级别:

  • DISABLED - 完成禁止检测内存泄漏,这个是不推荐。
  • SIMPLE - 如果buffer中出现1%的内存泄漏,打印错误日志,就是上面那样。但是日志是看不到详细在什么位置出现了内存泄漏。鉴于性能考虑,这个是在生产环境中默认使用。
  • ADVANCED - 如果buffer的1%出现内存泄漏,打印错误,并且输出详细的内存泄漏信息。
  • PARANOID - 这个和ADVANCED输出的内容是一样的,但它会对每一次请求的buffer做检查。很适用于调试和单元测试。

建议

1、为了今早的发现问题,强烈建议在开发调试过程中,把检测级别设为PARANOID,设置:

io.netty.leakDetection.level=PARANOID

2、谨记哪里消费buffer,哪里释放,即调用ReferenceCountUtil.release(msg)

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    try {
        ...
    } finally {
        ReferenceCountUtil.release(msg);
    }
}

输出的详细信息示例

可以看出在EchoServerHandler上出现了内存泄漏。

12:05:24.374 [nioEventLoop-1-1] ERROR io.netty.util.ResourceLeakDetector - LEAK: ByteBuf.release() was not called before it's garbage-collected.
Recent access records: 2
#2:
Hint: 'EchoServerHandler#0' will handle the message from this point.
io.netty.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:329)
io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846)
io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:133)
io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:485)
io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:452)
io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:346)
io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:794)
java.lang.Thread.run(Thread.java:744)
#1:
io.netty.buffer.AdvancedLeakAwareByteBuf.writeBytes(AdvancedLeakAwareByteBuf.java:589)
io.netty.channel.socket.nio.NiosocketChannel.doReadBytes(NioSocketChannel.java:208)
io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:125)
io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:485)
io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:452)
io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:346)
io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:794)
java.lang.Thread.run(Thread.java:744)
Created at:
io.netty.buffer.UnpooledByteBufAllocator.newDirectBuffer(UnpooledByteBufAllocator.java:55)
io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:155)
io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:146)
io.netty.buffer.AbstractByteBufAllocator.ioBuffer(AbstractByteBufAllocator.java:107)
io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:123)
io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:485)
io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:452)
io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:346)
io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:794)
java.lang.Thread.run(Thread.java:744)                

参考:

https://netty.io/wiki/reference-counted-objects.html     

https://majing.io/posts/10000049411246

   

以上是关于(转)Netty开发调试设置定位内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

(转)java内存泄漏的定位与分析

Memory Analyzer Tool定位Java heap space内存泄漏

netty API 中的内存泄漏

由于 Netty 中的 ByteBuffers 导致的内存泄漏

如何查看和调试动态链接库的内存泄露

Lua内存泄漏应对方法[转]