深入理解JAVA虚拟机学习笔记7——内存的分配与回收策略
今天的内容主要是内存的分配,其实前面已经介绍了很多,就当作是复习吧。
大家现在应该都知道了,JAVA虚拟机在堆中存放对象实例,所以对象的内存分配也主要是在堆上进行。
堆中又分新生代和老年代(有疑问的可以看一下以前的文章),对于新创建的对象,按照对象的大小,
大的对象直接进入老年代,小的对象进入新生代。
新生代一般又有两类区域,Eden区和Survivor区,Survivor区是用来存放垃圾收集后的存活对象的,
新创建的对象自然是进入Eden区,当Eden区没有足够区域的时候将进行一次垃圾回收。
下面让我们用代码来验证一下。
首先在IDEA的DEBUG Configuration中增加虚拟机的一些设置。
然后编写测试代码如下,
这里有一点需要注意,当最后没有设置-XX:+UseSerialGC参数的时候,运行程序,如下图所示,发现ParOldGen中并没有内存占用。
原来默认情况下的JDK8的新生代使用的Parallel Scavenge收集器,在该收集器下-XX:PretenureSizeThreshold参数是无效的,
所以这里设置-XX:+UseSerialGC,指定使用Serial垃圾收集器。
根据前几篇的内容,我们在参数中设置了堆的初始内存和最大内存都是90M,新生代(占1/3)和老年代(占2/3)应该分别占27M和60M,并且按照默认情况就应该是Eden:From Survivor:To Survivor为24M:3M:3M。
到底是不是这样呢,再次运行程序(方法1)。
程序结果表明,新生代大小27M,使用了5.8M,其中Eden区24M,From Survivor,To Survivor各3M;老年代有60M,使用了10M。
那么接下来,让我们运行如下程序(方法2)
运行结果如下,我们可以看到,由于第三次再分配9M空间的时候,超过了Eden区的范围,进行了一次垃圾回收,直接将18M分配到了老年代上。
前面已经介绍过,长期存活的对象进入老年代,晋升老年代的阈值可以通过-XX:MaxTenuringThreshold来设置。
这里我们先设置为1,运行如下代码(方法3),
运行结果如下,进行了垃圾回收,m2和m2_1加起来共有15M被分配到了老年代上。
另外还有几条规则:
1. 如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于等于该年龄的对象就可以直接进入年老代。
2.
在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor
GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor
GC,尽管这次Minor
GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。
这里就不一一展示了,感兴趣的朋友可以自己亲手试一下。
有两次名词,这里也直接介绍一下,Minor GC——新生代GC,指发生在新生代的垃圾收集动作。
Major GC/Full GC——老年代GC,指发生在老年代的垃圾收集动作。
喜欢文章或想一起学习的朋友可以关注我,给我点赞,我将会持续更新,有什么疑问或文中有不当之处请给我留言,真诚地希望能与大家一起交流探讨,学习进步。