当垃圾收集器运行并释放内存时,此内存会返回到操作系统,或者是否作为进程的一部分保留。我的印象是,内存从未实际发布回操作系统,而是作为内存区域/池的一部分保留,以便由同一进程重用。
因此,进程的实际内存永远不会减少。 An article提醒我的是这个和Java的Runtime是用C / C ++编写的,所以我想同样的事情适用吗?
更新
我的问题是关于Java。我提到C / C ++因为我假设Java的分配/解除分配是由JRE使用某种形式的malloc / delete
答案 0 :(得分:44)
HotSpot JVM确实将内存释放回操作系统,但是不情愿地这样做,因为调整堆的大小是昂贵的,并且如果你需要它再次需要它,则假设你需要它。
您可以通过设置-XX:InitiatingHeapOccupancyPercent=N
来使其更具攻击性,这将允许它在GC循环后花费更多的CPU时间来收集和约束已分配但未使用的堆内存量。
假设您正在使用并发收集器,您还可以将-XX:-ShrinkHeapInSteps
与N设置为某个较低的值,以使GC几乎连续地运行并发收集,这将消耗更多的CPU周期,但会更快地缩小堆。这个通常并不是一个好主意,但是在某些类型的机器上有很多备用CPU内核但是内存不足也很有意义。
如果您正在使用具有默认暂停时间目标(CMS或G1)的收集器,您还可以放宽该目标以减少对收集器的约束,或者您可以切换并行收集器以在暂停时间内确定占用空间的优先级。
此外,Java 9 PrintAdaptiveSizePolicy
选项可用于更积极地应用前两个选项引起的收缩。 Relevant OpenJDK bug
请注意,缩小的能力和行为取决于所选的垃圾收集器。例如,G1只能通过jdk8u20获得yield back unused chunks in the middle of the heap的能力,而ZGC支持not yet支持它,而epsilon收集器很可能永远不会支持它。
因此,如果需要堆缩小,则应测试特定的JVM版本和GC配置。
G1PeriodicGCInterval
进行GC记录也可以提供洞察力,例如当JVM试图为年轻一代使用更多内存来实现某些目标时。
还有JEP 346,包含在OpenJDK 12中,它引入了带有{{1}}选项的G1GC的快速内存释放,同样是以一些额外的CPU为代价。它还提到了Shenandoah和OpenJ9 VM中的类似功能。
答案 1 :(得分:8)
JVM确实会在某些情况下释放内存,但是(出于性能原因)只要某些内存被垃圾回收,就不会发生这种情况。它还取决于JVM,操作系统,垃圾收集器等。您可以使用JConsole,VisualVM或其他分析器来监视应用程序的内存消耗。
答案 2 :(得分:3)
这篇文章here解释了GC如何在Java 7中工作。简而言之,有许多不同的垃圾收集器可用。通常,内存是为Java进程保留的,只有一些GC将它释放到系统中(根据我的要求,我认为)。但是,Java进程使用的内存不会无限增长,因为Xmx选项定义了一个上限(通常为256m,但我认为它取决于操作系统/机器)。
答案 3 :(得分:3)
如果您偶尔使用G1收集器并偶尔调用System.gc()(我每分钟执行一次),Java将可靠地缩小堆并将内存分配给OS。
每当您甚至提到手动调用垃圾收集器的想法时,有些人就会foam之以鼻-但如果要可靠的堆压缩,则需要这样做。
我建议将这些选项与上述建议结合使用,以实现非常紧凑的驻留进程大小:
-XX:+UseG1GC -XX:MaxHeapFreeRatio=30 -XX:MinHeapFreeRatio=10
每天在大型应用程序(一个完整的基于Java的来宾OS)上使用这些选项几个月,并带有动态加载和卸载类-Java进程几乎总是保持在400到800 MB之间。
编辑:“当应用程序空闲时”,JDK 12将具有G1收集器return memory to the OS(不调用System.gc)。后者似乎是为服务器应用程序定义的,但是也许它也适用于台式机和/或它们将使它可定制。
答案 4 :(得分:0)