【问题标题】:Strategy for debugging surefire "The forked VM terminated without saying properly goodbye. VM crash or System.exit called ?"调试万无一失的策略“分叉的 VM 没有正确告别就终止了。VM 崩溃或 System.exit 被调用?”
【发布时间】:2014-01-05 17:14:00
【问题描述】:

我正在处理一个相当复杂的 java 项目,其中包含许多依赖项和许多单元测试。

我在 mac (mavericks) 上使用 java 1.6.0_65,maven 3.0.5 和 maven-surefire-plugin:2.16 在多个分支中运行。 我的问题是,使用多个 fork 运行此设置会导致 fork 退出:

“分叉的虚拟机没有正确告别就终止了。虚拟机崩溃或 System.exit 调用了吗?”

只用一个 fork 运行它不会产生问题(一切都通过了)

有一些关于这个问题的信息,包括this StackOverflow questionthis surefire bug(现在似乎已经解决了)

我知道这种情况的“答案”是找到我的代码中调用 System.exit() 的内容 - 我找不到任何东西。

或者是什么导致我的 JVM 崩溃 - 没有 hs_pid 崩溃报告。

我的问题是我可以使用什么样的策略来找到这个原因? 澄清一下,我对上面提到的答案不感兴趣,而是一种找到它的来源的方法。 (或者甚至更好地完全不同的答案来解决可能导致这种情况的原因)


我的 Surefire 配置是:(但我确实尝试了其他组合)

<parallel>classes</parallel>
<threadCount>1</threadCount>
<forkCount>1C</forkCount>
<reuseForks>false</reuseForks>
<useSystemClassLoader>false</useSystemClassLoader>
<useManifestOnlyJar>true</useManifestOnlyJar>
<useFile>true</useFile>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
<runOrder>alphabetical</runOrder>

更新 #1 使用 --debug (-X) 运行 maven 目标后添加相关输出

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.16:test (default-test) on project event-logger: ExecutionException; nested exception is java.util.concurrent.ExecutionException: java.lang.RuntimeException: The forked VM terminated without saying properly goodbye. VM crash or System.exit called ?
[ERROR] Command was/bin/sh -c cd /Users/nitzan/work/nitzan_5_parallel_tests/event-logger && /Library/Java/JavaVirtualMachines/1.6.0_65-b14-462.jdk/Contents/Home/bin/java org.apache.maven.surefire.booter.ForkedBooter /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire5107531798951225850tmp /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire_12561116468761732560tmp
[ERROR] -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.16:test (default-test) on project event-logger: ExecutionException; nested exception is java.util.concurrent.ExecutionException: java.lang.RuntimeException: The forked VM terminated without saying properly goodbye. VM crash or System.exit called ?
Command was/bin/sh -c cd /Users/nitzan/work/nitzan_5_parallel_tests/event-logger && /Library/Java/JavaVirtualMachines/1.6.0_65-b14-462.jdk/Contents/Home/bin/java org.apache.maven.surefire.booter.ForkedBooter /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire5107531798951225850tmp /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire_12561116468761732560tmp
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:213)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
    at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:47)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.apache.maven.plugin.MojoFailureException: ExecutionException; nested exception is java.util.concurrent.ExecutionException: java.lang.RuntimeException: The forked VM terminated without saying properly goodbye. VM crash or System.exit called ?
Command was/bin/sh -c cd /Users/nitzan/work/nitzan_5_parallel_tests/event-logger && /Library/Java/JavaVirtualMachines/1.6.0_65-b14-462.jdk/Contents/Home/bin/java org.apache.maven.surefire.booter.ForkedBooter /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire5107531798951225850tmp /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire_12561116468761732560tmp
    at org.apache.maven.plugin.surefire.SurefirePlugin.assertNoException(SurefirePlugin.java:198)
    at org.apache.maven.plugin.surefire.SurefirePlugin.handleSummary(SurefirePlugin.java:188)
    at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeAfterPreconditionsChecked(AbstractSurefireMojo.java:852)
    at org.apache.maven.plugin.surefire.AbstractSurefireMojo.execute(AbstractSurefireMojo.java:720)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
    ... 25 more
