java抛出不可能的NullPointerException

时间:2014-04-25 18:18:35

标签: java generics jdbc nullpointerexception

简化为基本要素我有以下代码:

public class C_Test extends A_Test {
    C_Test() {
        super( null );
    }
}

public abstract class A_Test {
    private final String m_test;

    public A_Test( String test ) {
        m_test = C_ObjectUtil.defaultIfNull( test, "" );

        if( m_test == null ) {
            throw new RuntimeException( "it happened again!" );
        }
    }
}

public class C_ObjectUtil {
    public static <T> T defaultIfNull( T object, T defaultObject ) {
        return ( object == null ) ? defaultObject : object;
    }
}

使用JDBC从DB2数据库中读取大约70 MB的数据后,调用new C_Test()会抛出不可能的异常。到那时,构造函数被调用超过100000次。

将方法defaultIfNull内联到构造函数A_Test(这会删除泛型)后,一切正常运行!

环境:Sun JDK 1.7.0_25 64位,Linux

知道这里发生了什么吗?


这是堆栈跟踪。它与我上面显示的缩减版本不匹配。在构造函数m_test.equals( "null" )中调用A_Test时抛出此异常。

java.lang.NullPointerException
        at com.src.db.attribvisitors.A_AttribVisitorAssignFromResultSet.<init>(A_AttribVisitorAssignFromResultSet.java:54)
        at com.src.db.attribvisitors.C_AttribVisitorAssignFromSelectStatement.<init>(C_AttribVisitorAssignFromSelectStatement.java:37)
        at com.src.db.attribvisitors.C_AttribVisitorAssignFromSelectStatement.<init>(C_AttribVisitorAssignFromSelectStatement.java:43)
        at com.src.framework.db.migration.A_DbTableRow.initFromResultset(A_DbTableRow.java:96)
        at com.src.framework.db.migration.helper.C_DbTableProcessor$1.process(C_DbTableProcessor.java:66)
        at com.src.db.C_TxSql.query(C_TxSql.java:144)
        at com.src.db.C_SqlExecuter$3.execute(C_SqlExecuter.java:142)
        at com.src.db.C_SqlExecuter.execute(C_SqlExecuter.java:80)
        at com.src.db.C_SqlExecuter.query(C_SqlExecuter.java:138)
        at com.src.framework.db.migration.helper.C_DbTableProcessor.processTable(C_DbTableProcessor.java:58)
        at com.src.framework.db.migration.helper.C_DbTableProcessor.processTable(C_DbTableProcessor.java:39)
        at com.src.tools.C_DbExporter.exportTable(C_DbExporter.java:93)
        at com.src.tools.C_DbExporter.exportTables(C_DbExporter.java:77)
        at com.src.tools.C_DbExporter.exportTables(C_DbExporter.java:61)
        at com.src.tools.C_DbExporterAndImporter.exportTables(C_DbExporterAndImporter.java:95)
        at com.src.tools.C_DbExporterAndImporter.doMain(C_DbExporterAndImporter.java:83)
        at com.src.common.C_MainUtil.runMain(C_MainUtil.java:150)
        at com.src.tools.C_DbExporterAndImporter.main(C_DbExporterAndImporter.java:57)

3 个答案:

答案 0 :(得分:3)

如果您没有向我们展示任何外部干预,这是不可能的。

此调用

m_test = C_ObjectUtil.defaultIfNull( test, "" );

test保存的值和引用的值推送到堆栈上的String文字""。调用该方法,消耗这两个引用。然后调用它

public static <T> T defaultIfNull( T object, T defaultObject ) {
    return ( object == null ) ? defaultObject : object;
}

来自test的值,即。 null绑定objectString的引用值绑定到defaultObject。执行条件运算符并返回defaultObject,即。非空String。这被分配给调用代码中的m_test。没有什么可以拦截代码中的这些步骤。不是另一个线程,不是任何代理机制(它是直接调用的static方法),而不是我能想到的任何东西。

您的错误在其他地方。

(如果您发布有助于确定真正问题的新细节,我将删除或编辑此答案。)

答案 1 :(得分:2)

你所描述的内容非常不可能。然而,这不是第一次不太可能发生。我有点犹豫要发布这个作为答案,因为它真的不是一个,但我们走了。

作为Kayaman points out,所描述的行为可能是由于JIT优化不正确。为了测试这个理论,我会执行两个步骤。

  1. 在发生不祥异常之前,打开-XX:+PrintCompilation并监视输出以编辑相关方法。 这本身并不能证明JIT编译器正在应用不正确的优化。但是,它足以开始第二步,看看JIT正在生成什么机器代码。

  2. 打开-XX:+PrintAssembly并浏览编译器输出的机器代码以获取相关方法。这是很多工作,特别是当你第一次这样做时。

  3. 我看到的方式有三种可能性。

    • 这是代码中的一个(微妙的)错误 - 与JIT无关。
    • JIT实际上是错的(不太可能,对不起)。您应该能够在上面的步骤(2)中证明它。
    • JIT是正确的,但它的(有效)优化会触发其他休眠的错误。你可以在上面的第(2)步中找到它。

    最后要指出的一点是 - 克朗彻已经alluded - 是一种竞争条件的可能性。种族条件值得特别提及,因为它们落在第一类和第三类之间。它们可能与优化本身无关,但优化的加速可能会导致不必要的和无法实现的执行跟踪。

    祝你好运,祝你好运。 :)

答案 2 :(得分:0)

我从 Sun JDK 1.7.0_25 64Bit 切换到 Sun JDK 1.7.0_55 64位,NPE消失了!