【问题标题】:How to detect the cause of OutofMemoryError?如何检测 OutofMemoryError 的原因?
【发布时间】:2012-07-25 19:58:30
【问题描述】:

我抱怨我的服务器应用程序在高负载时崩溃。
这是一个在Tomcat 5 中运行的网络应用程序。
我看到了线程转储,并且看到了 OutOfMemory 错误

1TISIGINFO 转储事件“systhrow”(00040000) 详细信息
"java/lang/OutOfMemoryError" "创建线程失败: retVal -1073741830, errno 12" >received 1TIDATETIME Date: 2012/07/17 at 20:03:17 1TIFILENAME >Javacore filename:C:\ServerApplication\Tomcat5\bin\ javacore.34545719.4646464.4172.0003.txt

堆信息如下:

Maximum Java heap size : 1500m    
Initial Java heap size : 256m

这是初始和最大堆大小的配置(32 位 java)

我还看到有可用的可用堆空间

1STHEAPFREE    Bytes of Heap Space Free: 2D19F3C0   
1STHEAPALLOC   Bytes of Heap Space Allocated: 5DC00000

这是大约 750MB 的可用空间,对吧?

从线程方法分析我看到线程数是695,其中49%java/lang/Object.wait(Native Method)39%sun/misc/Unsafe.park(Native Method)
我也看到这个NO JAVA STACK 1% 不确定这是什么意思。
还有 0 个胎面死锁,2% 是可运行的。

我不确定如何解释此信息或如何从这里继续检测根本原因。
对此有什么帮助吗?

【问题讨论】:

  • ""java/lang/OutOfMemoryError" "Failed to create thread: retVal -1073741830, errno 12" >" 那一段非常重要,调用windows库失败,你需要去看看在 Microsoft 参考(MSDN 等)中查找这些代码,看看它们的含义。
  • @user439407:谢谢!您指的是retValerrno,您建议我应该查一下吗?我应该去哪里看?
  • 两者都有,但 errno 可能会提供更多信息(retval 可能只是表示失败,可能是什么类型的失败,不确定,我没有使用 Windows 的经验)
  • 虽然不一定能回答您的问题,但您是否尝试过在 Tomcat 中使用 NIO 或 APR 连接器而不是默认连接器?这样你就不会为每个连接产生一个新线程。

标签: java multithreading debugging tomcat out-of-memory


【解决方案1】:

修复

遵循 IBM 技术说明 java.lang.OutOfMemoryError while creating new threads,特别是 'ulimit' 命令来增加默认值 1024。

症状

[2/25/15 12:47:34:629 EST] 00000049 SystemErr     R java.lang.OutOfMemoryError: Failed to create a thread: retVal -1073741830, errno 11
[2/25/15 12:47:34:630 EST] 00000049 SystemErr     R     at java.lang.Thread.startImpl(Native Method)
[2/25/15 12:47:34:630 EST] 00000049 SystemErr     R     at java.lang.Thread.start(Thread.java:936)
[2/25/15 12:47:34:630 EST] 00000049 SystemErr     R     at org.eclipse.osgi.framework.internal.core.InternalSystemBundle.stop(InternalSystemBundle.java:251)
[2/25/15 12:47:34:630 EST] 00000049 SystemErr     R     at com.ibm.ws.runtime.component.RuntimeBundleActivator.shutdownEclipse(RuntimeBundleActivator.java:54)
[2/25/15 12:47:34:630 EST] 00000049 SystemErr     R     at com.ibm.ws.runtime.component.ServerCollaborator$ShutdownHook$1.run(ServerCollaborator.java:878)
[2/25/15 12:47:34:630 EST] 00000049 SystemErr     R     at com.ibm.ws.security.auth.ContextManagerImpl.runAs(ContextManagerImpl.java:5459)
[2/25/15 12:47:34:631 EST] 00000049 SystemErr     R     at com.ibm.ws.security.auth.ContextManagerImpl.runAsSystem(ContextManagerImpl.java:5585)
[2/25/15 12:47:34:631 EST] 00000049 SystemErr     R     at com.ibm.ws.runtime.component.ServerCollaborator$ShutdownHook.run(ServerCollaborator.java:850)
[2/25/15 12:47:34:631 EST] 00000049 SystemErr     R     at com.ibm.ws.runtime.component.ServerCollaborator$StopAction.alarm(ServerCollaborator.java:809)
[2/25/15 12:47:34:631 EST] 00000049 SystemErr     R     at com.ibm.ejs.util.am._Alarm.run(_Alarm.java:133)
[2/25/15 12:47:34:631 EST] 00000049 SystemErr     R     at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1815)

环境

CentOS 6.6 64 位 IBM WAS 8.5.0.2 64 位

参考

