2

我在类初始化时遇到了 Netty 的问题。我认为静态字段总是在实例字段之前初始化,但显然情况并非如此:

  • AbstractByteBuff 类包含一个static final ResourceLeakDetector<ByteBuf> leakDetector
  • 班级AbstractReferenceCountedByteBuf extends AbstractByteBuff
  • 班级UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf

第一次创建 UnpooledUnsafeDirectByteBuf 时,会在其构造函数中抛出空指针异常:

protected UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        super(maxCapacity);
        if (alloc == null) {
            throw new NullPointerException("alloc");
        }
        if (initialCapacity < 0) {
            throw new IllegalArgumentException("initialCapacity: " + initialCapacity);
        }
        if (maxCapacity < 0) {
            throw new IllegalArgumentException("maxCapacity: " + maxCapacity);
        }
        if (initialCapacity > maxCapacity) {
            throw new IllegalArgumentException(String.format(
                    "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
        }

        this.alloc = alloc;
        setByteBuffer(ByteBuffer.allocateDirect(initialCapacity));
        leak = leakDetector.open(this);
    }

从代码中抛出异常leak = leakDetector.open(this);。通过使用 intellij Idea 检查,我发现该leakDetector变量为空。这怎么可能?它是在当前类的超类的超类中初始化的静态变量。

源代码在 Github 上可用,所有引发问题的类都在以下包中可用:https ://github.com/netty/netty/tree/master/buffer/src/main/java/io/netty/缓冲

三个来源如下:

https://github.com/netty/netty/blob/master/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java

https://github.com/netty/netty/blob/master/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java

https://github.com/netty/netty/blob/master/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java

最后一个,AbstractByteBuff,包含泄漏检测器。

请注意,我没有编译源代码,只是链接到 Netty 4.0.7 final。这里是堆栈跟踪:

o.netty.handler.codec.EncoderException: java.lang.NullPointerException
    at io.netty.handler.codec.MessageToByteEncoder.write(MessageToByteEncoder.java:131)
    at io.netty.channel.DefaultChannelHandlerContext.invokeWrite(DefaultChannelHandlerContext.java:643)
    at io.netty.channel.DefaultChannelHandlerContext.write(DefaultChannelHandlerContext.java:633)
    at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:115)
    at io.netty.channel.DefaultChannelHandlerContext.invokeWrite(DefaultChannelHandlerContext.java:643)
    at io.netty.channel.DefaultChannelHandlerContext.writeAndFlush(DefaultChannelHandlerContext.java:689)
    at io.netty.channel.DefaultChannelHandlerContext.writeAndFlush(DefaultChannelHandlerContext.java:713)
    at io.netty.channel.DefaultChannelPipeline.writeAndFlush(DefaultChannelPipeline.java:893)
    at io.netty.channel.AbstractChannel.writeAndFlush(AbstractChannel.java:239)
    at com.logentries.net.NettyBasedAsyncLogger.logLine(NettyBasedAsyncLogger.java:54)
    at com.logentries.logback.LogentriesAppender.append(LogentriesAppender.java:105)
    at com.logentries.logback.LogentriesAppender.append(LogentriesAppender.java:15)
    at ch.qos.logback.core.AppenderBase.doAppend(AppenderBase.java:85)
    at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:48)
    at ch.qos.logback.classic.Logger.appendLoopOnAppenders(Logger.java:280)
    at ch.qos.logback.classic.Logger.callAppenders(Logger.java:267)
    at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:449)
    at ch.qos.logback.classic.Logger.filterAndLog_1(Logger.java:421)
    at ch.qos.logback.classic.Logger.debug(Logger.java:514)
    at io.netty.util.internal.logging.Slf4JLogger.debug(Slf4JLogger.java:76)
    at io.netty.util.ResourceLeakDetector.<clinit>(ResourceLeakDetector.java:37)
    at io.netty.buffer.AbstractByteBuf.<clinit>(AbstractByteBuf.java:37)
    at io.netty.buffer.UnpooledByteBufAllocator.newDirectBuffer(UnpooledByteBufAllocator.java:49)
    at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:132)
    at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:123)
    at io.netty.buffer.AbstractByteBufAllocator.ioBuffer(AbstractByteBufAllocator.java:76)
    at io.netty.handler.codec.MessageToByteEncoder.write(MessageToByteEncoder.java:107)
    at io.netty.channel.DefaultChannelHandlerContext.invokeWrite(DefaultChannelHandlerContext.java:643)
    at io.netty.channel.DefaultChannelHandlerContext.write(DefaultChannelHandlerContext.java:633)
    at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:115)
    at io.netty.channel.DefaultChannelHandlerContext.invokeWrite(DefaultChannelHandlerContext.java:643)
    at io.netty.channel.DefaultChannelHandlerContext.access$2000(DefaultChannelHandlerContext.java:29)
    at io.netty.channel.DefaultChannelHandlerContext$WriteTask.run(DefaultChannelHandlerContext.java:887)
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:354)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:366)
    at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101)
    at java.lang.Thread.run(Thread.java:662)
