Java 的 RAM 使用情况与任务管理器显示的不符
Java's RAM usage doesn't correspond to what the Task Manager says
我一直在玩 Java 的 JVM,制作一个 1024^3
(基本上是 1Gb)长度的字节数组。我使用任务管理器(查看进程)和这个小片段测量了数组创建之前、之后和数组被垃圾收集器销毁之后的 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 内存的值并不像 heap 内存的值,并且通常大于堆内存。有没有办法减少使用的本机内存,使其接近堆内存?
堆只是JVM 内存中的一个区域。对于共享库、代码、线程堆栈、直接内存和 GUI 组件等事物,JVM 在最大堆大小之上额外增加 200 - 400 MB 并不罕见。
因此,当 2 MB(MB = 兆字节,Mb = 兆位)的对象当时可能正在使用时,应用程序可以保留更多。
Is there a way to reduce the native memory used, so that it is close to the heap memory?
您可以使用旧版本的 Java,它倾向于使用更少的内存、更小的最大堆和 perm gen,使用更少的额外资源。
结论:
使用垃圾优先 (G1) GC(Java 9 中的默认 GC),此垃圾收集器还缩小了 堆大小(总而言之,与 ParallelOldGC(Java 7 中的默认 GC 和Java 8),这很少甚至从不缩小 堆大小!
一般:
你的基本假设是错误的。
您假设您的代码片段显示堆大小。这是不正确的。它显示 堆利用率 。这意味着 "How much space of my heap is used?"。 Runtime.getRuntime().totalMemory()
显示 堆大小 ,Runtime.getRuntime().freeMemory()
显示 空闲堆大小 ,它们的差异显示 堆利用率(已用大小).
您的堆以 初始大小 开始,具有 0 字节 利用率 因为尚未创建对象,并且 最大堆大小。 最大堆大小 描述允许垃圾收集器调整堆大小的大小(例如,如果没有足够的 space 用于非常大的对象)
作为创建空堆后的下一步,会自动加载一些对象(class 对象等),它们通常应该很容易适合初始堆大小。
然后,您的代码开始 运行 并分配对象。一旦你的 Eden space 中不再有 space(堆被分成年轻一代(Eden、survivor-from 和 survivor-to space)和年老一代,如果您对这些详细信息感兴趣,请查找其他资源),将触发垃圾回收。
在垃圾收集期间,垃圾收集器可能会决定调整堆的大小(如上文所述,在讨论 最大堆大小 时)。在您的情况下会发生这种情况,因为您的 初始堆大小 太小而无法容纳您的 1GB 对象。因此 heap size 增加了,介于 initial heap size 和 max heap size.[=20 之间=]
然后,在你的大对象死亡后,下一次 GC 可以 再次使堆变小,但它 不必 。为什么?它低于 最大堆大小 ,这是 GC 关心的全部。一些垃圾收集算法会再次缩小堆,有些则不会。
尤其是 ParallelOldGC,Java 7 和 Java 8 中的默认 GC,很少甚至从不收缩堆。
如果你想要一个 GC 也试图通过在垃圾收集期间缩小它来保持 堆大小 小,请尝试 garabage 优先 (G1) GC 通过设置 -XX:+UseG1GC
Java 标志。
示例:
这将以字节为单位打印出所有值。
您将了解两个 GC 的工作原理以及使用其中一个时使用了多少 space。
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 的堆。然后创建大数组,我的 heap size(承诺大小)增加到 1.3GB,使用了大约 1GB(数组)。然后我强制进行垃圾收集,收集数组。然而,我的 heap size 保持在 1.3GB,因为 GC 不关心再次缩小它,只是 utilization 下降到 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 关心小堆!清理对象后,不仅 utilization 下降到大约 0.5MB,而且 heap size 也缩小到 8MB(与ParallelOldGC 中 1,3GB)
更多信息:
但是,请记住,堆大小 仍然与任务管理器中显示的不同。 following image from Wikipedia - Java virtual machine说明堆只是整个JVM内存的一部分:
如果您使用 G1 收集器进行 GC,您可以减少堆大小,但本机内存大小不会减少。在某些应用程序中,分配的本机内存可能比实际堆多。本机堆遭受抖动。
我一直在玩 Java 的 JVM,制作一个 1024^3
(基本上是 1Gb)长度的字节数组。我使用任务管理器(查看进程)和这个小片段测量了数组创建之前、之后和数组被垃圾收集器销毁之后的 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 内存的值并不像 heap 内存的值,并且通常大于堆内存。有没有办法减少使用的本机内存,使其接近堆内存?
堆只是JVM 内存中的一个区域。对于共享库、代码、线程堆栈、直接内存和 GUI 组件等事物,JVM 在最大堆大小之上额外增加 200 - 400 MB 并不罕见。
因此,当 2 MB(MB = 兆字节,Mb = 兆位)的对象当时可能正在使用时,应用程序可以保留更多。
Is there a way to reduce the native memory used, so that it is close to the heap memory?
您可以使用旧版本的 Java,它倾向于使用更少的内存、更小的最大堆和 perm gen,使用更少的额外资源。
结论:
使用垃圾优先 (G1) GC(Java 9 中的默认 GC),此垃圾收集器还缩小了 堆大小(总而言之,与 ParallelOldGC(Java 7 中的默认 GC 和Java 8),这很少甚至从不缩小 堆大小!
一般:
你的基本假设是错误的。
您假设您的代码片段显示堆大小。这是不正确的。它显示 堆利用率 。这意味着 "How much space of my heap is used?"。 Runtime.getRuntime().totalMemory()
显示 堆大小 ,Runtime.getRuntime().freeMemory()
显示 空闲堆大小 ,它们的差异显示 堆利用率(已用大小).
您的堆以 初始大小 开始,具有 0 字节 利用率 因为尚未创建对象,并且 最大堆大小。 最大堆大小 描述允许垃圾收集器调整堆大小的大小(例如,如果没有足够的 space 用于非常大的对象)
作为创建空堆后的下一步,会自动加载一些对象(class 对象等),它们通常应该很容易适合初始堆大小。
然后,您的代码开始 运行 并分配对象。一旦你的 Eden space 中不再有 space(堆被分成年轻一代(Eden、survivor-from 和 survivor-to space)和年老一代,如果您对这些详细信息感兴趣,请查找其他资源),将触发垃圾回收。
在垃圾收集期间,垃圾收集器可能会决定调整堆的大小(如上文所述,在讨论 最大堆大小 时)。在您的情况下会发生这种情况,因为您的 初始堆大小 太小而无法容纳您的 1GB 对象。因此 heap size 增加了,介于 initial heap size 和 max heap size.[=20 之间=]
然后,在你的大对象死亡后,下一次 GC 可以 再次使堆变小,但它 不必 。为什么?它低于 最大堆大小 ,这是 GC 关心的全部。一些垃圾收集算法会再次缩小堆,有些则不会。
尤其是 ParallelOldGC,Java 7 和 Java 8 中的默认 GC,很少甚至从不收缩堆。
如果你想要一个 GC 也试图通过在垃圾收集期间缩小它来保持 堆大小 小,请尝试 garabage 优先 (G1) GC 通过设置 -XX:+UseG1GC
Java 标志。
示例:
这将以字节为单位打印出所有值。
您将了解两个 GC 的工作原理以及使用其中一个时使用了多少 space。
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 的堆。然后创建大数组,我的 heap size(承诺大小)增加到 1.3GB,使用了大约 1GB(数组)。然后我强制进行垃圾收集,收集数组。然而,我的 heap size 保持在 1.3GB,因为 GC 不关心再次缩小它,只是 utilization 下降到 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 关心小堆!清理对象后,不仅 utilization 下降到大约 0.5MB,而且 heap size 也缩小到 8MB(与ParallelOldGC 中 1,3GB)
更多信息:
但是,请记住,堆大小 仍然与任务管理器中显示的不同。 following image from Wikipedia - Java virtual machine说明堆只是整个JVM内存的一部分:
如果您使用 G1 收集器进行 GC,您可以减少堆大小,但本机内存大小不会减少。在某些应用程序中,分配的本机内存可能比实际堆多。本机堆遭受抖动。