【问题标题】:how to generate thread dump java on out of memory errorjava - 如何在内存不足错误时生成线程转储java
【发布时间】:2011-02-16 19:04:45
【问题描述】:
  • 除了堆转储(java_pid14941.hprof)之外,java 6 是否生成线程转储

  • 这就是我的一个应用程序发生的情况。

    java.lang.OutOfMemoryError: 超出 GC 开销限制 将堆转储到 java_pid14941.hprof ...

  • 我确实在工作目录中找到了 ava_pid14941.hprof,但没有找到任何包含线程转储的文件。当我收到 OutOfMemory 错误时,我需要知道所有线程在做什么。

  • 除了内存不足异常的堆转储之外,是否有任何配置选项会生成线程转储?

【问题讨论】:

    标签: java crash-dumps jvm-crash


    【解决方案1】:

    如果您在 Linux/Unix 环境中,您可以这样做:

    -XX:OnOutOfMemoryError="kill -3 %p"
    

    这样您就不必让您的应用程序定期生成线程转储,并且您会在它实际阻塞时获得快照。

    使用%p,不需要传递PID,JVM会自动选择正确的进程ID为mentioned here

    【讨论】:

    • 实际上,您可以将 OOME 上生成的 Heap Dump 加载到 VisualVM 中,然后单击“堆转储处的线程”部分标题下的“显示线程”链接。
    • -XX:OnOutOfMemoryError="kill -3 %p" - 而且您不需要手动解析和指定 pid (forums.oracle.com/forums/…)
    • 至于“-XX:OnOutOfMemoryError="kill -3 %p”我可以把线程转储到指定的日志文件吗?
    【解决方案2】:

    如何在java上生成线程转储 内存不足错误?

    您的问题可以简化为:

    • 如何生成线程转储

    和:

    • 如何捕捉内存不足错误(不要在这里注意反对者,他们错过了大局,请参阅我的评论)

    其实很简单,你可以这样做:

    • 安装默认的未捕获异常处理程序

    • 在捕获未捕获的异常时,检查是否有 OutOfMemoryError

    • 如果您遇到 OutOfMemoryError,请为自己生成一个完整的线程转储,并要求用户通过电子邮件将其发送给您或主动发送它

    奖励:它在 1.5 上也可以正常工作 :)

     Thread.setDefaultUncaughtExceptionHandler( new Thread.UncaughtExceptionHandler() {
         public void uncaughtException( final Thread t, final Throwable e ) {
             ...
         }
    

    你可能想看看这个:

     e.getMessage();
    

    还有这个:

    Thread.getAllStackTraces();
    

    我一直在一个应用程序中这样做,该应用程序在数百个不同的 1.5 和 1.6 JVM(在不同的操作系统上)上提供。

    【讨论】:

    • 请注意,毫无头绪的评论者/反对者可能会提出诸如 “你永远不应该遇到 OOM” 之类的无意义的言论,但他们可能从未在 Real-World [TM ] Java 应用程序部署在数百个系统上。我一直这样做并且它确实有效。这使我能够远程调试和修复许多在测试期间从未出现过的非常微妙的内存泄漏。在这里捕获 OOM 非常有意义因为关键是要了解 OOM 发生的原因。但是不要惊讶地看到这里有很多反对者不理解这个非常基本的事实
    • 基于这个答案,我创建了实用程序类 UncaughtExceptionLogger (pastebin.com/e30g9y66)。这个你可以定义,比如说,一个 Spring bean,并且你都设置了扩展日志记录。
    【解决方案3】:

    当使用 jstack 触发 OnOutOfMemoryError 时,可能会触发线程转储。例如:-

    jstack -F pid > /var/tmp/<identifier>.dump
    

    【讨论】:

      【解决方案4】:

      根据接受的答案,我创建了实用程序类。这个你可以定义为一个 Spring bean,并且你已经设置好了扩展日志记录。

      import java.util.Iterator;
      import java.util.Map;
      
      import javax.annotation.PostConstruct;
      
      import org.apache.commons.lang3.exception.ExceptionUtils;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      public class UncaughtExceptionLogger {
      
          private final static Logger logger = LoggerFactory.getLogger(UncaughtExceptionLogger.class);
      
          @PostConstruct
          private void init() {
              Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                  public void uncaughtException(final Thread t, final Throwable e) {
                      String msg = ExceptionUtils.getRootCauseMessage(e);
                      logger.error(String.format("Uncaght exception handler captured expcetion '%s'", msg), e);
                      if (msg.contains("unable to create new native thread")) {
                          String dump = captureThreadDump();
                          logger.error(String.format(
                                  "OutOfMemoryError has been captured for threads limit. Thread dump: \n %s", dump), e);
                      }
                      if (ExceptionUtils.getRootCause(e) instanceof OutOfMemoryError) {
                          String dump = captureThreadDump();
                          logger.error(String.format("OutOfMemoryError has been captured. Thread dump: \n %s", dump), e);
                      }
                  }
              });
          }
      
          public static String captureThreadDump() {
              /**
               * http://stackoverflow.com/questions/2787976/how-to-generate-thread-
               * dump-java-on-out-of-memory-error
               * http://henryranch.net/software/capturing-a-thread-dump-in-java/
               */
              Map<Thread, StackTraceElement[]> allThreads = Thread.getAllStackTraces();
              Iterator<Thread> iterator = allThreads.keySet().iterator();
              StringBuffer stringBuffer = new StringBuffer();
              while (iterator.hasNext()) {
                  Thread key = (Thread) iterator.next();
                  StackTraceElement[] trace = (StackTraceElement[]) allThreads.get(key);
                  stringBuffer.append(key + "\r\n");
                  for (int i = 0; i < trace.length; i++) {
                      stringBuffer.append(" " + trace[i] + "\r\n");
                  }
                  stringBuffer.append("");
              }
              return stringBuffer.toString();
          }
      }
      

      【讨论】:

        【解决方案5】:

        -XX:OnOutOfMemoryError="kill -3 %p"

        遗憾的是,采用线程转储的 JVM 参数不起作用。 子进程无法结束对父进程的 SIGQUIT。

        Oracle 有 -XX:CrashOnOutOfMemoryError,但这在 Java 8 上可用。

        【讨论】:

          【解决方案6】:

          我认为 java 中没有任何东西可以为您提供退出线程转储。必要时我会通过定期执行kill -3 pid 的 cronjob 来解决这个问题。是的,它确实使日志有点混乱,但足迹仍然可以忽略不计。

          如果您正遭受 OOM 的困扰,了解情况如何逐线程发展可能会有所帮助。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-02-28
            • 2019-12-16
            • 2013-06-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多