在Java虚拟机中,给内存划分了三个代,根据不同的代使用不同的gc算法,在gc回收垃圾之前,我们应该先知道gc如何区分垃圾
GC如何区分垃圾
引用计数器
“引用计数”法通过统计控制生成对象和删除对象是的引用数来判断,在对象在添加一个引用计数器,每当有一个地方引用就+1,引用失效就-1,垃圾回收程序收集计数为0的对象即可。但是这种方法的缺陷是无法解决循环引用。
可达性分析
在主流垃圾判断算法中,都是从程序运行的根节点出发,遍历整个对象引用,查找存活的对象。
即通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
在Java语言中,可作为GC Roots的对象包括下面几种:
虚拟机栈(栈帧中的本地变量表)中引用的对象。
方法区中类静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中JNI(即一般说的Native方法)引用的对象。
虚拟机中共划分了三个代:年轻代(Young Generation)、年老代(Old Generation)和持久代(Permanent Generation)。
年轻代
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分为三个区。一个Eden区,两个Survivor区(一般而言)。
大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活将被复制到另外一个Survivor区,当这个Survivor区也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。
需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来的对象和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor区过来的对象。
而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
年老代
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
持久代
用于存放静态文件,如java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久空间来存放这些运行过程中新增的类。持久代大小通过 -XX:MaxPermSize = 进行设置。
分代回收算法
根据对象存活周期的不同将内存划分为几块,根据其特点采用最适合的收集算法。因为新生代的中每次垃圾回收时都会发现大量死亡对象,对存活下来的部分对象,采用复制算法。而年老代因为对象的存活率较高、没有额外的空间对其进行担保,就必须使用标记-整理或标记-清理的算法.