OpenJDK JVM是否会将堆内存返回给Linux?

Pet*_*rch 52 java memory-management heap-memory

我们有一个长期存在的服务器进程,很快就需要大量的RAM.我们看到,一旦JVM从操作系统获得内存,它就永远不会将其返回给操作系统.我们如何要求JVM将堆内存返回给操作系统?

通常,这些问题的接受答案是使用 -XX:MaxHeapFreeRatio-XX:MinHeapFreeRatio.(参见例如 1,2,3,4).但是我们像这样运行java:

java -Xmx4G -XX:MaxHeapFreeRatio=50 -XX:MinHeapFreeRatio=30 MemoryUsage
Run Code Online (Sandbox Code Playgroud)

仍然在VisualVM中看到这个:

Visual VM内存使用情况

显然,JVM并不尊重,-XX:MaxHeapFreeRatio=50因为heapFreeRatio非常接近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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

版本:

> 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
Run Code Online (Sandbox Code Playgroud)

我也尝试过OpenJDK 1.7和Sun Java 1.8.所有行为都相似,没有一个给内存回OS.

我确实认为我需要这个并且交换和分页不会"解决"这个问题,因为在寻呼上花费磁盘IO接近2GB的垃圾进出只会浪费资源.如果您不同意,请赐教.

我还用malloc()/ 写了一点memoryUsage.c free(),它确实将内存返回给操作系统.所以在C中是可能的.也许不是用Java?

编辑:奥古斯托指出,搜索会一直带领我-XX:MaxHeapFreeRatio-XX:MinHeapFreeRatio只与合作-XX:+UseSerialGC.我欣喜若狂,尝试了一下,感到困惑的是我自己没有找到这个.是的,它确实适用于我的MemoryUsage.java:

-XX:+ UseSerialGC使用简单的应用程序

但是,当我尝试-XX:+UseSerialGC使用我们的真实应用程序时,并非如此:

-XX:+ UseSerialGC无法使用真实应用

我发现gc()一段时间后确实有所帮助,所以我创建了一个或多或少的线程:

while (idle() && memoryTooLarge() && ! tooManyAttemptsYet()) {
    Thread.sleep(10*1000);
    System.gc();
}
Run Code Online (Sandbox Code Playgroud)

这就是诀窍:

GC线程工作

实际上,我之前在一些实验中看到了行为-XX:+UseSerialGC和多次System.gc()调用但不喜欢GC线程的需要.谁知道,随着我们的应用程序和Java的发展,它是否会继续发挥作用.肯定有更好的办法.

什么是迫使我System.gc()四次打电话(但不是立刻)的逻辑,这些东西记录在哪里?

为了寻找文档-XX:MaxHeapFreeRatio并且-XX:MinHeapFreeRatio只使用它-XX:+UseSerialGC,我阅读了java工具/可执行文件的文档,并且在任何地方都没有提到它-XX:MaxHeapFreeRatio,-XX:MinHeapFreeRatio只能使用它-XX:+UseSerialGC.事实上,修复问题[JDK-8028391]使Min/MaxHeapFreeRatio标志可管理说:

为了使应用程序能够控制允许更多或更少GC的方式和时间,标志-XX:MinHeapFreeRatio和-XX:MaxHeapFreeRatio应该是可管理的.还应在默认并行收集器中实现对这些标志的支持.

一个评论的固定问题,说:

作为自适应大小策略的一部分,ParallelGC还添加了对这些标志的支持.

我已经检查了,修复问题中引用的补丁反向移植到openjdk-8确实包含在我正在使用的openjdk-8版本的源代码包tarball中.所以它显然应该在"默认的并行收集器"中工作,但不像我在这篇文章中所证明的那样.我还没有发现任何文档说它应该只能使用-XX:+UseSerialGC.正如我在这里记载的那样,即使这是不可靠/冒险的.

我不能只是得到-XX:MaxHeapFreeRatio-XX:MinHeapFreeRatio做他们的诺言,而不必去通过所有这些箍?

小智 7

G1(-XX:+ UseG1GC),并行清除(-XX:+ UseParallelGC)和ParallelOld(-XX:+ UseParallelOldGC)会在堆收缩时返回内存.我不太确定Serial和CMS,他们在我的实验中没有缩小它们的堆.

两个并行收集器确实需要许多GC,然后将堆缩小到"可接受"的大小.这是每个设计.他们故意坚持堆,假设将来需要它.设置标志-XX:GCTimeRatio = 1会稍微改善这种情况,但仍需要几个GC缩小很多.

G1非常适合快速缩小堆,因此对于上面描述的用例,我会说它可以通过使用G1并System.gc()在释放所有缓存和类加载器等后运行来解决 .

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6498735