一、判断回收对象算法
1、引用计数算法
给对象添加一个引用计数器,当有一个地方引用它时,计数值加1,当引用失效时,计数减1。计数为0的对象不可能再被使用,进行回收。
问题:很难解决对象直接相互循环应用问题,Java虚拟机未使用次算法。
2、可达性分析算法
基本思想:通过一系列的称为“GC Roots"的对象作为起始点,从这个节点往下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时(路径不可达),则证明此对象不可用。
可作为GC Roots的对象:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈(Native方法)引用的对象
二、垃圾收集算法
1、收集区域
垃圾回收机制是对JVM内存模型中堆和方法区中内存进行回收。堆分为新生代(Eden区,Survior区)、老年代(Old区),方法区也称永久代(Perm区)。
2、GC分类
JVM在进行GC时,并非每次都对上面三个内存区域一起回收的,大部分时候回收的都是指新生代,因此GC按照回收的区域又分了两种类型,一种是普通GC(minor GC),一种是全局GC(major GC or Full GC)。
- 普通GC:只针对新生代区域的GC。
- 全局GC:针对年老代的GC。
3、GC三大算法
- 复制算法(Copying)
- 标记清除算法/标记整理算法(Mark-Sweep)/(Mark-Compact)
- 分代收集(Generational Collection)
(1) 复制算法
① 年轻代中使用的是普通GC采用的是复制算法(Copying)
② 原理:
复制算法将新生代分为Eden区,Survivor1区(From区),Survivor2(To区),当Eden区内存已满后触发Minor GC,其中的对象如果存活将会被复制到To区,且对象年龄加1,当年龄增加到一定程度时,就会被移动到年老代中。Eden区和From区被清空,From区和To区交换他们的角色,保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,知道“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
③ 空间分配比列:(Eden区:From区:To区)为(8:1:1)。
因为Eden区对象一般存活率较低,一般的,使用两块10%的内存作为空闲和活动区间,而另外80%的内存,则是用来给新建对象分配内存的,一旦发生GC,将10%的活动区间与另外80%中存活的对象转移到10%的内存区间,接下来,将之前90%的内存全部释放,以此类推。
(2) 标记清除算法/标记整理算法
①老年代一般由标记清除或者标记清楚与标记整理的混合实现。
②标记清除算法(Mark-Sweep)
③ 标记整理算法(Mark-Compact)
标记整理算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
(3)复制算法和标记清除算法、标记整理算法对比
①内存效率:复制算法>标记清除算法>标记整理算法
②内存整齐度:复制算法=标记整理算法>标记清楚算法
③内存利用率:标记整理算法=标记清除算法>复制算法
复制算法由于浪费了大量空间用于复制存储,内存利用率低;标记整理算法需要标记所有的存活对象,整理所有存活对象引用的地址,内存效率低。(4) 分代收集算法(Generational Collection)
分代收集算法是目前大部分JVM的垃圾收集器采用的算法,对于新生代都采取Copying算法,对于年老代使用的是Mark-Compact算法。
注意,在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。
三、垃圾收集器
如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的实现。基于JDK1.7之前的HotSpot虚拟机,包含了下面几种收集器:
收集器的分类指标
- 作用区域:新生代还是老年带
- 线程:单线程还是多线程