大家好,我们又见面了,我是你们的朋友全栈君。
深入探讨JVM内存分配策略:三大原则与担保机制JVM的内存分配机制包括三大原则和担保机制,具体如下:
首先,对象优先分配到Eden区。其次,大对象直接进入老年代。第三,长期存活的对象会被分配到老年代。最后,空间分配担保也是一个重要的机制。
对象优先在Eden区分配要如何验证这一原则呢?我们可以通过以下实验进行验证。
首先打印内存分配信息,代码如下所示:
public class A {
public static void main(String[] args) {
byte[] b1 = new byte[4*1024*1024];
}
}代码非常简单,创建了一个大小为4MB的Byte数组。我们在运行时添加虚拟机参数来打印垃圾回收信息。
-verbose:gc -XX:+PrintGCDetails
运行后,结果如下所示。
我们手动指定收集器,可以看到新生代使用的是Parallel Scavenge收集器。实际上,我们可以通过指定虚拟机参数来选择垃圾收集器,例如:
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC
运行结果如下:
虽然JDK默认的不是Parallel收集器,但JDK会根据各种环境调整使用的垃圾收集器。
要查看环境,可以使用以下代码:
java -version
因此,JDK根据服务器环境选择了Parallel收集器,而Serial收集器主要用于客户端。
验证Eden区分配我们观察到当前Eden区域为34432K,占用19%。如果我们将分配大小扩大10倍,Eden区是否还能容纳呢?我们来验证一下。
public class A {
public static void main(String[] args) {
byte[] b1 = new byte[40*1024*1024];
}
}运行结果如下:
因此,我们适当缩小每次分配的大小。我们限制Eden区域的大小,参数如下:
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
这里我们将内存大小限制为20M,Eden大小为8M。
然后我们运行以下代码:
public class A {
public static void main(String[] args) {
byte[] b1 = new byte[2*1024*1024];
byte[] b2 = new byte[2*1024*1024];
byte[] b3 = new byte[2*1024*1024];
byte[] b4 = new byte[4*1024*1024];
System.gc();
}
}运行结果如下:
原先的b1、b2、b3总共6M,被分配到了老年代。
原先的Eden区域如下图所示,在分配完b1、b2、b3后如下图所示。
此时我们发现已经无法继续分配了。
查看日志时,我们发现发生了两次GC。
在
[DefNew: 7129K->520K(9216K), 0.0053010 secs] 7129K->6664K(19456K), 0.0053739 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
中我们会看到,刚分配的对象并没有被回收。
上面的GC是针对新生代的,而下面的FullGC是针对老年代的。
如果我们此时要再分配4MB的内存,虚拟机会将原先的Eden区域放到可放的地方,即老年代。
因此会发生这种情况。
这就是整个过程,验证了对象优先在Eden区域分配。
大对象直接进入老年代指定大对象的参数如下:
-XX:PretenureSizeThreshold
测试代码如下:
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
public class A {
private static int M = 1024*1024;
public static void main(String[] args) {
byte[] b1 = new byte[8*M];
}
}运行结果如下:
我们可以看到,结果直接将8MB放到了老年代。而当我们将大小改为7MB时,
我们发现7MB全部被放到了Eden区。如果我们指定了参数,会发现结果变了。
参数如下所示:
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=6M
运行结果如下:
我们会发现7MB进入了老年代。
长期存活对象进入老年代---
参数如下:
-XX:MaxTenuringThreshold
每次进行回收时,如果对象没有被回收,那么对象的年龄会加1。
当对象的年龄达到阈值时,它会进入老年代。
具体测试与上述类似,这里就不再赘述了。
空间分配担保参数如下:
-XX:+HandlePromotionFailure
步骤如下:
首先,JVM会评估是否有足够的空间,然后才能进行分配。如果有能力放入,那么这个参数是‘+’号,表示开启了内存担保,否则是‘-’号,表示未开启。
总结:JVM的内存分配策略并不特别复杂,只要一步一步跟随虚拟机的操作,就可以理解其内存分配机制。
发布者:全栈程序员栈长,转载请注明出处:https://www.php.cn/link/ab4981fb59db07f87db4b1a7a2e9efcf
以上就是深入理解JVM内存分配策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号