java - Java的RAM使用情况与任务管理器所说的不符

标签 java memory jvm ram

我一直在通过制作1024^3(基本上为1Gb)长度的字节数组来玩Java的JVM。我使用任务管理器(查看过程)和以下代码片段,测量了数组创建之前,之后,垃圾回收器销毁数组之后的RAM使用情况:

public static void showMemory() {
    System.out.println("Memory used: "
            + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024.D * 1024.D) + "mB.");
}

上述代码分别显示2Mb,1029Mb和2Mb。 ->这看起来很正常。
但是,当查看TaskManager时,Java的RAM使用量起初为2mb,然后达到1052Mb并停留在该位置,即使代码段显示为2Mb。

由于我希望Java使用最少的资源,我该如何解决这个问题?

编辑:

我进行了一些研究,并弄清楚了要使用的术语。实际上, native 内存与内存的值不相似,并且通常大于堆内存。有没有办法减少使用的 native 内存,使其接近堆内存?

最佳答案

结论:

首先使用垃圾(G1)GC (Java 9中的默认GC),与 ParallelOldGC (Java 7和Java 8中的默认GC),很少会减小堆大小!

通常:

您的基本假设是错误的。

您假设您的代码段显示了堆大小。这是不正确的。它显示了堆利用率。这意味着“我的堆使用了多少空间?”。 Runtime.getRuntime().totalMemory()显示堆大小,Runtime.getRuntime().freeMemory()显示空闲堆大小,它们的差显示堆利用率(已用大小)。

您的堆以初始大小开始,具有0字节的利用率,因为尚未创建任何对象,并且最大堆大小。最大堆大小描述了允许垃圾收集器调整堆大小的大小(例如,如果没有足够的空间容纳非常大的对象)

在创建空堆之后,作为下一步,将自动加载一些对象(类对象等),它们通常应该很容易适应初始堆大小。

然后,您的代码开始运行并分配对象。一旦您的Eden空间中没有更多空间(堆被拆分为年轻一代(Eden,幸存者从生存者到空间)和老一代,如果您对这些细节感兴趣,请查找其他资源) ,则会触发垃圾回收。

在垃圾回收期间,垃圾回收器可能会决定调整堆的大小(如上所述,在讨论最大堆大小时)。在您的情况下会发生这种情况,因为您的初始堆大小太小而无法容纳1GB的对象。因此,堆大小会增加,介于初始堆大小和最大堆大小之间。

然后,在您的大对象死亡之后,下一个GC 可以再次使堆变小,但是不必是。为什么?它低于最大堆大小,这是GC所关心的。有些垃圾回收算法会再次缩小堆,有些则不会。

尤其是 ParallelOldGC (Java 7和Java 8中的默认GC)很少很少执行以减少堆大小。

如果您想让GC通过在垃圾回收期间缩小堆大小来尝试使堆大小保持较小,请首先通过设置-XX:+UseG1GC Java标志尝试垃圾(G1)GC

示例:

这将打印出所有以字节为单位的值。

您将获得一个概述,这两个GC如何工作以及使用它们中的任何一个时要占用多少空间。

System.out.println(String.format("Init:\t%,d",ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getInit()));
System.out.println(String.format("Max:\t%,d%n", ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax()));

Thread outputThread = new Thread(() -> {
    try {
        int i = 0;
        for(;;) {
            System.out.println(String.format("%dms\t->\tUsed:\t\t%,d", i, ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed()));
            System.out.println(String.format("%dms\t->\tCommited:\t%,d", i, ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getCommitted()));
            Thread.sleep(100);
            i += 100;
        }
    } catch (Exception e) { }
});

Thread allocThread = new Thread(() -> {
    try {
        int val = 0;
        Thread.sleep(500); // Wait 1/2 second
        createArray();
        Thread.sleep(500); // Wait another 1/2 seconds
        System.gc(); // Force a GC, array should be cleaned
        return;
    } catch (Exception e) { }
});

outputThread.start();
allocThread.start();
createArray()只是以下小方法:
private static void createArray() {
    byte[] arr = new byte[1024 * 1024 * 1024];
}

-结果ParallelOldGC :
Init:   262,144,000
Max:    3,715,629,056

