OpenJDK JVM会将堆内存回馈给Linux吗?
我们有一个长期的服务器进程,很less有很短的时间需要大量的RAM。 我们看到,一旦JVM从操作系统获得内存,它就不会将其返回到操作系统。 我们如何让JVM将堆内存返回给操作系统?
通常,这些问题的接受答案是使用-XX:MaxHeapFreeRatio
和-XX:MinHeapFreeRatio
。 (见例如1,2,3,4 )。 但是我们这样运行java:
java -Xmx4G -XX:MaxHeapFreeRatio=50 -XX:MinHeapFreeRatio=30 MemoryUsage
而在VisualVM中仍然可以看到这一点:
很明显,JVM并不尊重-XX:MaxHeapFreeRatio=50
因为堆自由比率非常接近100%,远不及50%。 没有任何点击“执行GC”将内存返回到操作系统。
MemoryUsage.java:
import java.util.ArrayList; import java.util.List; public class MemoryUsage { public static void main(String[] args) throws InterruptedException { System.out.println("Sleeping before allocating memory"); Thread.sleep(10*1000); System.out.println("Allocating/growing memory"); List<Long> list = new ArrayList<>(); // Experimentally determined factor. This gives approximately 1750 MB // memory in our installation. long realGrowN = 166608000; // for (int i = 0 ; i < realGrowN ; i++) { list.add(23L); } System.out.println("Memory allocated/grown - sleeping before Garbage collecting"); Thread.sleep(10*1000); list = null; System.gc(); System.out.println("Garbage collected - sleeping forever"); while (true) { Thread.sleep(1*1000); } } }
版本:
> java -version openjdk version "1.8.0_66-internal" OpenJDK Runtime Environment (build 1.8.0_66-internal-b01) OpenJDK 64-Bit Server VM (build 25.66-b01, mixed mode) > uname -a Linux londo 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt11-1+deb8u5 (2015-10-09) x86_64 GNU/Linux > lsb_release -a No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux 8.2 (jessie) Release: 8.2 Codename: jessie
我也尝试了OpenJDK 1.7和Sun Java 1.8。 所有行为都是相似的,没有任何内存回到操作系统。
我认为我需要这个,交换和分页不会“解决”这个,因为把磁盘IO分页到接近2GB的垃圾进出只是浪费资源。 如果你不同意,请赐教。
我还用malloc()
/ free()
写了一个小小的memoryUsage.c,它将内存返回给操作系统。 所以它可能在C.也许不是与Java?
编辑:奥古斯托指出,search会导致我-XX:MaxHeapFreeRatio
和-XX:MinHeapFreeRatio
只能使用-XX:+UseSerialGC
。 我欣喜若狂,试了一下,觉得自己没有find。 是的,它与我的MemoryUsage.java一起工作:
但是,当我尝试使用-XX:+UseSerialGC
与我们的真实应用程序,而不是:
我发现,gc()在一段时间后帮助,所以我做了一个或多或less的线程:
while (idle() && memoryTooLarge() && ! tooManyAttemptsYet()) { Thread.sleep(10*1000); System.gc(); }
那就是诀窍:
实际上,我之前在许多实验中看到过使用-XX:+UseSerialGC
和多个System.gc()
调用的行为,但不喜欢GC线程的需要。 谁知道这是否会继续工作,因为我们的应用程序和Java的演变。 一定会有更好的办法。
什么是强制我四次(但不是立即)调用System.gc()
的逻辑,以及这个东西在哪里logging?
在search-XX:MaxHeapFreeRatio
和-XX:MinHeapFreeRatio
的文档中,只使用-XX:+UseSerialGC
,我阅读了java工具/可执行文件的文档,并且在-XX:MaxHeapFreeRatio
和-XX:MinHeapFreeRatio
只能使用-XX:+UseSerialGC
。 事实上,修正的问题[JDK-8028391]使Min / MaxHeapFreeRatio标志易于pipe理 :
为了使应用程序能够控制如何以及何时允许或多或less的GC,标志-XX:MinHeapFreeRatio和-XX:MaxHeapFreeRatio应该是可pipe理的。 还应该在默认的并行收集器中实现对这些标志的支持。
针对固定问题的评论说:
作为自适应尺寸策略的一部分,对这些标志的支持也被添加到了ParallelGC中。
我已经检查了, 修复的问题中引用的修补程序 backported到openjdk-8确实包含在我使用的openjdk-8版本的源码包tarball中。 所以它应该显然在“默认并行收集器”中工作,但并不像我在这篇文章中演示的那样。 我还没有find任何说它只能用-XX:+UseSerialGC
。 正如我在这里logging的,即使这是不可靠的/冒险的。
我不能只是得到-XX:MaxHeapFreeRatio
和-XX:MinHeapFreeRatio
做他们承诺,而不必经历所有这些箍?
“堆栈收缩时,G1(-XX:+ UseG1GC),并行清除(-XX:+ UseParallelGC)和ParallelOld(-XX:+ UseParallelOldGC)会返回内存,我不太确定Serial和CMS,在我的实验中缩小堆。
两个并行收集器在将堆缩小到“可接受”大小之前确实需要大量的GC。 这是每个devise。 他们正在蓄意坚持,假设将来需要它。 设置标志-XX:GCTimeRatio = 1会稍微改善这种情况,但是仍然需要几个GC来缩小很多。
G1在缩小堆的速度方面非常出色,所以对于上面描述的用例,我可以说通过使用G1和释放所有caching和类加载器之后运行System.gc()
等方法可以解决这个问题。
如果有方法(比如m1())在你的应用程序中执行繁重的操作,你可以试试这个:
- 而不是调用m1(),
serialize
方法工作所需的所有对象。 - 创build一个
de-serialize
化前一步中de-serialize
对象的主要方法。 并调用m1() – 这个主要方法是程序P1的入口点。 - 在m1()完成时,序列化主程序的输出以反序列化。
- 使用
Runtime or ProcessBuilder
作为单独的程序执行P1。
这样,当繁重的提升方法m1()完成时,进程P1将结束,应释放该JVM上的堆。
你的问题退出复杂 – 我会build议一个简单的解决scheme。 从我读过的,你知道如何在C和Java中编码。 可能使用JNA(Java本机访问)或JNI(Java本地接口)可能会解决问题的根源(编码C中的繁重处理部分并从Java调用它)。 另一种方法是用C编写的另一个小程序做繁重的工作 – 你可以从你的java代码中调用这个小程序(最好是在它自己的线程中)。 Java本地访问 Java本地接口