6

问题描述

在运行我的 Java 服务器应用程序一段时间后,我在 Solaris 上遇到了 Oracle Java 虚拟机的奇怪行为。通常,当hs_err_pid.log创建 jvm 文件崩溃时(位置由-XX:ErrorFilejvm 参数确定,如下所述:如何抑制 hs_err_pid 文件的创建?

但在我的情况下,没有创建文件,唯一剩下的就是core核心转储文件。

使用标准 Solaris 工具pstackpflags我能够从文件中收集有关崩溃的更多信息(包括在下面)core

尝试过的解决方案

  • 试图hs_err_pid.log在文件系统中查找所有文件,但找不到任何文件(即使在应用程序工作目录之外)。IE:

    find / -name "hs_err_pid*"

  • 我试图找到与 jvm 相关的 jvm 错误,但我找不到与此案例类似的有趣内容。

  • 问题看起来有点类似于:Java VM: reproducable SIGSEGV on both 1.6.0_17 and 1.6.0_18, how to report?但我仍然无法确认这一点,因为hs_err_pid.log文件丢失了,当然操作系统平台也不同。
  • (编辑)正如分析 java 核心转储问题的工具的答案之一所建议的,我已经使用Eclipse MAT从core文件中提取了堆转储并对其进行了分析。jmap我发现了一个泄漏(在核心转储 1,4 M 个元素时,添加到 HashMap 的元素永远不会被清理)。然而,这并没有解释为什么hs_err_pid.log没有生成文件,也没有解释 jvm 崩溃的原因。
  • (EDIT2) 正如Darryl Miles所建议的, -Xmx 限制已被检查(测试包含无限期地将对象添加到 a 的代码LinkedList):
    • java -Xmx1444m Test结果与java.lang.OutOfMemoryError: Java heap space,
    • java -Xmx2048m Test结果与java.lang.OutOfMemoryError: Java heap space,
    • java -Xmx3600m Test结果与核心转储。

问题

有没有人遇到过类似的 jvm 问题以及如何在这种情况下继续查找实际发生的情况(即在什么情况下核心从 jvm 中转储并且没有hs_err_pid.log创建文件)?

任何解决此问题的提示或指针都会非常有帮助。

提取的标志

# pflags core
...
/2139095:      flags = DETACH
    sigmask = 0xfffffeff,0x0000ffff  cursig = SIGSEGV

提取堆栈

# pstack core
...
-----------------  lwp# 2139095 / thread# 2139095  --------------------
 fb208c3e ???????? (f25daee0, f25daec8, 74233960, 776e3caa, 74233998, 776e64f0)
 fb20308d ???????? (0, 1, f25db030, f25daee0, f25daec8, 7423399c)
 fb20308d ???????? (0, 0, 50, f25da798, f25daec8, f25daec8)
 fb20308d ???????? (0, 0, 50, f25da798, 8561cbb8, f25da988)
 fb203403 ???????? (f25da988, 74233a48, 787edef5, 74233a74, 787ee8a0, 0)
 fb20308d ???????? (0, f25da988, 74233a78, 76e2facf, 74233aa0, 76e78f70)
 fb203569 ???????? (f25da9b0, 8b5b400, 8975278, 1f80, fecd6000, 1)
 fb200347 ???????? (74233af0, 74233d48, a, 76e2fae0, fb208f60, 74233c58)
 fe6f4b0b __1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_ (74233d44, 74233bc8, 74233c54, 8b5b400) + 1a3
 fe6f4db3 __1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_ (fe6f4968, 74233d44, 74233bc8, 74233c54, 8b5b4
00) + 27
 fe6f4deb __1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_ (74233d44, 8975278, 74233c54, 8b5b400) + 2f
 fe76826d __1cJJavaCallsMcall_virtual6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_ (74233d44, 897526c, fed2d464, fed2d6d0, 7
4233c54, 8b5b400) + c1
 fe76f4fa __1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_ (74233d44, 8975268, 897526c, fed2d464, fed2d6d0, 8b5b
400) + 7e
 fe7805f6 __1cMthread_entry6FpnKJavaThread_pnGThread__v_ (8b5b400, 8b5b400) + d2
 fe77cbe4 __1cKJavaThreadRthread_main_inner6M_v_ (8b5b400) + 4c
 fe77cb8e __1cKJavaThreadDrun6M_v_ (8b5b400) + 182
 feadbd59 java_start (8b5b400) + f9
 feed59a9 _thr_setup (745c5200) + 4e
 feed5c90 _lwp_start (745c5200, 0, 0, 74233ff8, feed5c90, 745c5200)

系统信息:

# uname -a
SunOS xxxx 5.10 Generic_137138-09 i86pc i386 i86pc
# java -version
java version "1.6.0_11"
Java(TM) SE Runtime Environment (build 1.6.0_11-b03)
Java HotSpot(TM) Server VM (build 11.0-b16, mixed mode)
# ulimit -a
time(seconds) unlimited
file(blocks) unlimited
data(kbytes) unlimited
stack(kbytes) 10240
coredump(blocks) unlimited
nofiles(descriptors) 256
memory(kbytes) unlimited

使用的 jvm 参数:

java -Xms1024M -Xmx2048M -verbose:gc -Xloggc:logs/gc.log -server com.example.MyApplication

如果您发现缺少某些信息,请发表评论,我会尝试添加它们。

4

2 回答 2

3

6.0_11 已经很老了,我最近没有经验,真的建议在那里升级...

但是,在本机代码中,stackoverflow 可能不会发生崩溃转储,即以非常低的堆栈调用一些本机函数(如 FileOutputStream 的写入,套接字使用相同的 impl)。因此,即使 JVM 尝试写入文件,也没有足够的堆栈,写入代码也会崩溃。第二个 stackoverflow 只是解决了这个过程。

我在生产系统上确实有类似的情况(没有创建文件),跟踪它并不好,但上面解释了原因。

于 2011-10-04T23:42:27.333 回答
3

根据我上面的评论。我相信这个问题是因为设置了太高的 -Xmx 值而导致 32 位地址空间中的可用堆用完。这迫使内核在 JVM 可以监管它(通过使用受控的 OutOfMemoryException 机制)之前监管该限制(通过拒绝对新内存的请求)。不幸的是,我不知道 Intel Solaris 的具体情况,也不知道该平台会发生什么。

但作为 Windows 的一般规则,最大 -Xmx 可能为 1800M,然后您创建的每个附加应用程序线程将其减少 16M。由于每个线程都需要堆栈空间(本机堆栈和 Java 堆栈)以及其他每个线程的会计事项,例如线程本地存储等......这个计算的结果应该为您提供 Java VM 的实际可用堆空间的近似值在其操作系统使用 2G/2G 拆分(用户/内核)的任何 32 位进程上。

WinXP 及更高版本可以在内核上使用 /3G 开关来获得更高的拆分(3G/1G 用户/内核),Linux 有一个 /proc/<pid>/map 文件可以让您准确查看进程地址空间是从给定进程中布局的(如果您正在运行此应用程序,您可以随着时间的推移观察 [heap] 增长以满足用于 .text/.rodata/.data/etc 的共享文件映射......来自 DSO导致内核拒绝增加堆的请求。

这个问题在 64 位上消失了,因为有更多的地址空间可供使用,并且在堆遇到其他映射之前,您将用完物理和虚拟(交换)内存。

我相信 Solaris 上的“truss”会在核心转储前不久显示返回错误代码的 brk/sbrk 系统调用。部分标准本机库被编码为从不检查新内存请求的返回代码,因此可以预期崩溃。

于 2011-10-07T20:25:31.380 回答