4

我们的生产服务器上有一个堆大小为几 GB 的 Java EE 应用程序。每隔一段时间,我们的任何一个服务器都不再对任何请求做出反应。

  • 当问题发生时,GC 日志表明服务器花费大量/大部分时间运行 GC,这需要 8 到 10 秒(通常它们需要不到 1 秒)。
  • 我们永远不会得到任何 OutOfMemoryErrors。
  • 当堆达到某个堆大小时,问题不会特别发生 - 事实上,它发生在不同的堆大小时,它们都没有接近配置的最大值。
  • 该问题不会在某个时间间隔、某个时间、用户负载或特定服务器节点上发生。它似乎完全随机。
  • 堆转储,即使是在显示问题时从服务器获取的转储,也没有显示任何明显错误的内容。
  • 每天重新启动生产服务器似乎可以降低问题发生的可能性,但并不能解决问题。
  • 如果我们不每天重新启动服务器,那么问题很可能会在一到三天内出现在我们的 8 台生产服务器中的一台上。

您将如何开始诊断?

配置

我们的 JAVA_OPTS 如下: -Xms8096m -Xmx8096m -XX:MaxPermSize=512M -Dsun.rmi.dgc.client.gcInterval=1800000 -Dsun.rmi.dgc.server.gcInterval=1800000 -XX:NewSize=150M -XX:+UseParNewGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/path/to/gc.log

$ java -version
java version "1.6.0_12"
Java(TM) SE Runtime Environment (build 1.6.0_12-b04)
Java HotSpot(TM) 64-Bit Server VM (build 11.2-b01, mixed mode)

$ uname -a
Linux myhostname 2.6.18-274.3.1.el5 #1 SMP Tue Sep 6 20:13:52 EDT 2011 x86_64 x86_64 x86_64 GNU/Linux

$ cat /proc/version
Linux version 2.6.18-274.3.1.el5 (mockbuild@builder10.centos.org) (gcc version 4.1.2 20080704 (Red Hat 4.1.2-51)) #1 SMP Tue Sep 6 20:13:52 EDT 2011

$ cat /etc/issue
CentOS release 5.7 (Final)
Kernel \r on an \m

$ cat /proc/meminfo|grep "MemTotal"
MemTotal:     16279356 kB

垃圾回收日志

这是问题发生时的示例 GC 日志片段:

111036.554: [GC 111036.555: [ParNew: 2210816K->2210816K(2487104K), 0.0000090 secs]111036.555: [Tenured: 3629252K->3647971K(5526912K), 8.7565190 secs] 5840068K->3647971K(8014016K), 8.7567840 secs]
111055.691: [GC 111055.691: [ParNew: 2210816K->2210816K(2487104K), 0.0000090 secs]111055.691: [Tenured: 3647971K->3667529K(5526912K), 8.7876340 secs] 5858787K->3667529K(8014016K), 8.7878690 secs]
111071.037: [GC 111071.037: [ParNew: 2210816K->2210816K(2487104K), 0.0000090 secs]111071.037: [Tenured: 3667529K->3692057K(5526912K), 8.7581830 secs] 5878345K->3692057K(8014016K), 8.7584210 secs]
111088.407: [GC 111088.407: [ParNew: 2210816K->2210816K(2487104K), 0.0000090 secs]111088.407: [Tenured: 3692057K->3638194K(5526912K), 10.7072790 secs] 5902873K->3638194K(8014016K), 10.7074960 secs]
111110.238: [GC 111110.238: [ParNew: 2210816K->2210816K(2487104K), 0.0000090 secs]111110.238: [Tenured: 3638194K->3654614K(5526912K), 8.8021440 secs] 5849010K->3654614K(8014016K), 8.8023860 secs]
111128.115: [GC 111128.115: [ParNew: 2210816K->2210816K(2487104K), 0.0000090 secs]111128.115: [Tenured: 3654614K->3668670K(5526912K), 8.8451510 secs] 5865430K->3668670K(8014016K), 8.8453600 secs]
111161.684: [GC 111161.684: [ParNew: 2210816K->2210816K(2487104K), 0.0000090 secs]111161.684: [Tenured: 3668670K->3684080K(5526912K), 8.8156740 secs] 5879486K->3684080K(8014016K), 8.8159260 secs]
111186.669: [GC 111186.669: [ParNew: 2210816K->2210816K(2487104K), 0.0000090 secs]111186.669: [Tenured: 3684080K->3639333K(5526912K), 10.6025350 secs] 5894896K->3639333K(8014016K), 10.6030040 secs]
111208.692: [GC 111208.692: [ParNew: 2210816K->2210816K(2487104K), 0.0000090 secs]111208.692: [Tenured: 3639333K->3657993K(5526912K), 8.7967920 secs] 5850149K->3657993K(8014016K), 8.7970090 secs]
111235.486: [GC 111235.487: [ParNew: 2210816K->2210816K(2487104K), 0.0000090 secs]111235.487: [Tenured: 3657993K->3676521K(5526912K), 8.8212340 secs] 5868809K->3676521K(8014016K), 8.8214930 secs]
4

3 回答 3

2

由于您有一个非常旧的 Java 版本(大约四年前)并且您似乎正在进入错误状态,所以首先要尝试的是更新版本,例如 Java 6 update 35。我怀疑 update 12 没有默认情况下启用 Compressed Oops,这是一个可以节省一些内存的选项(从而节省开销)

于 2012-11-29T15:10:01.600 回答
1

首先:一个线程转储!杀死 -3 进程并检查日志输出。

您可以通过查看正在运行的 GC 线程来确认 GC。

您将 8Go 用于 JVM。RAM 有多少演出?(我希望至少有 12 个)。

线程转储将向您显示 Eden、From、To、Permgen 大小。这可能有助于找出存在问题的内存空间。

于 2012-11-29T15:02:08.900 回答
0

这通常需要一些实验,但我猜你每 30 分钟就会看到一次这个问题,对吧?当你有一个完整的 GC 时,就像你对那些似乎不是由完整堆引起的永久行一样,它们很可能是由 System.gc() 引起的。通常在日志中它会被打印为(系统),但因为你的虚拟机很旧,我不确定。GC 日志信息每次发布都会更改。因此,为了消除这种情况,我强烈建议使用“-XX:+DisableExplicitGC”。它也忽略了 DGC 所做的调用。

这里的第二个选项可能是您在转储中没有看到内存泄漏/问题。这些行说你总是有新的2210816K,而旧的最后仍然是3676521K。如果所有 2210816K 都还活着(看起来像什么),那么就不可能将它们移动到 Tenured,因为它不适合 (5887337K),所以如果它不停止这样做,您可能会收到 GC Overhead exceeded 消息。但在这种情况下,当你进行转储时,你必须将那些 6G 活动对象放在堆中

于 2012-11-29T20:16:19.627 回答