糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > 深入理解Java虚拟机(第三弹)- JVM 内存分配与回收策略原理 从此告别 JVM 内存分配文盲

深入理解Java虚拟机(第三弹)- JVM 内存分配与回收策略原理 从此告别 JVM 内存分配文盲

时间:2019-10-19 07:37:57

相关推荐

深入理解Java虚拟机(第三弹)- JVM 内存分配与回收策略原理 从此告别 JVM 内存分配文盲

点击上方好好学java,选择星标公众号

重磅资讯、干货,第一时间送达今日推荐:打卡活动第二期来啦,100% 能获得奖品个人原创+1博客:点击前往,查看更多

虚拟机系列文章

深入理解 Java 虚拟机(第一弹) - Java 内存区域透彻分析

深入理解 Java 虚拟机(第二弹) - 常用 vm 参数分析

深入理解 Java 虚拟机-如何利用 VisualVM 对高并发项目进行性能分析

在前面的一篇文章深入理解Java虚拟机-如何利用VisualVM进行性能分析中讲到了一些关于JVM调优的知识,但是,其实,还是有一些问题没有非常清楚的可以回答的,这里先给出几个问题,然后,我们再展开这篇文章需要讲解的知识。

我们生成的对象最开始在哪分配?Eden?Survivor?还是老年代呢?

进入到老年代需要满足什么条件呢?

接下来,我们就带着这两个问题展开全文。

1 对象优先在哪分配

其实,通过前面几篇文章的讲解,这个问题其实已经见怪不怪了,在大多数的情况下,对象都是在新生代Eden区分配的,在前面的文章我们提到,在Eden区中如果内存不够分配的话,就会进行一次Minor GC。同时,我们还知道年轻代中默认下Eden:Survivor0:Survivor2 = 8:1:1,同时,还能通过参数-XX:SurvivorRatio来设置这个比例(关于这些参数的分析都可以查看这篇文章:深入理解Java虚拟机-常用vm参数分析)。

下面我们通过一个例子来分析是不是这样的。

1.1 实例

给定JVM参数:-Xms40M -Xmx40M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=4

前面三个参数设置Java堆的大小为40M,新生代为10M,紧跟着后面两个是用于输入GC信息。更多参数可以查看这篇文章:深入理解Java虚拟机-常用vm参数分析。

1/**2*@ClassNameTest_013*@Description参数:-Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:+UseSerialGC -XX:SurvivorRatio=84*@Author欧阳思海5*@Date/12/316:006*@Version1.07**/8publicclassTest_01{910privatestaticfinalintM=1024*1024;1112publicstaticvoidtest(){13byte[]alloc1,alloc2,alloc3,alloc4;14alloc1=newbyte[5*M];15alloc2=newbyte[5*M];16alloc3=newbyte[5*M];17alloc4=newbyte[10*M];1819}publicstaticvoidmain(String[]args){22test();23}2425}

输入结果:

分析

eden:from:to=8:1:1,这个因为前面设置了参数-XX:SurvivorRatio=8

新生代分配了20M的内存,所以前面三个byte数组可以分配,但是,分配第四个的时候,空间不够,所以,需要进行一次Minor GC,GC之后,新生代从12534K变为598K

前面在新生代分配的内存Minor GC之后,进入到了Survivor,但是,Survivor不够分配,所以进入到了老年代,老年代已用内存达到了50%

1.2 回答问题

所以,经过上面的例子我们发现,对象一般优先在新生代分配的,如果新生代内存不够,就进行Minor GC回收内存。

2 进入到老年代需要满足什么条件

先给出答案,分为几点。

条件①:大对象直接进入到老年代

条件②:长期存活的对象可以进入到老年代

条件③:如果在Survivor空间中相同年龄所有对象的大小的总和大于Survivor空间的一半,年龄大于等于该年龄的对象直接进入到老年代

2.1 分析条件①

哪些属于大对象呢?

一般来说大对象指的是很长的字符串及数组,或者静态对象

那么需要满足多大才是大对象呢?

这个虚拟机提供了一个参数-XX:PretenureSizeThreshold=n,只需要大于这个参数所设置的值,就可以直接进入到老年代。

step1:解决了这两个问题,首先,我们不设置上面的参数的例子,将对象的内存大于Eden的大小看看情况。

