2017-04-10 85 views
4

我正在测试自定义Eclipse-RCP应用程序。这个应用程序做了一些简单的初始化,然后启动了一堆线程,这些线程解析了工作区内的很多XML文件。来自随机代码行的NullPointerExceptions

在1000次执行中大约有一次,其中一个线程崩溃并伴有NullPointerException。这通常发生在Xerces内部,有时在其他库中,有时在Java标准库内部发生。问题是这些NullPointerExceptions似乎发生在没有指针被解引用的行中。例如:

java.lang.NullPointerException 
    at java.util.concurrent.locks.ReentrantReadWriteLock$Sync$HoldCounter.<init>(ReentrantReadWriteLock.java:279) 
    at java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter.initialValue(ReentrantReadWriteLock.java:289) 
    at java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter.initialValue(ReentrantReadWriteLock.java:286) 
    at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:180) 
    at java.lang.ThreadLocal.get(ThreadLocal.java:170) 
    at java.util.concurrent.locks.ReentrantReadWriteLock$Sync.tryAcquireShared(ReentrantReadWriteLock.java:481) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireShared(AbstractQueuedSynchronizer.java:1282) 
    at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.lock(ReentrantReadWriteLock.java:727) 
    at org.eclipse.osgi.container.ModuleDatabase.readLock(ModuleDatabase.java:744) 
    at org.eclipse.osgi.container.ModuleDatabase.getWiring(ModuleDatabase.java:431) 
    at org.eclipse.osgi.container.ModuleContainer.getWiring(ModuleContainer.java:398) 
    at org.eclipse.osgi.container.ModuleRevision.getWiring(ModuleRevision.java:137) 
    at org.eclipse.osgi.container.ModuleWire.getProviderWiring(ModuleWire.java:51) 
    at org.eclipse.osgi.internal.loader.BundleLoader.findRequiredSource(BundleLoader.java:1114) 
    at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:392) 
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:352) 
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:344) 
    at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:160) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) 
    at org.eclipse.core.internal.resources.ProjectContentTypes.usesContentTypePreferences(ProjectContentTypes.java:116) 
    at org.eclipse.core.internal.resources.ContentDescriptionManager.getDescriptionFor(ContentDescriptionManager.java:321) 
    at org.eclipse.core.internal.resources.File.getContentDescription(File.java:255) 
    at my_app.ModelParser.getContentType(ModelParser.java:54) 
    at my_app.ModelParser.parse(ModelParser.java:43) 
    at my_app.ValidationModelsCache.getModel(ValidationModelsCache.java:44) 
    at my_app.BuilderContext.getParseResult(BuilderContext.java:37) 
    at my_app.ValidationHandler.validate(ValidationHandler.java:37) 
    at my_app.ProjectValidationBuilder$1.run(ProjectValidationBuilder.java:57) 
    at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) 

没有什么能符合279.事实上null,没有在整个方法的单一的间接引用:

276: static final class HoldCounter { 
277:  int count = 0; 
278:  // Use id, not reference, to avoid garbage retention 
279:  final long tid = getThreadId(Thread.currentThread()); 
280: } 

我已经双重和三重检查我有权利源。我甚至反汇编了其中的一些方法,似乎没有任何方法可以在那里解除null。

再举一例:

Caused by: java.lang.NullPointerException 
    at com.google.common.collect.ObjectArrays.checkElementsNotNull(ObjectArrays.java:233) 
    at com.google.common.collect.ObjectArrays.checkElementsNotNull(ObjectArrays.java:226) 
    at com.google.common.collect.ImmutableList.construct(ImmutableList.java:303) 
    at com.google.common.collect.ImmutableList.of(ImmutableList.java:98) 
    at com.google.common.collect.Iterables.concat(Iterables.java:432) 

线233仅仅是一个return语句:

229:  static Object[] checkElementsNotNull(Object[] array, int length) { 
230:   for (int i = 0; i < length; i++) { 
231:    checkElementNotNull(array[i], i); 
232:   } 
233:   return array; 
234:  } 

到目前为止,这似乎只是一台机器上发生:

CPU: Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz 
Linux: 4.9.0-2-amd64 #1 SMP Debian 4.9.18-1 (2017-03-30) x86_64 GNU/Linux 
Java: 
    openjdk version "1.8.0_121" 
    OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-4-b13) 
    OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode 

但抄录在几个不同的Java和内核版本上。

什么可能导致此行为,如何调试?

OpenJDK有像IBMs -Xdump这样的选项,所以当有问题的NullPointerException发生时,我可以获得核心转储吗?

是否有一些技巧在NullPointerException中设置gdb断点?我猜jdb不会提前赶上它。

这可能与JVM隐式空检查有关吗?是否有一些标志禁用它们(-Xrs似乎没有工作)?

+4

279:final long tid = getThreadId(Thread.currentThread());可以为null。如果getThreadId()返回一个Long,则返回的值可以为null,并且在尝试解析为一个原始long时会导致NPE。 – Wietlol

+0

[getThreadId](http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/80280d8b40e9/src/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java#l1492)返回'long '。 –

+0

这可能是因为你的堆栈跟踪以某种方式被破坏,所以你看到的跟踪实际上与产生异常的地方没有任何关系。 –

回答

3

什么引起这种行为

的仪器代理,硬件错误或SIGSEGV信号以某种方式发送到该进程。

是否OpenJDK的有选择像IBM的-Xdump这样我就可以获取核心转储 当问题发生的NullPointerException?

-XX:AbortVMOnException=java.lang.NullPointerException,但此选项仅在non-product版本中可用。

是否有一些技巧在NullPointerException上设置gdb断点?

你可以尝试以下功能设置断点:

  • Runtime1::throw_null_pointer_exception(JavaThread*)
  • SharedRuntime::throw_NullPointerException(JavaThread*)
  • SharedRuntime::throw_NullPointerException_at_call(JavaThread*)

虽然异常可以从很多不同的抛出地方。

更好的方法是设置将在每个抛出的异常上调用的JVM TI回调。这是拦截异常的JVM TI代理的an example

这可能与JVM隐式空检查有关吗?是否有一些标志 禁用它们

这可能与此有关。隐式空值检查可能被-XX:-ImplicitNullChecks禁用,但该标志仅在JVM的调试版本中才可用。

+0

我仍然遇到'-XX:-ImplicitNullChecks'的崩溃。我只能在优化的OpenJDK构建中重现这一点。不会在调试或OracleJDK上发生。我已经设法通过稍微修改了'-XX:AbortVMOnException'来获得核心转储。在gdb中检查时,看起来像合法的NPE。我无法获得任何热点工具。最后放弃了,不再运行我的应用程序在这个确切的配置。 –