对象内存的分配(三)

1 内存的回收和分配主要目标是堆中对象

Java自动内存管理最核心的功能是堆内存中对象的分配与回收。因为

①程序计数器、Java虚拟机栈、本地方法栈内存的分配和回收都具有确定性,一般在编译阶段就能确定需要分配的内存大小,并且由于都是线程私有,因此它们的内存空间都随着线程的创建而创建,线程的结束而回收。也就是这三个区域的内存分配和回收都具有确定性。

②但是堆是所有线程共享,虽然每个对象的大小在类加载的时候就能确定,但对象的个数只有在程序运行期间才能确定,而且各个对象的生命周期不同,需要针对不同生命周期的对象采用不同的内存回收算法。

2  对象优先在Eden区中分配

如下图所示,堆内存分为

 

2.1 新生代

  • Eden:内存创建时,首先会在Eden区中分配
  • Survior1(From):Eden满,在这里分配
  • Survior2(To)

三者比例为8:1:1(可以变化)据统计,绝大多数对象(98%以上甚至更高)生存周期都不长。

2.2 老年代

(1)大对象直接进入老年代

大对象直接进入老年代,所谓“大对象”就是指一个占用大量连续存储空间的对象,如数组。

当发现一个大对象在Eden区+Survior1区中存不下的时候就需要分配担保机制,把当前Eden区+Survior1区的所有对象都复制到老年代中去。

一个大对象能够存入Eden区+Survior1区的概率比较小,发生分配担保的概率比较大,而分配担保需要涉及到大量的复制,就会造成效率低下。

因此,对于大对象我们直接把他放到老年代中去,从而就能避免大量的复制操作

那么,什么样的对象才是“大对象”呢?

通过-XX:PretrnureSizeThreshold参数设置大对象

 

该参数用于设置大小超过该参数的对象被认为是“大对象”,直接进入老年代。

注意:该参数只对Serial和ParNew收集器有效

(2)长期存活的对象将进入老年代

新生代中的每个对象都有一个年龄计数器,当新生代发生一次MinorGC后,存活下来的对象的年龄就加一,当年龄超过一定值时,就将超过该值的所有对象转移到老年代中去。

使用-XXMaxTenuringThreshold设置新生代的最大年龄

 

设置该参数后,只要超过该参数的新生代对象都会被转移到老年代中去。

(3)相同年龄的对象内存超过Survior内存一半的对象进入老年代

如果当前新生代的Survior中,年龄相同的对象的内存空间总和超过了Survior内存空间的一半,那么所有年龄相同的对象和超过该年龄的对象都被转移到老年代中去。无需等到MaxTenuringThreshold中要求的年龄。

附:

Minor GC 和 Full GC的区别:

 

3 “分配担保”策略

当垃圾收集器准备要在新生代发起一次MinorGC时,首先会检查“老年代中最大的连续空闲区域的大小是否大于 新生代中所有对象的大小?”,也就是老年代中目前能够将新生代中所有对象全部装下?

(1)能装下:就进行MinorGC。

(2)装不下:垃圾收集器会进行一次预测:根据以往MinorGC过后存活对象的平均数来预测这次MinorGC后存活对象的平均数。(看MinorGC一次后还能装下不)

以往存活对象的平均数小于当前老年代最大的连续空闲空间(预测MinorGC后可以装下了),那么就进行MinorGC,虽然此次MinorGC是有风险的,担保失败了就会触发Full GC。

  • 以往存活对象的平均数小于当前老年代最大的连续空闲空间(预测MinorGC后可以装下了),那么就进行MinorGC,虽然此次MinorGC是有风险的,担保失败了就会触发Full GC
  •  平均数大于当前老年代最大的连续空闲空间(预测MinorGC后还是装不下),那么就对老年代进行一次Full GC,通过清除老年代中废弃数据来扩大老年代空闲空间,以便给新生代作担保。

这个过程就是分配担保。(自注:感觉就是——“你放心放对象吧,有我老年代给你担保,实在不行,我GC我自己,给你让出空间”)



猜你喜欢

转载自blog.csdn.net/fantalee/article/details/80803098