当垃圾收集器运行并释放内存时,这些内存是返回给操作系统还是作为进程的一部分保留。我的强烈印象是,内存实际上从未释放回操作系统,而是保留为内存区域/池的一部分,以供同一进程重用。
结果,进程的实际内存永远不会减少。 An article 提醒我的是,Java 的运行时是用 C/C++ 编写的,所以我猜同样的事情也适用?
更新我的问题是关于 Java 的。我提到 C/C++,因为我假设 Java 的分配/释放是由 JRE 使用某种形式的 malloc/delete 完成的
Java’s Runtime is written in C/C++ so I guess the same thing applies?
而且 Java 的 GC 也是用 C++ 编写的……它不是“低于”C++
HotSpot JVM 确实将内存释放回操作系统,但这样做很不情愿,因为调整堆的大小很昂贵,并且假设如果您需要该堆,您将再次需要它。
一般来说,收缩能力和行为取决于选择的垃圾收集器,JVM 版本,因为收缩能力通常是在添加 GC 本身很久之后的更高版本中引入的。一些收集器可能还需要传递其他选项来选择收缩。有些很可能永远不会支持它,例如 EpsilonGC。因此,如果需要堆收缩,则应针对特定的 JVM 版本和 GC 配置进行测试。
JDK 8 及更早版本
在这些版本中,没有明确的提示内存回收选项,但您可以通过设置 -XX:GCTimeRatio=19 -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=30
使 GC 通常更具侵略性,这将允许它花费更多的 CPU 时间来收集和限制已分配但未使用的堆内存量在一个 GC 循环之后。
如果您使用并发收集器,您还可以将带有 N 的 -XX:InitiatingHeapOccupancyPercent=N
设置为某个较低的值,以让 GC 几乎连续地运行并发收集,这将消耗更多的 CPU 周期,但会更快地缩小堆。这一般不是一个好主意,但在某些类型的机器上有很多备用 CPU 内核但内存不足时,它是有意义的。
如果您使用的是 G1GC,请注意它只能通过 jdk8u20 获得 yield back unused chunks in the middle of the heap 的能力,早期版本只能在堆末尾返回块,这对可以回收的量有很大的限制。
如果您正在使用具有默认暂停时间目标的收集器(例如 CMS 或 G1),您还可以放宽该目标以减少对收集器的限制,或者您可以切换到并行收集器以优先考虑占用空间而不是暂停时间。
要验证收缩发生或诊断 GC 决定不收缩的原因,您可以使用带有 -XX:+PrintAdaptiveSizePolicy
的 GC Logging 也可以提供洞察力,例如,当 JVM 尝试为年轻代使用更多内存以满足某些目标时。
JDK 9
添加了 -XX:-ShrinkHeapInSteps
选项,可用于更积极地应用由上一节中提到的选项引起的收缩。 Relevant OpenJDK bug。
对于日志记录,-XX:+PrintAdaptiveSizePolicy
已替换为 -Xlog:gc+ergo
JDK 12
引入了通过 G1PeriodicGCInterval
(JEP 346) 为 G1GC 启用即时内存释放的选项,再次以一些额外的 CPU 为代价。 JEP 还在 Shenandoah 和 OpenJ9 VM 中提到了类似的功能。
JDK 13
添加类似的行为 for ZGC,在这种情况下默认启用。
在某些情况下,JVM 确实会释放回内存,但是(出于性能原因)当某些内存被垃圾回收时,这不会发生。它还取决于 JVM、操作系统、垃圾收集器等。您可以使用 JConsole、VisualVM 或其他分析器查看应用程序的内存消耗。
另请参阅此related bug report
如果您使用 G1 收集器并偶尔调用 System.gc()(我每分钟调用一次),Java 将可靠地缩小堆并将内存归还给操作系统。
从 Java 12 开始,如果应用程序空闲,G1 会自动执行此操作。
我建议将这些选项与上述建议结合使用,以获得非常紧凑的常驻进程大小:
-XX:+UseG1GC -XX:MaxHeapFreeRatio=30 -XX:MinHeapFreeRatio=10
几个月来每天都在使用这些选项来处理具有动态加载和卸载类的大型应用程序(整个基于 Java 的客户操作系统)——Java 进程几乎总是保持在 400 到 800 MB 之间。
Xms
和 Xmx
设置为相同的值会怎样。 G1 还会将内存还给操作系统吗? (JDK 12 之前)
@
标记我,否则我不知道你回复了 2) Xms
是 不是 最小内存,而是 initial,因此您错了。
-Xms
的文档,现在越来越多的人(包括那些开发中 GC)说它是最小堆值?
InitialHeapSize
,这使得这更加令人困惑。