Caused by: org.apache.maven.surefire.booter.SurefireBooterForkException: ExecutionException; nested exception is java.util.concurrent.ExecutionException: java.lang.RuntimeException: The forked VM terminated without saying properly goodbye. VM crash or System.exit called ?
Command was/bin/sh -c cd /Users/nitzan/work/nitzan_5_parallel_tests/event-logger && /Library/Java/JavaVirtualMachines/1.6.0_65-b14-462.jdk/Contents/Home/bin/java org.apache.maven.surefire.booter.ForkedBooter /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire5107531798951225850tmp /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire_12561116468761732560tmp
    at org.apache.maven.plugin.surefire.booterclient.ForkStarter.runSuitesForkPerTestSet(ForkStarter.java:316)
    at org.apache.maven.plugin.surefire.booterclient.ForkStarter.run(ForkStarter.java:169)
    at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeProvider(AbstractSurefireMojo.java:958)
    at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeAfterPreconditionsChecked(AbstractSurefireMojo.java:822)
    ... 28 more
Caused by: java.util.concurrent.ExecutionException: java.lang.RuntimeException: The forked VM terminated without saying properly goodbye. VM crash or System.exit called ?
Command was/bin/sh -c cd /Users/nitzan/work/nitzan_5_parallel_tests/event-logger && /Library/Java/JavaVirtualMachines/1.6.0_65-b14-462.jdk/Contents/Home/bin/java org.apache.maven.surefire.booter.ForkedBooter /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire5107531798951225850tmp /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire_12561116468761732560tmp
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
    at java.util.concurrent.FutureTask.get(FutureTask.java:83)
    at org.apache.maven.plugin.surefire.booterclient.ForkStarter.runSuitesForkPerTestSet(ForkStarter.java:300)
    ... 31 more
Caused by: java.lang.RuntimeException: The forked VM terminated without saying properly goodbye. VM crash or System.exit called ?
Command was/bin/sh -c cd /Users/nitzan/work/nitzan_5_parallel_tests/event-logger && /Library/Java/JavaVirtualMachines/1.6.0_65-b14-462.jdk/Contents/Home/bin/java org.apache.maven.surefire.booter.ForkedBooter /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire5107531798951225850tmp /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire_12561116468761732560tmp
    at org.apache.maven.plugin.surefire.booterclient.ForkStarter.fork(ForkStarter.java:485)
    at org.apache.maven.plugin.surefire.booterclient.ForkStarter.fork(ForkStarter.java:352)
    at org.apache.maven.plugin.surefire.booterclient.ForkStarter.access$300(ForkStarter.java:85)
    at org.apache.maven.plugin.surefire.booterclient.ForkStarter$2.call(ForkStarter.java:288)
    at org.apache.maven.plugin.surefire.booterclient.ForkStarter$2.call(ForkStarter.java:283)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
    at java.lang.Thread.run(Thread.java:695)
[ERROR] 
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

【问题讨论】:

  • /Users/nitzan/work/nitzan_5_parallel_tests/event-logger/target/surefire/surefire_12561116468761732560tmp有什么有用的信息吗?
  • 该文件包含surefire 配置和surefire 传递给ForkedBooter 的类路径。隔离有点棘手,因为它在测试后被删除,但内容中似乎没有任何用处。
  • 存在一个影响所有 Surefire 2.x 插件的错误,该错误会阻止分叉的 VM 在安装了 OpenJDK 8u181 的所有 Linux 计算机上正常启动。另见bugs.debian.org/cgi-bin/bugreport.cgi?bug=911925issues.apache.org/jira/browse/SUREFIRE-1588
  • 谢谢@THelper,第一张票的其中一个解决方法对我有用:false

标签: java maven maven-surefire-plugin


【解决方案1】:

步骤:

(1) 使用 -e-X 选项运行 mvn 以获取更多调试信息。

(2) 在输出中查找“错误”。就我而言,当我运行 mvn 命令时,部分输出包括:

[ERROR] Command wascmd.exe /X /C "C:\dev\dev-tools\.....

(3)直接在命令shell中执行有问题的命令。

