当垃圾收集器运行并释放内存时,这些内存是返回到操作系统,还是作为进程的一部分保留。我有一个强烈的印象,那就是内存实际上永远不会释放回操作系统,而是作为内存区/池的一部分保留下来,供同一进程重用。
因此,进程的实际内存永远不会减少。这让我想起了这一点,An article的运行时是用C/C++编写的,所以我猜同样的事情也适用?
更新
我的问题是关于Java的。我之所以提到C/C++,是因为我假设Java的分配/释放是由JRE使用某种形式的malloc/delete来完成的
发布于 2015-05-27 00:33:23
HotSpot JVM确实将内存释放回操作系统,但这样做是不情愿的,因为调整堆的大小代价很高,而且假定如果您需要该堆一次,那么您将再次需要它。
通常,收缩能力和行为取决于所选的垃圾收集器,JVM版本因为收缩功能通常是在添加GC本身很久之后才引入的。一些收集器可能还需要传递额外的选项才能选择收缩。而有些很可能永远不会支持它,例如EpsilonGC。因此,如果需要堆收缩,应该针对特定的JVM版本和GC配置进行测试。
JDK 8及更早版本
在这些版本中没有显式的内存回收选项,但是您可以通过设置-XX:GCTimeRatio=19 -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=30
使GC更加积极,这将允许它花费更多的CPU时间来收集,并在GC周期之后限制已分配但未使用的堆内存的数量。
如果您正在使用并发收集器,您还可以将-XX:InitiatingHeapOccupancyPercent=N
的N设置为某个较低的值,让GC几乎不间断地运行并发收集器,这将消耗更多的CPU周期,但会更快地缩小堆。这通常不是一个好主意,但在某些具有大量空闲CPU核心但内存不足的机器上,这可能是有意义的。
如果您使用的是G1GC,请注意,它只能通过jdk8u20获得yield back unused chunks in the middle of the heap的能力,早期版本只能在堆的末尾返回块,这对可以回收的块有很大限制。
如果您正在使用具有默认暂停时间目标的收集器(例如CMS或G1),您还可以放宽该目标以减少对收集器的限制,或者您可以切换到并行收集器,以便优先考虑内存占用而不是暂停时间。
为了验证收缩的发生或诊断GC决定不收缩的原因,可以将GC日志记录与-XX:+PrintAdaptiveSizePolicy
一起使用也可以提供洞察力,例如,当JVM试图为年轻一代使用更多内存来实现某些目标时。
JDK 9
添加了-XX:-ShrinkHeapInSteps
选项,该选项可用于更积极地应用由上一节提到的选项引起的收缩。Relevant OpenJDK bug。
对于日志记录,已用-Xlog:gc+ergo
取代了-XX:+PrintAdaptiveSizePolicy
JDK 12
引入了通过G1PeriodicGCInterval
(JEP 346)为G1GC启用即时内存释放的选项,这也是以一些额外的CPU为代价的。JEP还提到了Shenandoah和OpenJ9 VM中的类似功能。
JDK 13
添加类似的行为for ZGC,在本例中,它是默认启用的。
发布于 2015-05-26 20:59:01
在某些情况下,JVM确实会释放回内存,但(出于性能原因)只要有一些内存被垃圾回收,就不会发生这种情况。这也取决于JVM,操作系统,垃圾收集器等。你可以用JConsole,VisualVM或者其他分析器来观察你的应用的内存消耗。
另请参阅此related bug report
发布于 2018-11-13 23:52:36
如果您使用Java收集器并偶尔调用System.gc() (我每分钟调用一次),G1将可靠地收缩堆并将内存返回给操作系统。
从Java12开始,如果应用程序空闲,G1就会自动执行此操作。
我建议将这些选项与上述建议结合使用,以获得非常紧凑的驻留进程大小:
-XX:+UseG1GC -XX:MaxHeapFreeRatio=30 -XX:MinHeapFreeRatio=10
几个月来,我每天在一个大型应用程序(整个基于Java的来宾操作系统)中使用这些选项,这些应用程序动态地加载和卸载类-Java进程几乎总是在400到800 MB之间。
https://stackoverflow.com/questions/30458195
复制相似问题