【问题标题】:GRPC Severe Memory Leak With Netty使用 Netty 的 GRPC 严重内存泄漏
【发布时间】:2020-04-21 13:59:33
【问题描述】:

我有时在使用 netty 的 GRPC 上遇到异常。

我正在创建一堆这样的服务器:

        for(int i = 0; i < 4; i++) {
            var executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);
            var eventGroup = new NioEventLoopGroup(1); //One thread for each channel
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
            _channels[i] = NettyChannelBuilder
                    .forAddress(host, port + i)
                    .executor(executor)
                    .channelType(NioSocketChannel.class)
                    .eventLoopGroup(eventGroup)
                    .usePlaintext()
                    .build();
            var stub = ServiceDPASGrpc.newStub(_channels[i]);
            _stubs[i] = stub;
            _executors[i] = executor;
        }
        for (int i = 0; i < 4; i++) {
            var impl = ...
            _servers[i] = NettyServerBuilder.forPort(port + i).addService(impl).build();
            _servers[i].start();
            _impls[i] = impl;

        }

在应用程序结束时,我像这样关闭所有内容:

        for (int i = 0; i < 4; i++) {
            _channels[i].shutdown();
            _executors[i].shutdown();
            _servers[i].shutdown();

        }

但有时我会收到异常说明:

Apr 21, 2020 2:32:51 PM io.grpc.netty.shaded.io.netty.util.ResourceLeakDetector reportTracedLeak
SEVERE: LEAK: ByteBuf.release() was not called before it's garbage-collected. See https://netty.io/wiki/reference-counted-objects.html for more information.
Recent access records: 
Created at:
        io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:349)
        io.grpc.netty.shaded.io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:187)
        io.grpc.netty.shaded.io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:178)
        io.grpc.netty.shaded.io.netty.buffer.AbstractByteBufAllocator.ioBuffer(AbstractByteBufAllocator.java:139)
        io.grpc.netty.shaded.io.netty.channel.DefaultMaxMessagesRecvByteBufAllocator$MaxMessageHandle.allocate(DefaultMaxMessagesRecvByteBufAllocator.java:114)
        io.grpc.netty.shaded.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:147)
        io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
        io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
        io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
        io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
        io.grpc.netty.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
        io.grpc.netty.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        io.grpc.netty.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        java.base/java.lang.Thread.run(Thread.java:834)

我做错了什么

【问题讨论】:

    标签: netty grpc grpc-java


    【解决方案1】:

    几件事情要检查。

    1. 您不需要在每个循环中创建大小为 1 的 EventLoop。每个通道仅使用来自 EventLoopGroup 的 1 个 EventLoop。因此,您可以只传入较大线程数的 EventLoopGroup(如果它小于通道数,则某些线程是共享的)。

    2. 通道关闭不是阻塞操作。在后台,它仍然需要一些工作来结束等等。你需要等待优雅地关闭。

    _channels[i]
      .shutdown() // could use `shutdownNow`, but still need to wait
      .awaitTermination(5, TimeUnit.SECONDS); // or whatever time
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 2013-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-18
      • 1970-01-01
      相关资源
      最近更新 更多