1/**2*@ClassNameTest_013*@Description参数:-Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC4*@Author欧阳思海5*@Date/12/316:006*@Version1.07**/8publicclassTest_01{910privatestaticfinalintM=1024*1024;1112publicstaticvoidtest(){13byte[]alloc1,alloc2,alloc3,alloc4;14//alloc1=newbyte[5*M];15//alloc2=newbyte[5*M];16//alloc3=newbyte[5*M];17alloc4=newbyte[22*M];1819}publicstaticvoidmain(String[]args){22test();23}2425}

我们发现分配失败,Java堆溢出,因为超过了最大值。

step2:下面我们看一个例子:设置-XX:PretenureSizeThreshold=104,857,600,这个单位是B字节(Byte/bait),所以这里是100M

1/**2*@ClassNameTest_013*@Description参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:PretenureSizeThreshold=104,857,6004*@Author欧阳思海5*@Date/12/316:006*@Version1.07**/8publicclassTest_01{910privatestaticfinalintM=1024*1024;1112publicstaticvoidtest(){13byte[]alloc1,alloc2,alloc3,alloc4;14//alloc1=newbyte[5*M];15//alloc2=newbyte[5*M];16//alloc3=newbyte[5*M];17alloc4=newbyte[500*M];1819}publicstaticvoidmain(String[]args){22test();23}2425}

发现新生代没有分配,直接在老年代分配。

注意:参数PretenureSizeThreshold只对SerialParNew两款收集器有效。

2.2 分析条件②

进入老年代规则:这里需要知道虚拟机对每个对象有个对象年龄计数器,如果对象在Eden出生经过第一次Minor GC后任然存活,并且能够被Survivor容纳,将被移动到Survivor空间中,并且年龄设置为1。接下来,对象在Survivor中每次经过一次Minor GC,年龄就增加1,默认当年龄达到15,就会进入到老年代。

晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。

在下面的实例中,我们设置-XX:MaxTenuringThreshold=1

1/**2*@ClassNameTest_013*@Description参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:MaxTenuringThreshold=14*@Author欧阳思海5*@Date/12/316:006*@Version1.07**/8publicclassTest_01{910privatestaticfinalintM=1024*1024;1112publicstaticvoidtest(){13byte[]alloc1,alloc2,alloc3,alloc4;14alloc1=newbyte[300*M];15alloc2=newbyte[300*M];16alloc3=newbyte[300*M];17alloc4=newbyte[500*M];1819}publicstaticvoidmain(String[]args){22test();23}2425}

从结果可以看出,from和to都没有占用内存,而老年代则占用了很多内存。

2.3 分析条件③

条件③是:如果在Survivor空间中相同年龄所有对象的大小的总和大于Survivor空间的一半,年龄大于等于该年龄的对象直接进入到老年代,而不需要等到参数-XX:MaxTenuringThreshold设置的年龄。

实例分析

1/**2*@ClassNameTest_013*@Description参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC4*@Author欧阳思海5*@Date/12/316:006*@Version1.07**/8publicclassTest_01{910privatestaticfinalintM=1024*1024;1112publicstaticvoidtest(){13byte[]alloc1,alloc2,alloc3,alloc4;14alloc1=newbyte[100*M];15alloc2=newbyte[100*M];16//分配alloc3之前,空间不够,所以minor GC,接着分配alloc3=900M大于Survivor空间一半,直接到老年代。17alloc3=newbyte[900*M];1819//alloc4=newbyte[500*M];}2223publicstaticvoidmain(String[]args){24test();25}2627}

输入结果:

分配alloc3之前,空间不够,所以minor GC,接着分配alloc3=900M大于Survivor空间一半,直接到老年代。从而发现,survivor占用0,而老年代占用900M。

3 总结

这篇文章主要讲解了JVM内存分配与回收策略的原理,回答了下面的这两个问题。

我们生成的对象最开始在哪分配?Eden?Survivor?还是老年代呢?

进入到老年代需要满足什么条件呢?

更多Java技术文章,尽在【好好学java】网站。网址:搜索好好学java或阅读原文可达!

如果觉得《深入理解Java虚拟机(第三弹)- JVM 内存分配与回收策略原理 从此告别 JVM 内存分配文盲》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。