就我而言,执行

cmd.exe /X /C "C:\dev\dev-tools\....

从命令行导致 OutOfMemoryError。

【讨论】:

  • 这没有回答问题,问题要求提供一种调试策略“分叉的虚拟机在没有正确告别的情况下终止”。这可能有很多可能的原因 -> OutOfMemoryError 只是此问题的众多选项之一。
  • @NitzanVolman:我非常不同意你的评论。虽然 kc2001 的答案很简洁,但它对我有用,而且它一个通用策略。简而言之,策略是:(1)使用-e-X选项重新运行mvn; (2) 查找失败的命令; (3) 重新运行该命令。如果输出仍然不足,请考虑使用-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5001 运行命令,然后连接远程 JVM 调试器(您选择的 IDE 可以执行此操作)。更大的问题:为什么 mvn 的输出在这些情况下如此无用?
  • 按照 Kevin 的建议,我更新了答案,使步骤更加明确。
  • 这个策略对我有用。就我而言,我在终端输出中找到了[ERROR] . . . Failed to get user permissions from end-point: https://[mycompanyurl]/users。我意识到我没有登录到我的公司 VPN。登录 VPN 解决了​​错误。
【解决方案2】:

在我的情况下,maven-surefire-plugin 的配置 forkedProcessExitTimeoutInSeconds 会有所帮助。默认值为自 maven-surefire-plugin:2.20.1 30 秒。我的项目进行了非常耗时的测试,因此分叉的 JVM 崩溃了。 使用以下属性在 pom 中配置插件即可解决问题。

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
                <forkedProcessExitTimeoutInSeconds>120</forkedProcessExitTimeoutInSeconds>
        </configuration>
</plugin>

【讨论】:

  • 结合这篇文章有意义:stackoverflow.com/a/51099769/5773994 对我来说,120 还不够高,但是嘿,这是一个你不想遇到的超时值。所以我把它设置为1200。看起来很多。并且没有单元测试运行那么长时间。实际上附近没有。我们总共在大约 4 分钟内完成了 1000 多项测试。所以我认为 120 应该可以工作,
【解决方案3】:

在这里写下我用来帮助其他遇到此问题的人的策略。

当调用 System.exit() 时,可以利用 SecurityManager 抛出异常。然后,您可以检查堆栈跟踪以确切了解谁调用了 exit()。 如果退出的调用是从隐藏在您所依赖的 jar 中的代码而不是您自己的代码发出的,这将特别有用。

private static void forbidSystemExitCall() {
    final SecurityManager securityManager = new SecurityManager() {
        public void checkPermission( Permission permission ) {
            if( permission.getName().startsWith("exitVM") ) {
                throw new RuntimeException("Something called exit ") ;
            }
        }
    } ;
    System.setSecurityManager( securityManager ) ;
}  

【讨论】:

  • 对于线程每类策略,我们可以在此代码上添加@BeforClass,并将其放置在一个公共子测试类中。有什么线索可以在与 Surefire 相同的 JVM 中运行此代码以进行其他加载策略?
  • 我不明白你的问题。您是否打算将 @BeforeClass 作为主要 maven 构建的一部分运行,而不是形成肯定的分支?如果是这样的话,我认为这是不可能的。
  • 如何为所有测试安装它?就我而言,约 1000 个随机测试失败。
【解决方案4】:

如果有人包含自定义 argLine 参数,您必须重新考虑,因为这很可能是您的内存分配问题的根源。

例如(我曾经有过):

<argLine>XX:MaxPermSize=4096m ${argLine}</argLine>

现在我使用硬指定值:

<argLine>-Xmx1024m -XX:MaxPermSize=256m</argLine>

无论出于何种原因,与 Surefire 集成的应用程序(例如 Jacoco)都不会请求足够的内存来与构建时发生的测试共存。

(更多信息也可以在这个s.o.问题(1)中找到)

(1)-maven jacoco: not generating code coverage report

