有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java中的静态初始化顺序:Netty 4.0.7的例外

我在初始化类时遇到了Netty的问题。我认为静态字段总是在实例字段之前初始化,但显然不是这样:

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

第一次创建未经处理的UnsaleUnsafeDirectByTeBuf时,会在其构造函数中引发空指针异常:

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/buffer

这三个来源如下:

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。这里是stackTrace:

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

共 (1) 个答案

  1. # 1 楼答案

    堆栈跟踪显示您的问题:

    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的类初始化完成并分配leakDetector之前)正在生成日志语句

    更新:

    查看整个堆栈跟踪时,最令人困惑的可能是,还不清楚发生了什么。关键是UnpooledUnsafeDirectByteBuf的静态类初始化尚未发生!jvm的当前状态是:

    1. UnmoledUnsafeDirectByTeBuf分配器正在尝试创建UnmoledUnsafeDirectByTeBuf的新实例
    2. 已加载(但未初始化)未经处理的UnsaledUnsafeDirectByTeBuf类
    3. AbstractReferenceCountedByteBuf类已加载(但未初始化),因为它是UnmoledUnsafeDirectByTeBuf的父类
    4. AbstractByteBuf类已加载(但未初始化),因为它是AbstractReferenceCountedByteBuf的父类
    5. AbstractByteBuf类初始化开始(因为父类在子类之前初始化),leakDetector仍然为null
    6. ResourceLeakDetector类已加载,因为它被AbstractByteBuf类init引用
    7. ResourceLeakDetector类初始化开始,其中包括一条日志语句
    8. 。。。还有很多其他的方法调用
    9. UnpoliedUnsafeDirectByTeBuf分配器创建UnpoliedUnsafeDirectByTeBuf的新实例(这是对该方法的递归调用)
    10. 已分配UnmoledUnsafeDirectByTeBuf的新实例(该类已加载,但尚未初始化)
    11. NullPointerException

    如果从ResourceLeakDetector类init中删除日志调用,发生的情况是:

    1. 同样的
    2. 同样的
    3. 同样的
    4. 同样的
    5. 同样的
    6. 同样的
    7. ResourceLeakDetector类初始化运行到完成
    8. AbstractByteBuf类初始化完成,leakDetector现在被分配到
    9. AbstractReferenceCountedByteBuf类初始化运行到完成
    10. unpoliedUnsafeDirectByteBuf类初始化运行到完成
    11. UnmoledUnsafeDirectByTeBuf分配器创建UnmoledUnsafeDirectByTeBuf的新实例
    12. 生活过得很愉快