0ms ->  Used:       6,606,272
0ms ->  Commited:   251,658,240
100ms   ->  Used:       6,606,272
100ms   ->  Commited:   251,658,240
200ms   ->  Used:       6,606,272
200ms   ->  Commited:   251,658,240
300ms   ->  Used:       6,606,272
300ms   ->  Commited:   251,658,240
400ms   ->  Used:       6,606,272
400ms   ->  Commited:   251,658,240
500ms   ->  Used:       1,080,348,112
500ms   ->  Commited:   1,325,924,352
600ms   ->  Used:       1,080,348,112
600ms   ->  Commited:   1,325,924,352
700ms   ->  Used:       1,080,348,112
700ms   ->  Commited:   1,325,924,352
800ms   ->  Used:       1,080,348,112
800ms   ->  Commited:   1,325,924,352
900ms   ->  Used:       1,080,348,112
900ms   ->  Commited:   1,325,924,352
1000ms  ->  Used:       1,080,348,112
1000ms  ->  Commited:   1,325,924,352
1100ms  ->  Used:       1,080,348,112
1100ms  ->  Commited:   1,325,924,352
1200ms  ->  Used:       2,261,768
1200ms  ->  Commited:   1,325,924,352
1300ms  ->  Used:       2,261,768
1300ms  ->  Commited:   1,325,924,352

您可以看到,我的堆开始时的初始大小约为260MB,允许的最大大小(GC可能会决定调整您的堆大小的大小)约为3.7 GB。

在创建阵列之前,使用了大约6MB的堆。然后创建大数组,我的堆大小( promise 大小)增加到1,3GB,使用了大约1GB(数组)。然后,我强制进行垃圾收集,以收集数组。但是,我的堆大小保持在1,3GB,因为GC不在乎再次收缩它,只是利用率降低了2MB。

-结果G1 :
Init:   262,144,000
Max:    4,179,623,936

0ms ->  Used:       2,097,152
0ms ->  Commited:   262,144,000
100ms   ->  Used:       2,097,152
100ms   ->  Commited:   262,144,000
200ms   ->  Used:       2,097,152
200ms   ->  Commited:   262,144,000
300ms   ->  Used:       2,097,152
300ms   ->  Commited:   262,144,000
400ms   ->  Used:       2,097,152
400ms   ->  Commited:   262,144,000
500ms   ->  Used:       1,074,364,464
500ms   ->  Commited:   1,336,934,400
600ms   ->  Used:       1,074,364,464
600ms   ->  Commited:   1,336,934,400
700ms   ->  Used:       1,074,364,464
700ms   ->  Commited:   1,336,934,400
800ms   ->  Used:       1,074,364,464
800ms   ->  Commited:   1,336,934,400
900ms   ->  Used:       1,074,364,464
900ms   ->  Commited:   1,336,934,400
1000ms  ->  Used:       492,520
1000ms  ->  Commited:   8,388,608
1100ms  ->  Used:       492,520
1100ms  ->  Commited:   8,388,608
1200ms  ->  Used:       492,520
1200ms  ->  Commited:   8,388,608

现在我们开始! G1 GC关心小堆!清除对象后,不仅利用率降低到约0.5MB,而且堆大小缩小为8MB(在ParallelOldGC中为1,3GB)

更多信息:

但是,请记住,堆大小仍将与任务管理器中显示的大小不同。 following image中的Wikipedia - Java virtual machine说明了堆只是完整JVM内存的一部分:

JVM memory

关于java - Java的RAM使用情况与任务管理器所说的不符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35539283/

相关文章:

c++ - 用c/c++实现实时最佳拟合内存分配算法

memory - 将数据存储在内存中 : Session vs Cache vs Static

python - 字典的 clear() 方法是否会从内存中删除所有与项目相关的对象?

java - 为什么Java在类加载过程中需要解析阶段?

java - 对 4GB JVM 和 3GB 缓存进行适当的 JVM/GC 调优

java - Wildfly 服务器、Maven、RestEasy(java.lang.ClassNotFoundException : org. jboss.resteasy.core.ResourceMethod)

java - Spring 4 请求驱动的 Bean 创建

java - Android java for循环突然中断

java - 让 javagent 使用 Java < 9 中的系统类加载器

java - 如何使用 hibernate-jpa2.0 抛出 OptimisticLockException