我一直在通过制作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内存的一部分:
关于java - Java的RAM使用情况与任务管理器所说的不符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35539283/