JVM 在尝试获取信号量时卡住

JVM is stuck trying to acquire a semaphore

我有一个使用嵌入式 Jetty(版本 9.3.6.v20151106)和 JDK8u65 的应用程序。

当我在 Mac 或 Linux 上使用此应用程序时,我没有任何困难。然而,在 Windows 上,Jetty 没有启动并且应用程序永久挂起。

我 运行 对进程执行 jstack 命令并隔离了阻止服务器启动的线程。

java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x1a3b2018> (a java.util.concurrent.Semaphore$NonfairSync)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)
    at java.util.concurrent.Semaphore.acquire(Semaphore.java:312)
    at org.eclipse.jetty.annotations.AnnotationConfiguration.scanForAnnotations(AnnotationConfiguration.java:540)
    at org.eclipse.jetty.annotations.AnnotationConfiguration.configure(AnnotationConfiguration.java:447)
    at org.eclipse.jetty.webapp.WebAppContext.configure(WebAppContext.java:491)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1336)

我寻找另一个持有锁的线程,但有 none。

如何进一步调试这个问题?是 JVM 错误还是 Windows 安全功能?非常感谢任何帮助。

windows 似乎没有正确报告处理器数量。 尝试将 org.eclipse.jetty.annotations.multiThreaded 属性 设置为 false,看看是否有帮助。

原来我对自己的日志视而不见。我发现我在扫描应用程序的 jar 时抛出了 OOM。 有很多因素可以解释为什么这种情况特别发生在 Windows 上。首先,这是一个 32 位的发行版,因此在内存方面有点反复无常。 Java 的最大堆大小最终会受到影响。

正如在 #jetty IRC 频道上讨论的那样(感谢 joakime 的出色帮助),这是由于应用程序 WEB-INF/lib 的 jar 扫描。如果你有装满垃圾文件的大罐子,这会增加启动时间并可能导致 OOM。

joakime 指出了解决问题的两种可能性:

  • 预先打包并准备更多应用程序并使用quickstart
  • 使用正则表达式有意扫描 jar 以选择要扫描的 jar(此 page 的底部)

我最终使用了后者。我使用此正则表达式来匹配 jstl jar 和 spring jar(我使用 spring security 的 taglibs jar):

".*/jstl-[^/]*\.jar$|.*/spring-[^/]*\.jar$"

I looked for another thread holding up the lock, but there were none.

信号量比锁更通用。这是一堆许可证,不一定与任何事物相关联。它们可以由一个或多个线程获取和释放,并且不需要在完成获取的线程上发生释放。

一个线程甚至可以创建一个信号量,消耗所有可用的许可,然后等待它自己的信号量而无需另一个线程接触它。

因此无法通过寻找其他线程轻松地调试信号量"holding a lock"。

要调试它们,您必须跟踪获取和释放并查看许可证在何处耗尽,然后找出应该释放但未释放的位置。