Tomcat内存增长问题

10

是什么原因导致Tomcat内存增长和下降


2
由于内存已成功清除,这是正常的。Tomcat显然有定期运行的后台任务,会创建对象并分配内存。您可以通过VisualVM或其他分析工具监视应用程序,以确定周期性创建的对象,或者查看源代码。 - Zpetkov
你使用的是哪个版本的JVM和Tomcat?你尝试过在一个全新的Tomcat安装上吗? - Mehmet Sunkur
@MehmetSunkur 尝试过使用 Java 7 和 Tomcat 5.5,以及使用 Java 8 和 Tomcat 8.5。 - 124697
这是一个有趣的问题。显然这不是内存泄漏(因为内存会随着GC而返回)。正如你所说,“如果我给JVM更多的内存,比如12GB,那么这种锯齿状的模式就从500MB变成了3GB”,我猜这是因为GC很有礼貌,如果不需要就不想打扰JVM。但是在空闲时间Tomcat在做什么是好知道的。你试过向Tomcat团队提出你的问题吗? - Honza Zidek
5个回答

9

它是jconsole本身导致了这种波浪形状。

为了看到或的效果,您可以编写一个简单的Java程序,只包含一个主循环。例如:

public static void main(String[] args) {
    while (true) {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }
    }
}

执行以下命令,并只分配一点堆内存:java -Xmx20m ...。这将帮助您更好地查看堆利用率,因为下一个我将使用的工具jstat以百分比打印利用率。

现在打开一个命令行窗口并执行jstat。您需要Java进程的PID,并可使用jps -l找到它。

jstat -gcutil <PID>

它将打印出类似于这样的东西
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00   0,00  69,34   0,00  14,48  17,19      0    0,000     0    0,000    0,000

关注 eden 空间 E。该值为空间当前容量的百分比。详见 jstat

如果您反复执行该命令,则会发现 eden 空间利用率不会增长。它将保持在例如 69,34。我在 Windows 上使用类似 Linux watch 的命令以特定间隔重新运行命令。请参见 watch.bat

现在打开 jconsole

jconsole <PID>

执行jstat命令,反复执行。您会看到伊甸园空间不断增长,直到达到最大值并进行垃圾回收。

这是我每隔1秒钟输出的jstat --gcutil <PID>结果。请关注伊甸园空间E

  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00   0,00  67,42   0,00  14,48  17,19      0    0,000     0    0,000    0,000
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00   0,00  67,42   0,00  14,48  17,19      0    0,000     0    0,000    0,000
# jconsole connected
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  96,88  81,64   7,57  92,26  84,87      1    0,001     0    0,000    0,001
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  96,88  84,66   7,57  92,26  84,87      1    0,001     0    0,000    0,001
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  96,88  89,70   7,57  92,26  84,87      1    0,001     0    0,000    0,001
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  96,88  91,70   7,57  92,26  84,87      1    0,001     0    0,000    0,001
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  96,88  93,70   7,57  92,26  84,87      1    0,001     0    0,000    0,001
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  96,88  95,70   7,57  92,26  84,87      1    0,001     0    0,000    0,001
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  96,88  96,70   7,57  92,26  84,87      1    0,001     0    0,000    0,001
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  96,88  98,71   7,57  92,26  84,87      1    0,001     0    0,000    0,001
# Garbage collected
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
100,00   0,00   1,13  14,06  94,75  89,26      2    0,003     0    0,000    0,003
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
100,00   0,00   3,00  14,06  94,75  89,26      2    0,003     0    0,000    0,003
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
100,00   0,00   5,75  14,06  94,75  89,26      2    0,003     0    0,000    0,003

正如您所看到的...当jconsole连接到进程后,伊甸园空间会不断增长,直到被垃圾回收。这就导致了锯齿状的模式。以下是来自jvisualvm的快照。

enter image description here

