分代
算法是目前最jvm使用的算法。
分代算法:
对对象进行分代(分类)、
分代算法是在引用计数,标记清除,拷贝收集和标记整理收集器的基础上进行整合实现的一种算法。
对于对象有不同的特性,按照生命周期可将
内存中的对象分为三种,
1、夭折型对象:朝生夕灭的对象。生命周期极短的对象,如某一方法内部的局域变量,
循环内的零食变量
2、老不死对象:该对象的存活时间较长,但最终还是会死的。。如
缓存对象,数据库连接对象、单例对象等等
3.永生不灭型对象:该类对象一般一旦生成就几乎不会死,它们几乎永生不灭。
String池中的对象(享元对象)、加载过的类信息等等。
以上的三类对象对应的内存区域,
夭折对象和老不死对象对应于java堆,而不灭对象对应于方法区。对于java堆对应于java堆,jvm规范要求必须实现gc,而对于夭折对象和老不死对象来讲,死几乎是必然的。当然会有一些例外。而jvm规范对方法区的gc不做要求,所以假设一个jvm在方法区中没有实现gc,那么不灭的对象就是真的不灭了。
由于不灭的对象的生命周期过长,所以分代算法主要针对java堆设计,也就是针对夭折型对象和老不死对象来设计的。
java堆得对象回收(夭折对象和老不死对象)
有了以上的分析,我们看下分代算法如
何处理java堆的内存回收,也就是夭折对象与老不死对象的回收。
夭折对象,存活时间较短,对象的存活时间不能太长,使用拷贝收集算法。
copy中使用分配一个Eden区和两个survivor区,各占80%和10%。
故每次复制时,存活的对象占用内存的不能超过10%,否则将无法复制。
为了解决以上的问题,将堆分为两部分进行处理以上的三个区域为年轻带young generation。而剩下的部分为tenured generation区域。
老不死对象:这类对象的生命周期比较长大多是从young generation区域传过来的。
通常有两种情况会从新生代转到年老代区域。
1、在新生代中的区域中的每个对象都有一个年龄,当这些年龄熬到一定的值时,就会被转到tenured区域。
2、当young generation区域的存活的对象大于10%时,多余的对象会被转到年老代。
针对老不死对象的特性,显然不再适合使用复制算法,因为其存活率太高。故对于tenured区域只能使用标记整理算法(mark-compact collector)。
方法区的对象回收(不灭对象)
以上的两种情况已经解决了大部分的问题,gc算法主要关注java堆区域。以上已经包含了分代算法的全部内容。一下为对于永生不灭的对象的回收不属于分代算法。
不灭对象存在于方法区,在常用的
hotspot虚拟机中,方法区被称为永久带
开始时永久带是和年老代存放在一块的。后来由于方法去中的对象很少被
卸载,故将不灭带独立出来。
这一区域的gc与
Tenured generation的相似,没有备用仓库,故只能使用mark-compact collector算法清理。
回收时机
jvm进行gc时,并不是三个区域一起回收的,大部分时候都是指新生代。因此,gc按照回收的区域又分为不同gc(minor gc) ,一种是
全局gc(major gc 或 full gc)
他们针对的区域为:
普通gc(minor gc):只针对新生代区域的gc。
全局gc(major gc or full gc):针对所有分代的区域。(新生代,年老代,永久带)的gc。
由于年老代和永久带gc的效果不好。而且二者的内存使用的增长速度也慢。故需要使用标记/整理方法来gc。