【讨论】:

    【解决方案5】:

    按照 Maven Surefire 文档,您可以在 debug mode 中执行分叉的 VM,如果它总是失败的话。然后你可以调试你的代码直到它退出。

    【讨论】:

    • 如果问题是可预测的,这是一个好方法。如果问题随机发生 -> 这种方法不实用
    【解决方案6】:

    FWIW,当 JVM 在 maven 构建期间内存不足时,我遇到了这个错误。在 linux 上,这是由 OOM 杀手检测到的,它最终生成了内核消息,例如 Aug 28 20:53:27 ip-xxx-xxx-xxx-xxx kernel: [248686.775455] java invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0.

    我猜在 Mac 上你只想用 ActivityMonitor 监控你的内存使用情况。

    【讨论】:

      【解决方案7】:

      您可能要检查的是在您的 pom.xml 中的 build/pluging/plugin 配置中为 surefire 或 failsafe 设置的 argline 设置。我在那里有一些不正确的东西导致分叉的虚拟机失败(具有讽刺意味的是,我把 maven.failsafe.debug 放在那里帮助调试早期的分叉崩溃)。

      【讨论】:

      • 在我的情况下,没有为故障安全插件设置 argline。
      【解决方案8】:

      可以提供帮助的 JVM 选项:

      -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=c:/dumps/
      

      注意:您可以使用正斜杠。
      注意2:确保文件夹存在并且进程有写权限。在最近的 Windows 系统上,C:\ 是写保护的。
      注意 3:确保您有足够的可用空间来写入转储。 Java 9 文档中提到系统的临时文件夹将在磁盘已满时使用。

      如果没有转储文件,那么你的 JVM 没有耗尽内存。

      下一个选项是-XX:ErrorFile=,它允许 JVM 记录致命错误。

      -XX:+ShowMessageBoxOnError 会在 JVM 崩溃时显示错误对话框。

      注意:您可以使用 jinfo 命令更改正在运行的 JVM 的标志。

      通过 argLine 选项将这些选项传递给 Maven Surefire:

      <build>
          <plugins>
              <plugin>
                  <artifactId>maven-surefire-plugin</artifactId>
                  <configuration>
                      <!-- -XX:HeapDumpPath=C:\ -XX:+ShowMessageBoxOnError  -->
                      <argLine>@{argLine} -XX:+HeapDumpOnOutOfMemoryError -Xmx1g -XX:HeapDumpPath=H:/dumps/ -XX:ErrorFile=H:/dumps/ -XX:+ShowMessageBoxOnError</argLine>
                  </configuration>
              </plugin>
          </plugins>
      </build>
      

      开头奇怪的@{argLine} 允许像Jacoco 这样的其他插件注入他们的选项。为此,您需要添加一个空属性:

      <properties>
          <argLine></argLine> <!-- Fallback when Jacoco isn't active. -->
      </properties>
      

      您可以在错误发生时验证它是否有效:Maven 将转储用于启动分叉 JVM 的整个命令行。

      【讨论】:

        【解决方案9】:

        更改插件的配置应该可以解决问题:

        <project>
          [...]
          <build>
            <plugins>
              <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M1</version>
                <configuration>
                  <useSystemClassLoader>false</useSystemClassLoader>
                </configuration>
              </plugin>
            </plugins>
          </build>
          [...]
        </project>
        

        正如这个post 和插件的主要documentation 中所建议的那样。

        【讨论】:

          【解决方案10】:

          如果您使用的是 Visual Studio Code

          我没有对 pom.xml 文件进行任何更改或更新任何依赖版本
          将此行添加到 Visual Studio Code 的 settings.json 即可解决问题。

          "maven.executable.options": "-DforkCount=0",
          

          【讨论】:

            【解决方案11】:

            考虑将jacoco-maven-plugin 更新到 0.8.3

            例如

            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.8.3</version>
            <configuration>
            <excludes>
            <exclude>**/schemas/**/*</exclude>
            </excludes>
            <destFile>${sonar.jacoco.reportPaths}</destFile>
            </configuration>
            

            【讨论】:

              【解决方案12】:

              我只是删除了所有 maven 存储库并运行 maven clean install。然后问题就解决了。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2014-06-09
                • 1970-01-01
                • 1970-01-01
                • 2010-10-24
                • 1970-01-01
                相关资源
                最近更新 更多