大家好,我们又见面了,我是你们的朋友全栈君。
深入探讨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内存分配策略的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/8892.html
微信扫一扫
支付宝扫一扫