君子以自强不息。
引用计数法
在堆内存中分配对象时,会为对象分配一段额外的空间,这个空间用于维护一个计数器,如果对象增加了一个新的引用,则将增加计数器。如果一个引用关系失效则减少计数器。当一个对象的计数器变为0,则说明该对象已经被废弃,处于不活跃状态,可以被回收。
可达性分析法
将根集合作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象没有被任何引用链访问到时,则证明此对象是不活跃的,可以被回收。
JVM 垃圾回收采用了可达性分析法
垃圾回收算法主要有以下分类:
-
垃圾回收算法实现主要分为复制(Copy)、标记清除(Mark-Sweep)和标记压缩(Mark-Compact)。
-
在回收方法上又可以分为串行回收、并行回收、并发回收。
-
在内存管理上可以分为代管理和非代管理。
复制算法
复制算法的实现使用多个分区。
假设:把堆空间分为1个新生代(分为3个分区:Eden、Survivor0、Survivor1)、1个老生代的收集过程:
1、普通对象创建的时候都是放在 Eden 区,S0 和 S1 分别是两个存活区。第一次垃圾收集前 S0 和 S1 都为空,在垃圾收集后,Eden 和 S0 里面的活跃对象(即可以通过根集合到达的对象)都放入了 S1 区。
2、回收后 Mutator(指 Java 应用线程,这里的含义是因为线程运行,导致了内存的变化) 继续运行并产生垃圾,在第二次运行前 Eden 和 S1 都有活跃对象,在垃圾收集后,Eden 和 S1 里面的活跃对象(即可以通过根节点到达的对象)都被放入到 S0 区,一直这样循环收集。
标记清除
从根集合出发,遍历对象,把活跃对象入栈,并依次处理。处理方式可以是广度优先搜索也可以是深度优先搜索(通常使用深度优先搜索,节约内存)。标记出活跃对象之后,就可以把不活跃对象清除。标记清除算法最大的缺点就是使内存碎片化。
标记压缩
标记压缩算法是为了解决标记清除算法中使内存碎片化的问题,除了上述的标记动作之外,还会把活跃对象重新整理从头开始排列,减少内存碎片。