Netty 4/5确定性缓冲区泄漏检测,可用于单元测试



我读了很多关于Netty4中缓冲区泄漏检测的文章,对我来说,在单元测试中似乎没有确定的方法来检测这种泄漏。

然而,这种功能对单元测试的重要性是如此之大,以至于没有明确的指导方针来做这件事,这让人感觉非常错误

此外,包括原始文件在内的大多数来源http://netty.io/wiki/reference-counted-objects.html通过给出诸如之类的模糊提示,让事情变得令人惊讶地困惑

"以下输出显示了我们的单元测试(XmlFrameDecoderTest.testDecodeWithXml())的泄漏:"

这听起来像是有一种方法可以在单元测试中确定地检测缓冲区分配器中的泄漏,而事实上没有这种方法,正如Trustin Lee自己在回答中指出的那样(链接如下)。

难怪这篇文章被各种来源转发了几十次,他们不知道,只是在没有测试的情况下复制粘贴单词。

*)Trustin Lee在下面的主题中建议在繁忙的工作负载下运行应用程序30秒。Netty 4/5实际上没有检测到字节跳动的资源泄漏?然而,对我来说,这不会触发检测或ResourceLeakDetector的任何输出

*)我还尝试了以下主题中建议的GC技巧如何在Java中强制垃圾收集?但这也没什么区别。

GC是如此不可预测,以至于很难想象如何利用ResourceLeakDetector来创建干净彻底的缓冲区泄漏单元测试。

*)另一种方法是为运行测试时创建的每个ByteBuf测试refCnt。但有时不可能掌握每一个这样的引用,因为接口可能会将String声明为输入参数,然后它的实现会在内部创建并发布ByteBuf实例,而该引用对于单元测试来说是不可访问的,但是如果没有发布,则会产生泄漏,在单元测试中无法检测到。

*)我也找不到一种简单的方法来从分配器中获得所有现有缓冲区的列表,否则就可以只检查其中每一个缓冲区的refCnt。


我想知道是否有人能分享哪些以确定性方式工作的最佳实践,并且可以在单元测试中实际使用,以在使用Netty缓冲区分配器的大型代码库中一致地发现缓冲区泄漏。

到目前为止,在我看来,单元测试对此毫无用处,除非你在单元测试中长时间运行一个完整的服务器(顺便说一句,这也不能保证你什么,只是在理论上增加了你的机会)。就我所见,没有充分的理由存在这样的测试限制,但我们有我们所拥有的。

不幸的是,互联网上有太多关于这个话题的令人困惑的信息,来源于Netty文档本身,我真的希望以直接明了的方式陈述事实。

即使我的问题的答案是"这是不可能的",只要有这篇文章就可以节省一些人大量的研究时间。


p.S.一个非常简单的例子来说明输出的缺乏。如果有人能告诉我需要做哪些更改才能使代码产生泄漏输出,我将不胜感激。

http://gist.github.com/codekrolik/e55b8ece07270f40aad85f691696fe6a

所以我设法让单元测试为我工作。

机制如下:

1) 按照中的建议,创建具有禁用缓存的PooledBufferAllocatorhttps://github.com/netty/netty/issues/5275

PooledByteBufAllocator alloc = new PooledByteBufAllocator(true, 1, 1, 8192, 11, 0, 0, 0);

2) 确保所有引导程序都使用此分配器

a。客户端

Bootstrap clientBootstrap = new Bootstrap();
clientBootstrap.option(ChannelOption.ALLOCATOR, alloc);

b。服务器

ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.option(ChannelOption.ALLOCATOR, alloc)
    .childOption(ChannelOption.ALLOCATOR, alloc);

3) 测试完成后,检查直接缓冲区和堆缓冲区的缓冲区泄漏

assertEquals(0, getActiveDirectBuffers(alloc));
assertEquals(0, getActiveHeapBuffers(alloc));
int getActiveDirectBuffers(PooledByteBufAllocator alloc) {
    int directActive = 0, directAlloc = 0, directDealloc = 0;
    for (PoolArenaMetric arena : alloc.directArenas()) {
        directActive += arena.numActiveAllocations();
        directAlloc += arena.numAllocations();
        directDealloc += arena.numDeallocations();
    }
    System.out.println("directActive " + directActive + " directAlloc " + directAlloc + " directDealloc " + directDealloc);
    return directActive;
}
int getActiveHeapBuffers(PooledByteBufAllocator alloc) {
    int heapActive = 0, heapAlloc = 0, heapDealloc = 0;
    for (PoolArenaMetric arena : alloc.heapArenas()) {
        heapActive += arena.numActiveAllocations();
        heapAlloc += arena.numAllocations();
        heapDealloc += arena.numDeallocations();
    }
    System.out.println("heapActive " + heapActive + " heapAlloc " + heapAlloc + " heapDealloc " + heapDealloc);
    return heapActive;
}

对于单元测试,System.gc()对我很有效。然而,对我来说,单元测试中的泄漏主要是因为我在测试中忘记释放缓冲区(而不是因为服务器代码本身有泄漏)。

正如您所提到的,集成和/或负载测试可能有助于发现服务器中的任何泄漏。

最新更新