垃圾回收思路:

  • 发现无用信息对象
  • 回收被无用对象占用的内存空间,使该空间可被程序再次使用。

关键词联想:(类型)堆和非堆,(人类成长)伊甸园-幸存者乐园-养老园 (垃圾分类) 可达性 和标记 (垃圾清理)标记清理 空间to 复制 All 标记整理 more 分代回收

1、JVM的内存结构

Jvm(Java虚拟机)主要管理两种类型内存:堆和非堆。 ##

1.1、堆-运行时数据区域

所有类实例和数组的内存均从此处分配,由垃圾回收器的自动内存管理系统回收

  • 堆内新生代和老年代。比例为1:2。
  • 老年代:主要存放应用程序中生命周期长的存活对象。
  • 新生代:一个Eden区和两个Survivor区,比例为8:1:1 Eden区存放新生的对象。Survivor存放每次垃圾回收后存活的对象

1.2、非堆:

JVM留给自己用的,包含方法区、JVM内部处理或优化所需的内存(如 JIT Compiler,Just-in-time Compiler,即时编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码。

1.3、形象记忆

来一张图有助于记忆

伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是它们唯一存在过的区域。

幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。

终身颐养园(Old Generation):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时,就会触发一次完全收集(Major-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。

ok,在看看垃圾收集的思路第一条发现无用信息对象

2、 可回收对象的判定

  • 引用计数算法
  • 可达性分析算法(根搜索算法)

2.1、引用计数算法

堆中每个对象实例都有一个引用计数。

当一个对象被创建时,且将该对象实例分配给一个变量,该变量计数设置为1。

当变量被赋值给其他对象时,计数加1(a = b,则b引用的对象实例的计数器+1)

但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1。

任何引用计数器为0的对象实例可以被当作垃圾收集。

当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1

优缺点

  • 优点: 简单,高效
    引用计数收集器可以很快的执行,交织在程序运行中。对程序需要不被长时间打断的实时环境比较有利。
  • 缺点: 无法检测出循环引用
    如父对象有一个对子对象的引用,子对象反过来引用父对象。这样,他们的引用计数永远不可能为0.
public class Main {
    public static void main(String[] args) {
        MyObject object1 = new MyObject();
        MyObject object2 = new MyObject();

        object1.object = object2;
        object2.object = object1;

        object1 = null;
        object2 = null;
    }
}

最后面两句将object1和object2赋值为null,也就是说object1和object2指向的对象已经不可能再被访问,但是由于它们互相引用对方,导致它们的引用计数器都不为0,那么垃圾收集器就永远不会回收它们。

2.2、可达性分析算法(根搜索算法)

这个算法比较像树,从根节点出发寻找下一个节点(引用),如果无法被寻找的节点被,清理掉!他的出现解决了循环引用的问题

java中可作为GC Root的对象

  • 虚拟机栈中引用的对象(本地变量表)
  • 方法区中静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中引用的对象(Native对象)

3. 垃圾回收算法

3.1、标记清除算法

过程
分标记阶段,清除阶段,其实标记阶段应该就是用了可达性分析算法,如果可达就标记,后面清除未标记的


优缺点:
优点:简单,容易实现
缺点:容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。

3.2、 复制算法 (Copying)

它的出现就是解决空间利用率不足的问题,所以第一步分区域
活动区:
在任意时间点,所有动态分配的对象都只能分配在活动区
空闲区:
当活动区快接近full的时候,触发GC,将存活的从活动区转移到空闲区,当然这个时候他们两的意义就交换了

优缺点

  • 优点:实现简单,运行高效且不容易产生内存碎片
  • 缺点:浪费内存
    总结
    适用于存活对象很少。回收对象多,毕竟需要copy嘛,存活越少效率越高

3.3、 标记整理算法 (Mark-Compact)

他才是标记清理算法的升级版,不用多说了,就是多了一个步骤整理
优缺点

  • 优点: 标记/整理算法不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价
  • 缺点: 低效
    总结
    适用用于存活对象多,回收对象少,回收对象一少,这样整理起来效率也高

3.4、分代回收算法(Generational Collector)

他则是copy+Mark-Compact整合版

你先想想之前两个回收算法的总结,就会很快明白分代回收的原理

3.4.1、 原理(过程)

所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。

  • 分配对象时,保存在Eden区
  • Eden区满,触发GC(Minor GC), 将Eden区存活对象复制到一个survivor0区,然后清空Eden区
  • 当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另一个survivor1区,然后清空eden和这个survivor0区
  • 此时survivor0区是空的,然后将survivor0区和survivor1区交换,即保持survivor1区为空, 如此往复
  • 当survivor1区不足以存放 eden和survivor0的存活对象时,就将存活对象直接存放到老年代。若是老年代也满了就会触发一次Full GC,也就是新生代、老年代都进行回收

参考深入理解Java垃圾回收机制

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