Java垃圾回收器与内存分配策略( 二 )


内存分配策略

Java垃圾回收器与内存分配策略

文章插图
这里所说的内存分配,主要至的是在堆上的分配,一半的,对象的内存分配都是在堆上进行,但现代技术页支持将对象拆程标量类型(标量类型即原子类型,表示单个值,可以是基本类型或类型),然后在栈上分配,在栈上分配很少见,我们这里不考虑 。
Java内存分配和回收的机制概括的说,就是分代分配,分代回收 。对象根据存活的时间被分为:年轻代(Young )、老年代(Old )、永久代( ,也就是方法区) 。
年轻代(Young ):对象被创建时,内存的分配首先发生在年轻代(大对象可以直接 被创建在年老代),大部分的对象在创建后很快就不再使用,因此很快变得不可达,于是被年轻代的GC机制清理掉(IBM的研究表明,98%的对象都是很快消 亡的),这个GC机制被称为Minor GC或叫Young GC 。注意,Minor GC并不代表年轻代内存不足,它事实上只表示在Eden区上的GC 。
Minor GC:采用复制算法()
年老代(Old ):对象如果在年轻代存活了足够长的时间而没有被清理掉(即在几次 Young GC后存活了下来),则会被复制到年老代,年老代的空间一般比年轻代大,能存放更多的对象,在年老代上发生的GC次数也比年轻代少 。当年老代内存不足时, 将执行Major GC,也叫 Full GC 。
Java垃圾回收器与内存分配策略

文章插图
【Java垃圾回收器与内存分配策略】Full GC:标记-整理算法(Mark-)
Java垃圾回收器与内存分配策略

文章插图
年轻代上的内存分配是这样的,年轻代可以分为3个区域:Eden区(伊甸园,亚当和夏娃偷吃禁果生娃娃的地方,用来表示内存首次分配的区域,再 贴切不过)和两个存活区( 0 、 1) 。
绝大多数刚创建的对象会被分配在Eden区,其中的大多数对象很快就会消亡 。Eden区是连续的内存空间,因此在其上分配内存极快;
当Eden区满的时候,执行Minor GC,将消亡的对象清理掉,并将剩余的对象复制到一个存活区(此时,是空白的,两个总有一个是空白的);
此后,每次Eden区满了,就执行一次Minor GC,并将剩余的对象都添加到;
当也满的时候,将其中仍然活着的对象直接复制到,以后Eden区执行Minor GC后,就将剩余的对象添加(此时,是空白的) 。
当两个存活区切换了几次(虚拟机默认15次,用-XX:控制,大于该值进入老年代)之后,仍然存活的对象(其实只有一小部分,比如,我们自己定义的对象),将被复制到老年代 。
对象优先在Eden区分配 大对象直接进入老年代 长期存活的对象将进入老年代
第一次进入区域的时候对象年龄设置为1,对象在区域中每“熬过”一次,年龄增加一岁,当它的年龄增加到一定程度(默认为15岁),将会被晋升到老年代中 。
动态对象年龄判断
为了能更好地适应不同程序的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了才能晋升老年代,如果在空间中相同年龄所有对象大小的总和大于空间的一半,年龄大于或等于该年龄的对象就直接进入老年代,无须等到中要求的年龄 。
空间分配担保
在发生之前,虚拟机会先检查老年代最大可用的连续空间是否大于新手代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的 。如果不成立,则虚拟机会查看re设置值是否允许担保失败 。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将会尝试着一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者re设置不允许冒险(冒险:当出现大量对象在Minor GC后任然存活的情况,就需要老年代进行分配担保 ,把无法容纳的对象直接进入老年代),那这时改为进行一次Full GC 。