当垃圾收集器运行并释放内存时,该内存将返回操作系统,还是作为进程的一部分保留。我印象深刻的是,该内存实际上从未释放回OS,而是保留为内存区域/池的一部分,以供同一进程重用。
结果,进程的实际内存将永远不会减少。一篇使我想起的文章是Java的Runtime是用C / C ++编写的,所以我想同样的事情适用吗?
更新 我的问题是关于Java的。我之所以提到C / C ++,是因为我假设Java的分配/取消分配是由JRE使用某种形式的malloc / delete完成的
在热点JVM不释放内存给操作系统,但不会因为调整堆所以勉强是昂贵的,它假定,如果你需要的堆一旦你会再次使用它。
你可以通过设置使其更具侵略性,-XX:GCTimeRatio=19 -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=30从而使其在GC周期后花费更多的CPU时间来收集和限制已分配但未使用的堆内存量。
-XX:GCTimeRatio=19 -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=30
假设你正在使用并发收集器,还可以将-XX:InitiatingHeapOccupancyPercent=NN 设置为一个较低的值,以使GC几乎连续运行并发收集,这将消耗更多的CPU周期,但会更快地缩小堆。通常这不是一个好主意,但是在某些类型的具有大量备用CPU内核但内存不足的机器上,这是有道理的。
XX:InitiatingHeapOccupancyPercent=NN
如果你使用的收集器具有默认的暂停时间目标(CMS或G1),则你也可以放宽该目标,以在收集器上设置更少的约束,或者你可以切换并行收集器,以在暂停时间之前优先考虑占用空间。
此外,通过Java 9 -XX:-ShrinkHeapInSteps选项,可以更积极地应用由前两个选项引起的缩小。相关的OpenJDK错误。
Java 9 -XX:-ShrinkHeapInSteps
请注意,收缩能力和行为取决于所选择的垃圾收集器。例如,G1仅使用jdk8u20 获得了在堆中间退回未使用的块的能力,而ZGC 使用jdk13进行了回收,而ε收集器则极有可能不会这样做。 因此,如果需要堆收缩,则应该针对特定的JVM版本和GC配置进行测试。
GC Logging with PrintAdaptiveSizePolicy也可能提供洞察力,例如,当JVM尝试为年轻一代使用更多内存以满足某些目标时。
GC Logging with PrintAdaptiveSizePolicy
OpenJDK 12中还包含JEP 346,该JEP引入了带有G1PeriodicGCInterval选件的G1GC的即时内存释放,但又以一些额外的CPU为代价。它还提到了Shenandoah和OpenJ9 VM中的类似功能。
G1PeriodicGCInterval