【讨论】:

    【解决方案2】:

    类似的消息on IBM WebSphere 显示了这一行

    “创建线程失败:retVal”

    表示 本机 OOM,这意味着(进程的)某个线程正在尝试请求堆上的大部分内存。

    上面的 IBM 链接包含一系列步骤 - 其中一些是 IBM 特定的。看看吧。

    从本机内存使用的角度来看:

    • 最大 Java 堆设置
    • JDBC 驱动程序
    • JNI 代码或本机库
    • 未使用类的垃圾收集。确保未设置 -Xnoclassgc。
    • 线程池设置(固定大小的线程池)
    • 类加载器等太多,但这些并不常见。
    • 来自 javacores 的类/类加载器的数量。

    您可以查看的另一件事是 PermGenSpace - 它有多大?

    这个链接http://www.codingthearchitecture.com/2008/01/14/jvm_lies_the_outofmemory_myth.html建议

    增加堆分配实际上加剧了这个问题!它 减少编译器和其他原生组件的空间 和玩。所以我的问题的解决方案是: 1.减少分配给JVM的堆。 2. 消除原生对象未及时释放造成的内存泄漏。

    您是否还在 server.xml 中为 maxThreads 配置了一个值?默认值为 200,但您的应用似乎有 695?

    【讨论】:

    • 是的,我在 server.xml 中使用了 500 线程池。这个数字是不是太大了?
    • 您可以尝试减少 - 当您允许 500 个请求线程进入系统时,每个请求线程都需要它自己的文件处理程序等,这会占用内存。话虽如此 - 没有。请求线程的数量与进程中的 695 个应用程序线程不同 - beny23 的答案更好
    【解决方案3】:

    根据this post

    java.lang.OutOfMemoryError 有两个可能的原因: 创建线程消息失败:

    • 运行的线程过多,系统内部资源不足,无法创建新线程。
    • 系统已用完用于新线程的本机内存。线程需要用于内部 JVM 结构的本机内存,即 Java™ 堆栈和本机堆栈。

    所以这个错误很可能与内存完全无关,只是创建了太多线程......

    编辑:

    由于您有 695 个线程,因此您需要 695 倍的堆栈大小作为内存。考虑到this post 的线程限制,我怀疑您试图为可用的虚拟内存空间创建过多的线程。

    【讨论】:

      【解决方案4】:

      我明白你的意思,找个地方开始可能会让人困惑。

      看看Eclipse Memory Analyzer (MAT)。它将使用 JHat 将程序的内存快照转储到文件中,您可以重新打开和分析该文件。

      该文件的浏览器非常整齐地概述了程序创建的所有对象,您可以查看各个级别以查找是否有可疑之处。


      附加我的 cmets 来回答...

      当您的 executable webapp 崩溃时,将其转储到 MAT。 MAT 会多次告诉您正在创建什么对象。如果它是自定义对象,而且通常是,那么很容易找到。如果没有,您可以看到它的父级,将其截肢,然后从那里运球(对于图形示例,我目前并不完全专注于 SO :)。

      哦,我忘了说,你可以在几个条件下多次运行程序,每次都做一个转储。然后,您可以分析每个转储的趋势。


      但就我而言,我应该使用什么?我有一个在 Tomcat 中运行的网络应用程序

      对不起,我也错过了。如果我没记错的话,MAT 会转储 JVM 进程,因此只要 VM 在您的机器上运行,您就可以转储其进程并查看发生了什么。


      另一个评论变成了部分解决方案......

      这变得比实际更困难。说真的,这很容易,只要你运行 MAT 一次或两次就能掌握窍门。运行您的应用程序,直到事情崩溃。倾倒它。改变些什么。运行,崩溃,转储。重复。然后,打开 MAT 中的转储,并比较可疑的内容。

      当我学习这一点时,最棘手的部分是找到要转储的 process ID - 这还不算太让人麻木。

      【讨论】:

      • 好的。我也可以试试这个。但我已经在使用分析工具,实际上OP的数据实际上是该工具帮助我找到的数据。我只是不知道如何使用他们找到根本原因
      • 我看到 jmap 需要一个可执行文件来映射内存段。但在我的情况下,我应该使用什么?我有一个在 Tomcat 中运行的网络应用程序。
      • 制作了一些 cmets,但最好将它们附加到答案中。
      • 问题是我不能在这里重现这个。你是说我应该重现这个然后连接到进程来收集转储?
      • 那么这些问题只能动态解决吗?通过重现错误?通过查看日志无法“静态地”找到问题?
      【解决方案5】:

      您应该使用-XX:+HeapDumpOnOutOfMemoryError 标志启动JVM。这将在生成OutOfMemoryError 时产生堆转储。

      然后,正如@Steve 所说,您可以使用 MAT 之类的工具来分析转储并查看分配了哪些对象,以及谁在保留对它们的引用。这通常会让您了解 JVM 耗尽内存的原因。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-07-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-12
        • 2014-02-06
        相关资源
        最近更新 更多