Java进程使用固定堆大小在操作系统中分配越来越多的内存。

3

我有一个Java应用程序,似乎从操作系统中分配了越来越多的内存(但堆大小根本没有增长!)这是一个与PLC通信的应用程序,所以需要相当多的CPU。

为了测试目的,我编写了这个程序,以确保问题不在某个库中:

public static void main(String[] args) {
    Random rand= new Random();
    if (args[0].equals("auto")) {
        for(int i = 0; i< Integer.valueOf(args[2]);i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    List<byte[]> threaddata = new ArrayList<>();
                    while(true) {                               
                        byte[] arr = new byte[Integer.valueOf(args[3])];
                        rand.nextBytes(arr);
                        threaddata.add(arr);
                        Thread.sleep(Long.valueOf(args[1]));
                        threaddata.clear();
                        Thread.sleep(Long.valueOf(args[1]));                                
                    }
                }                   
            }).start();
        }
    }

我这样启动了应用程序。
java -XX:NativeMemoryTracking=detail -Xmx1G -XX:+UseG1GC -XX:G1PeriodicGCInterval=10000 -XX:G1HeapWastePercent=10 -jar gctest.jar auto 100 3 10000000

这意味着应用程序在3个线程中每100ms分配和释放10MB。现在我遇到了Native Memory Tracking,它为我提供了Internal的输出结果。
-                  Internal (reserved=367356KB, committed=367356KB)
                        (malloc=367324KB #3131147)
                        (mmap: reserved=32KB, committed=32KB)

开始时需要约 15MB,现在几乎需要 400MB

我刚刚看到了这个文档,但它并没有真正帮助我。有什么线索可以防止Java从操作系统中分配越来越多的内存吗?

编辑:我分配和释放内存的速度越快,操作系统中的内存增长就越快。


尝试将 List<byte[]> threaddata = new ArrayList<>(); 移动到循环内部,看看会发生什么。 - Ray Tayek
这只是一个测试应用程序,在我的理解中,我实例化和释放的东西只会影响堆。奇怪的是,释放和分配内存越快,内存增长也越快。 - user2071938
移动列表不会改变任何内容,因为其中元素的引用会立即被clear()删除。但是尝试通过将“-Xmx1G”更改为“-Xmx256M”来减少JVM所需的内存量,并查看发生了什么。现在它应该会更早地开始进行GC。 - Torben
4
你并不是在"释放"对象 - 你只是使对象无法访问,这样垃圾回收器就可以在以后的某个时间释放内存。具体何时释放由垃圾回收器决定。你已经告诉 Java 它可以增长到1GB,这也是它正在做的事情。 - Erwin Bolwidt
1
你没有回答我的任何问题,也没有提供任何新信息。Java每天在数百万个系统上使用,除非您的系统出现了一些非常可疑的情况,否则它不会像这样出现内存泄漏。因此,我的假设是您对所看到的内容存在误解。例如,JVM可以分配更多的内存,但您测量时看到的已用堆大小可能很小(因为它刚刚被压缩)。 - john16384
显示剩余6条评论
1个回答

2

我运行了你的代码片段。

这是在VisualVM中的效果(JDK 11,标准设置):

enter image description here

我在任务管理器(Windows)中检查资源使用情况。它是恒定的。

所以,我在Linux上进行了检查。我需要安装htop并升级JDK,但我没有看到任何区别。这是运行程序5分钟后的htop输出情况。VIRT和RES值保持不变,总内存消耗保持类似。

enter image description here

然后再过5分钟的情况如下。Res变为了119M,但我认为这没什么好担心的。

enter image description here

从本地内存跟踪功能上,相隔5分钟时间:

最初的回答

[root@Ukyo 130 /download]# while sleep 300; do jcmd 16920 VM.native_memory |grep Internal -A 3; done
               Internal (reserved=605KB, committed=605KB)
                        (malloc=565KB #1151)
                        (mmap: reserved=40KB, committed=40KB)

               Internal (reserved=605KB, committed=605KB)
                        (malloc=565KB #1151)
                        (mmap: reserved=40KB, committed=40KB)

它的增长速度是多少?我仍然在运行它,现在是120M分辨率,在40分钟后,也许每20分钟增加1 MB吧?编辑:它刚跳到了129M,然后又回落到了128M。 - john16384
你说一周后它会耗尽内存...这是使用你发布的代码片段吗? - john16384
是的,因为我在systemd中限制了内存为500M,但是在我的实际应用程序中,系统会因为没有限制而耗尽内存。 - user2071938
所以,既然你使用了-Xmx1G(1 GB)限制了它,并在systemd中将其限制为0.5 GB,那么当JVM尝试分配超过0.5 GB的内存时,它会崩溃?这可能会发生在VM决定需要更多空间来处理(即使大部分是垃圾...这取决于内存使用/丢弃的速度)。那么你怎么限制VM的实际限制量,比如-Xmx400M或其他值? - john16384
另外,看看我的最后一个快照,RES(以及SHR)下降了... 我仍然没有看到任何内存消耗增加的迹象。当然,堆在任何时候都可能增长,如果VM感觉需要更多内存,出于任何原因(但永远不会超过你使用 -Xmx 设置的数量)。需要更多内存的原因也可能是为了保持分配速度快,减少垃圾回收的次数。 - john16384
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接