如何查找以下 netty 错误的根本原因:io.netty.util.internal.OutOfDirectMemoryError: failed to allocate 16777216 byte(s)

dar*_*k_n 5 out-of-memory netty

我经历过从负责将数据发送到 TCP 客户端的管道抛出的以下异常。

2017-03-02T18:00:53,749 [epollEventLoopGroup-3-1] ERROR [ExceptionHandler - null] - Unknown exception
io.netty.util.internal.OutOfDirectMemoryError: failed to allocate 16777216 byte(s) of direct memory (used: 117440512, max: 119537664)
    at io.netty.util.internal.PlatformDependent.incrementMemoryCounter(PlatformDependent.java:613) ~[netty-common-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.util.internal.PlatformDependent.allocateDirectNoCleaner(PlatformDependent.java:567) ~[netty-common-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.buffer.PoolArena$DirectArena.allocateDirect(PoolArena.java:699) ~[netty-buffer-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.buffer.PoolArena$DirectArena.newChunk(PoolArena.java:688) ~[netty-buffer-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.buffer.PoolArena.allocateNormal(PoolArena.java:237) ~[netty-buffer-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.buffer.PoolArena.allocate(PoolArena.java:213) ~[netty-buffer-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.buffer.PoolArena.allocate(PoolArena.java:141) ~[netty-buffer-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:262) ~[netty-buffer-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:179) ~[netty-buffer-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:170) ~[netty-buffer-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.buffer.AbstractByteBufAllocator.ioBuffer(AbstractByteBufAllocator.java:131) ~[netty-buffer-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.channel.DefaultMaxMessagesRecvByteBufAllocator$MaxMessageHandle.allocate(DefaultMaxMessagesRecvByteBufAllocator.java:73) ~[netty-transport-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.channel.RecvByteBufAllocator$DelegatingHandle.allocate(RecvByteBufAllocator.java:124) ~[netty-transport-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:950) [netty-transport-native-epoll-4.1.2.Final-linux-x86_64.jar:4.1.2.Final]
    at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe$1.run(AbstractEpollChannel.java:359) [netty-transport-native-epoll-4.1.2.Final-linux-x86_64.jar:4.1.2.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:400) [netty-common-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:306) [netty-transport-native-epoll-4.1.2.Final-linux-x86_64.jar:4.1.2.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:805) [netty-common-4.1.2.Final.jar:4.1.2.Final]
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:145) [netty-common-4.1.2.Final.jar:4.1.2.Final]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_60] 
Run Code Online (Sandbox Code Playgroud)

报告此错误的组件以块的形式发送数据。块大小范围为 512b 到 15kb

您能否建议我有关找到此错误根本原因的最佳工具和技术。

Dmi*_*kiy 8

上述异常可能有两个原因:

  1. 您的应用程序中存在内存泄漏(很可能);
  2. 您确实创建了很大的负载或在直接内存中分配了许多对象,因此您需要更多的直接内存;

对于情况 1,您需要启用偏执级别的对象跟踪,以便-Dio.netty.leakDetection.level=paranoid在服务器启动期间(或-Dio.netty.leakDetection.level=advanced至少级别)检测属性可能存在的内存泄漏。启用此选项后,如果检测到内存泄漏,netty 将通过日志消息通知您,以便您能够修复管道。你需要从这一点开始。更多信息

如果上述情况不适合您,那么您可以尝试使用-XX:MaxDirectMemorySize=上述答案中所述的选项来增加内存大小。另一种选择是使用 关闭直接内存分配器-Dio.netty.noPreferDirect=true。最后一个选项 - 您可以增加为进程分配的内存。看来您的服务器只有 512 RAM。

另一项建议是更新到最新的 netty 版本。因为以前的一些版本netty本身就存在内存泄漏。


小智 3

在您的情况下,直接内存大小已耗尽。根据 io.netty.util.internal.PlatformDependent 类的代码,您可以在这里找到一些注释:

    // Here is how the system property is used:
    //
    // * <  0  - Don't use cleaner, and inherit max direct memory from java. In this case the
    //           "practical max direct memory" would be 2 * max memory as defined by the JDK.
    // * == 0  - Use cleaner, Netty will not enforce max memory, and instead will defer to JDK.
    // * >  0  - Don't use cleaner. This will limit Netty's total direct memory
    //           (note: that JDK's direct memory limit is independent of this).
    long maxDirectMemory = SystemPropertyUtil.getLong("io.netty.maxDirectMemory", -1);
Run Code Online (Sandbox Code Playgroud)

建议通过命令行使用'-XX:MaxDirectMemorySize='修改JVM运行时参数


我对你的描述感到困惑:“块大小不同于 512b 到 15kb 的范围”。好吧,日志表明另一个大小“无法分配 16777216 字节”16Mbytes。也许您需要检查您的应用程序的包裹尺寸。


通常,我在使用 Netty 时会修改一些引导选项,例如

            bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
                .option(ChannelOption.SO_SNDBUF, 1024 * 256)
                .option(ChannelOption.SO_RCVBUF, 1024 * 32768)
                .option(ChannelOption.TCP_NODELAY, true);
Run Code Online (Sandbox Code Playgroud)

或者

bootstrap.group(bossGroup, workerGroup)
            .option(ChannelOption.TCP_NODELAY, true)
            .option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(4096))
            .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
Run Code Online (Sandbox Code Playgroud)