Java 老年代不断增长,直到 Tomcat 重新启动

Java old generation keeps growing until Tomcat restarts

我在多个服务器上有 JDK 8,堆大小不断增加,直到达到 90%,应用程序失败。在此期间多次 GC 运行 但老年代不断增长,直到应用程序失败。当前的解决方法是重新启动 Tomcat。我的服务器有 15GB RAM,4 核 CPU。我的 Java 设置是:

-Xms12036M -Xmx12036M -Xmn800M 

对这些值有什么建议吗?我以为 GC 会处理它,但老年代似乎不会。我是 Java 的新手,不确定这是否表示存在内存泄漏,或者 GC 是否没有这样做?

我认为你的参数没有问题。我建议在您的代码中寻找内存泄漏。使用探查器是个好主意,但您也可以查找泄漏模式。这是一个非常好的集合:Creating a memory leak with Java

JVM 的 GC 只是对您的主程序未引用的对象的响应,(包括 "reference cycle"),

如果Heap中有很多对象无法被GC回收, 导致OOM,两种可能:

  1. 你的程序逻辑上需要这些对象:
    您需要添加内存资源,或者需要更改程序的进程 以避免使用大量内存资源。

  2. 这些对象逻辑上不需要,但仍然被你的主程序引用, (例如:不需要对象,但没有删除 来自列表): 这是 java 中的内存泄漏,这是一个错误,你必须 在你的程序中修复它。

尝试使用一些分析工具,或跟踪您的代码以确定它是否是内存泄漏问题。

首先使用 JStacks/JMaps、线程转储和内存分析器工具检查内存泄漏前沿。如果您的系统内存没有任何泄漏,则必须微调垃圾收集器算法。

Java 提供不同类型的垃圾收集器。

看看各种GC algorithms

的优点和缺点
  1. 串行收集器:串行收集器是最简单的一种,您可能不会使用它,因为它主要是为单机设计的-线程环境。

  2. 并行/吞吐量收集器:它最大的优点是使用多个线程来扫描和压缩堆。并行收集器的缺点是它会在执行次要或完整 GC 收集时停止应用程序线程。

  3. CMS 收集器: 该算法使用多个线程(“并发”)扫描堆(“标记”)以查找未使用的对象回收(“清扫”)。如果您增加老年代(或整个堆)的大小或为 collector.It 分配更多后台线程使用更多 CPU 以便为应用程序提供更高的性能,这将是高效的连续吞吐量水平。

  4. G1 收集器:JDK7 更新 4 中引入的垃圾优先收集器 (G1) 旨在更好地支持大于 4GB 的堆. G1 收集器利用多个后台线程扫描它划分为区域的堆,范围从 1MB 到 32MB(取决于堆的大小)。使用 –XX:+UseG1GC 标志。

如果您使用的是 G1 收集器,则必须微调区域大小参数。如果 15 GB 是您的堆大小,您的区域大小应该是 (15 GB / 2048 ) MB,大约是 7 MB。不要在 New Gen Size 上进行更多实验。您可以坚持使用默认设置,因为此算法使用默认值可提供最佳性能。

看看这篇文章:

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html

-Xmn800M(800MB 年轻一代)对于 12GB 堆来说似乎是一个相当低的值。考虑直接取消该参数并保留 -Xms12036M -Xmx12036M 参数。

请注意,您的老一代将继续增长,直到运行完整的 GC。您将有许多次要 GC,它们将在前 800MB 上执行 GC,但不会在剩余的 11.2GB 上执行。因此,尝试 compare heap dumps using something the Eclipse Memory Analysis Tool. Also, try to grab a few different heap dumps。留意尺寸单调增长的对象。