user3615911
user3615911

Reputation: 21

LEAK: ByteBuf.release() was not called before it's garbage-collected

This is my server side code. this code read udp traffic with the help of netty. But, sometimes I have an error.... I can't fix it.... It is very sorrow...

@Override
     protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
      ByteBuf bb = msg.content();
      RoomController roomController = RoomController.getInstance();
      try
      {
       int opcode = bb.readUnsignedByte();
       byte slot = bb.readByte();
       byte[] ignore = new byte[8];
       bb.readBytes(ignore);
       int accountId = bb.readUnsignedByte();
       switch(opcode)
        {
         case 65: {
          log.info("Try connect player " + msg.sender().getAddress() + ":" + msg.sender().getPort());
          Player player = roomController.getPlayer(msg.sender().getAddress());
          if(player != null) {
           ByteBuffer _buffer = new ByteBuffer();
           _buffer.writeC((byte) 66);
           _buffer.writeC((byte) 0);
           _buffer.writeB(new byte[5]);
           _buffer.writeC((byte) 0x0b);
           _buffer.writeB(new byte[3]);
           player.playerPort = msg.sender().getPort();
           log.info("Send accept data! " + msg.sender().getAddress().toString() + ":" + msg.sender().getPort());
           ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(_buffer.getData(), 0, _buffer.getData().length), msg.sender()));
          }
          break;
         }
         //case (byte) 131:
         //case (byte) 132:
         //case 84:
         //case 97:
         case 131:
         case 132:
         case 4:
         case 3: {
          Player player = roomController.getPlayer(msg.sender().getAddress());
          if(player != null) {
           if(player.isActive) {
            if(player.isHost) {
             for(Player players : roomController.getPlayersForIP(player.roomID).values()) {
              if(!players.isHost && players.playerPort > 0) {
               ctx.writeAndFlush(new PBUDPServerPacket(opcode, Unpooled.copiedBuffer(msg.content()), new InetSocketAddress(players.playerIP, players.playerPort), (byte)slot, ignore, accountId).getPacket());
              }
             }
            } else {
             Player host = roomController.getHostForIP(player.roomID);
             if(host.playerPort > 0) {
              ctx.writeAndFlush(new PBUDPServerPacket(opcode, Unpooled.copiedBuffer(msg.content()), new InetSocketAddress(host.playerIP, host.playerPort), (byte)slot, ignore, accountId).getPacket());
             }
            }
           }
          }
          break;
         }
         case 97: {
          log.info("opcode: " + opcode + " sender: " + msg.sender().getAddress().getHostAddress());
          ctx.writeAndFlush(new PBUDPServerPacket(opcode, Unpooled.copiedBuffer(msg.content()), new InetSocketAddress(msg.sender().getAddress(), msg.sender().getPort()), (byte)slot, ignore, accountId).getPacket());
          break;
         }
         case 67: {
          log.info("try remove player " + msg.sender().getAddress().toString());
          RoomController.getInstance().removePlayer(msg.sender().getAddress());
          break;
         }
         default: {
          log.warn("unknown opcode: " + opcode);
         }

        }
       }
      catch(Exception e) {
       log.error(e.toString());
      }
      finally {
       bb.clear();
      }
     }

ERROR:

[18:16:46|ERROR|nioEventLoopGroup-2-1 |io.netty.util.ResourceLeakDetector             ] LEAK: ByteBuf.release() was not called before it's garbage-collected.

How to fix an error?? It is very important for me.

STACKTRACE:

[17:14:59|ERROR|nioEventLoopGroup-2-1 |io.netty.util.ResourceLeakDetector             ] LEAK: ByteBuf.release() was not called before it's garbage-collected.
Recent access records: 1
#1:
 io.netty.buffer.AdvancedLeakAwareByteBuf.writeBytes(AdvancedLeakAwareByteBuf.java:559)
 io.netty.buffer.Unpooled.copiedBuffer(Unpooled.java:421)
 ru.pb.battle.network.client.BattleClientConnection.channelRead0(BattleClientConnection.java:89)
 ru.pb.battle.network.client.BattleClientConnection.channelRead0(BattleClientConnection.java:42)
 io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:103)
 io.netty.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:338)
 io.netty.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:324)
 io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:785)
 io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe.read(AbstractNioMessageChannel.java:93)
 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$2.run(SingleThreadEventExecutor.java:101)
 java.lang.Thread.run(Thread.java:744)
Created at:
 io.netty.util.ResourceLeakDetector.open(ResourceLeakDetector.java:190)
 io.netty.buffer.AbstractByteBufAllocator.toLeakAwareBuffer(AbstractByteBufAllocator.java:42)
 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.Unpooled.directBuffer(Unpooled.java:127)
 io.netty.buffer.Unpooled.copiedBuffer(Unpooled.java:417)
 ru.pb.battle.network.client.BattleClientConnection.channelRead0(BattleClientConnection.java:89)
 ru.pb.battle.network.client.BattleClientConnection.channelRead0(BattleClientConnection.java:42)
 io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:103)
 io.netty.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:338)
 io.netty.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:324)
 io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:785)
 io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe.read(AbstractNioMessageChannel.java:93)
 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$2.run(SingleThreadEventExecutor.java:101)
 java.lang.Thread.run(Thread.java:744)

======================PBUDPServerPacket

public class PBUDPServerPacket {
    private ByteBuf buff;
    private InetSocketAddress addr;

    public PBUDPServerPacket(int opcode, ByteBuf data, InetSocketAddress recipient, byte slot, byte[] ignore, int accountId) {
        buff = Unpooled.buffer().order(ByteOrder.LITTLE_ENDIAN);
        buff.writeByte(opcode);
        buff.writeByte(slot);
        buff.writeBytes(ignore);
        buff.writeByte(accountId);
        buff.writeBytes(data);
        addr = recipient;
    }

    public DatagramPacket getPacket() {
        return new DatagramPacket(buff, addr);
    }
}

Upvotes: 2

Views: 25069

Answers (3)

iamfnizami
iamfnizami

Reputation: 183

Add below code in finally block-

finally {
    if(bb!= null) {
        bb.clear();
        bb.release();
    }
}

Also override method 'channelReadComplete' to release buffer whose reference count is 0.

@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
    ctx.flush();
}
  • bb.clear(): reset reader as well as writter index at 0; which netty will flush itself as ByteBuf manages it well.
  • bb.release(): decrease reference count by 1 and make available for release if reference count becomes 0.

Memory leak may cause your application in OutOfDirectMemoryError because buffer uses space in Direct Memory instead of Heap.

Note: If you do not call bb.release() then application must be warning you in service log which is a memory leak and application may face error in future like-

failed to allocate 123 byte(s) of direct memory (used: 234, max: 345)

Upvotes: 0

Imran khan
Imran khan

Reputation: 847

If you are using DataBuffer you might get the same error. Spring has DataBufferUtils library to release the resource.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/io/buffer/DataBufferUtils.html

import org.springframework.core.io.buffer.DataBufferUtils;

DataBufferUtils.release(dataBuffer);

Upvotes: 0

stridecolossus
stridecolossus

Reputation: 1551

You need to call release on the ByteBuf rather than clear in your finally block, otherwise the reference counted buffer is not de-allocated properly.

Upvotes: 3

Related Questions