你会发现当你对tomcat进程执行相同操作时,会出现类似的行为。唯一的区别是,即使没有连接jconsole,eden空间也会略微增长。但这种微小的增长并不是你看到的锯齿状模式的原因。
以下是我的tomcat的jstat输出。
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  99,80  70,33   4,81  96,85  90,10      5    0,037     0    0,000    0,037
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  99,80  70,33   4,81  96,85  90,10      5    0,037     0    0,000    0,037
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  99,80  70,43   4,81  96,85  90,10      5    0,037     0    0,000    0,037
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  99,80  70,43   4,81  96,85  90,10      5    0,037     0    0,000    0,037
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  99,80  70,43   4,81  96,85  90,10      5    0,037     0    0,000    0,037
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT

这个输出会持续36秒,直到你可以看到小的内存变化。从70,43变为70,53
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  99,80  70,43   4,81  96,85  90,10      5    0,037     0    0,000    0,037
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  99,80  70,53   4,81  96,85  90,10      5    0,037     0    0,000    0,037
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0,00  99,80  70,53   4,81  96,85  90,10      5    0,037     0    0,000    0,037

因此,这个小改变是Tomcat后台进程唯一需要负责的事情。

附注: 您也可以使用Memory Analyser不时地获取堆转储,您会发现byte[]数组的使用内存量增加,因为RMI网络流量。


谢谢你的回答。我无法授予你完整的500分,我猜这是因为悬赏时间已经结束了。我以为这不会有什么影响。 - 124697

3
以下是链接答案中解释的锯齿形模式需要理解的重要要点:
- 当收集器运行时,它首先尝试进行部分收集,仅释放最近分配的内存。 - 仍处于活动状态的最近创建的对象会被“提升”。 - 一旦一个对象被提升几次,即使它已准备好进行收集,部分收集也不会再清理它。 - 这些对象被称为老年对象,只有当需要进行完全收集以便为程序腾出足够的空间时才会被清理。
问题:即使应用程序完全空闲,是什么导致内存增长? 回答:有一些内部计划任务,如自动化计时器等,或者一些外部进程监视会在应用程序空闲时导致内存增长。也许您的应用程序具有相同数量的对象,但某些对象可能更大。例如,您可能有一些ArryList和一些StrigBuilder,它们不断增加缓存。

来源:


你知道是什么原因导致内存首先增长的吗? - 124697

1
让我们看一下图表。大约在12:08,工作内存的大小达到最大值(199 MB)。
大约在12:09,垃圾收集器正在工作,死对象被收集,工作内存的大小约为145 MB。
大约在12:12,工作内存增加,产生了许多死对象,内存从145 MB增加到155 MB,165 MB,180 MB,...,190 MB。
大约在12:13,垃圾收集器再次工作,死对象被收集,工作内存的大小约为145 MB。
......等等。 实际上,如果没有特殊事件发生,它是一个周期性的图形。

你可以在https://www.javaworld.com/article/2073905/build-ci-sdlc/pick-up-performance-with-generational-garbage-collection.html?page=2阅读更多内容。请参见第7图。 - Raphaël Colantonio
有没有想法是什么原因导致应用程序未被使用时内存增长?是JVM还是Tomcat? - 124697
我在没有任何Web应用程序运行时检查了Apache Tomcat。它仍然增加,然后减少,然后增加内存... 这是垃圾收集器循环在工作。Tomcat只是Java应用程序的一个例子,您可以使用其他Java应用程序。 - Raphaël Colantonio

1

1

原因可能各不相同,如果没有更多关于您的应用程序的信息:假设它没有自己执行任何重复任务,则可能的原因之一是JConsole本身,因为收集信息并通过RMI发送它会消耗内存,这些内存可以从Eden空间中快速收集。

您可以通过使用较少"侵入性"的工具(例如使用-verbosegc启用GC详细输出)进行分析,并在类似设置中比较内存使用情况来测试此假设。

正如其他答案所强调的那样,Tomcat也可能有其自己的开销。您可以通过提供一个"hello, world" servlet并使用类似的工具比较两者的内存消耗来测试这个假设。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接