2023-09-13
原文作者:https://blog.csdn.net/wangwei19871103/category_9681495_2.html 原文地址: https://blog.csdn.net/wangwei19871103/article/details/104675120

最容易内存泄漏的例子

客户端代码

写个处理器,里面就加这段,就给服务器发送1亿条1

202309132208482281.png

服务端代码

写个处理器继承ChannelInboundHandlerAdapter,就获取消息对象,转成字节缓冲区,打印内存地址和内容。

202309132208488812.png

堆内内存溢出

虚拟机参数设置

为了让堆内内存快速溢出,我加了虚拟机参数:

    -Xmx10m //堆内最大10M
    -XX:MaxDirectMemorySize=800M //堆外800M
    -Dio.netty.allocator.maxOrder=4 //让缓存区变小,可以让块变小,可以开启缓存
    -XX:+HeapDumpOnOutOfMemoryError //监控堆内溢出了
    -XX:HeapDumpPath=D:\gc.dump //把溢出信息保存到文件里

这里要注意,为了快速看到堆内存溢出,我把堆最大内存设置为10M,但是这样netty默认的设置就达不到开启缓存的要求,就不池化了,所以我设置io.netty.allocator.maxOrder=4 ,让块内存变小,可以开启缓存池化,如果不开启池化了,直接用UnpooledUnsafeDirectByteBuf,我们希望使用默认的PooledUnsafeDirectByteBuf堆内内存溢出,所以要开启缓存,因为netty现在默认是用PooledUnsafeDirectByteBuf来做IO缓冲区的。至于为什么要这么设置,可以去PooledByteBufAllocator的静态代码块里看,里面都有,自己调试下好了:

202309132208493343.png

202309132208501394.png

运行服务端

还没开启客户端

还没客户端的时候:

202309132208508185.png

开启客户端

内存地址一直在变,说明没有池化复用。

202309132208514776.png

visualvm看内存使用情况

内存方面也开始不停的GC

202309132208522617.png

visualvm看内存对象数量

内存里这几个对象特别多,一个是缓存分配里的子页内存,其他两个是回收句柄和缓冲区:

202309132208529968.png

visualvm看GC情况

3分钟左右的GC情况,两种GC都很多了:

202309132208536489.png

堆内内存溢出了

过了一会儿堆内内存溢出了:

2023091322085440810.png

visualvm看dump文件

然后我们看dump文件:

2023091322085517911.png
很多都是PoolSubPage,说明创建了好多小缓冲区,没有复用。

为什么会出现这种呢,不是说netty会自动释放么,其实有自动释放的,好多地方,比如如果你用SimpleChannelInboundHandler的话,就会自动释放,其实是封装了一层而已:

2023091322085659712.png

最简单解决方法-自己释放

如果我们自己释放呢:

2023091322085761013.png
首先缓冲区复用了:

2023091322085855014.png

内存比较稳:

2023091322085934115.png
GC也还行,没有full gc

2023091322090025916.png
也是3分钟左右,这次的对象个数很稳定:

2023091322090102617.png
内存也一直稳:

2023091322090246318.png

大多数情况可能使用ChannelInboundHandlerAdapter传进来的缓冲区,其实这个是IO的读缓冲区,如果后面不用了,要释放,不然就导致泄漏啦。当然系统也有好多处理器会帮你释放这个,后面我会讲下哪些情况,系统会帮你释放。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。

阅读全文