Caused by: java.lang.NullPointerException
    at io.netty.buffer.UnpooledUnsafeDirectByteBuf.<init>(UnpooledUnsafeDirectByteBuf.java:72)
    at io.netty.buffer.UnpooledByteBufAllocator.newDirectBuffer(UnpooledByteBufAllocator.java:49)
    at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:132)
    at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:123)
    at io.netty.buffer.AbstractByteBufAllocator.ioBuffer(AbstractByteBufAllocator.java:76)
    at io.netty.handler.codec.MessageToByteEncoder.write(MessageToByteEncoder.java:107)
    ... 36 more
4

1 回答 1

3

堆栈跟踪表明您的问题:

at io.netty.util.internal.logging.Slf4JLogger.debug(Slf4JLogger.java:76)
at io.netty.util.ResourceLeakDetector.<clinit>(ResourceLeakDetector.java:37)
at io.netty.buffer.AbstractByteBuf.<clinit>(AbstractByteBuf.java:37)

"<clinit>"意味着这些类正处于它们的类初始化之中。看起来 ResourceLeakDetector 在类初始化期间生成了一条日志语句(在 AbstractByteBuf 的类初始化完成并分配泄漏检测器之前)。

更新:

查看整个堆栈跟踪最令人困惑的部分可能是尚不清楚尚未发生的事情。关键是静态类的初始化UnpooledUnsafeDirectByteBuf 还没有发生!jvm的当前状态是:

  1. UnpooledByteBufAllocator 正在尝试创建 UnpooledUnsafeDirectByteBuf 的新实例
  2. UnpooledUnsafeDirectByteBuf 类已加载(但未初始化)
  3. AbstractReferenceCountedByteBuf 类已加载(但未初始化),因为它是 UnpooledUnsafeDirectByteBuf 的父类
  4. AbstractByteBuf 类已加载(但未初始化),因为它是 AbstractReferenceCountedByteBuf 的父类
  5. AbstractByteBuf 类初始化开始(因为父母在孩子之前被初始化),leakDetector 仍然为空
  6. ResourceLeakDetector 类被加载,因为它被 AbstractByteBuf 类 init 引用
  7. ResourceLeakDetector 类初始化开始,其中包括一条日志语句
  8. ...进行了许多其他方法调用...
  9. UnpooledByteBufAllocator 创建一个新的 UnpooledUnsafeDirectByteBuf 实例(这是对该方法的递归调用)
  10. 分配了 UnpooledUnsafeDirectByteBuf 的新实例(该类已加载,但尚未初始化)
  11. 空指针异常

如果您从 ResourceLeakDetector 类初始化中删除了日志记录调用,将会发生什么:

  1. 相同的
  2. 相同的
  3. 相同的
  4. 相同的
  5. 相同的
  6. 相同的
  7. ResourceLeakDetector 类初始化运行完成
  8. AbstractByteBuf 类初始化完成,leakDetector 已分配
  9. AbstractReferenceCountedByteBuf 类初始化运行完成
  10. UnpooledUnsafeDirectByteBuf 类初始化运行完成
  11. UnpooledByteBufAllocator 创建一个新的 UnpooledUnsafeDirectByteBuf 实例
  12. 生活愉快地进行
于 2013-08-20T14:08:42.810 回答