垃圾收集(Garbage Collection), 简称 GC,是 Java 语言一个成名特性,使它摆脱了 C、C++ 那样手动管理内存的痛苦,提到垃圾收集,必然想到它是干什么的?简单来说,它是我们管理堆内存和方法区上的空间的好助手,要想对垃圾收集建立最基本的认识,最起码能够回答:
回答这些问题必须知道 Java 的垃圾回收是按代的垃圾回收机制。Java 里面没有显示的注销内存的方式,有人可能说 Java 里面有 finalize() 方法,但是这个方法绝对不是 C++ 中的析构函数,而且执行的时机也是不确定甚至是否执行也是未知的,也有可能使用 System.gc(),但是这个方法会显著的影响系统性能,不建议过多使用。
首先简略的回答下上面的三个问题。1. 一般发现空间不够或者其它时机会触发 GC,GC 又分为 minor GC/full GC, 下面会详细展开说。 2. Java 回收那些从 GC root 开始不可达的对象 3. 主要做的就是停止线程,标记内存,有的会复制清理,有的会标记清理,取决于具体的垃圾回收算法。
上面只是一个粗浅的印象,下面来说说按代的垃圾回收机制。
我在 浅析 JVM 内存分区 中提到过 Java 堆分为新生代和老年代。
新生代的目标就是尽可能快速的收集掉那些生命周期短的对象,大多数对象可谓是朝生夕死,GC 的频率也比较高,总的来说它有 3 个空间。
Eden 空间和 Survior 空间的空间比例默认是 8:1,通过参数 - XX: SurvivorRatio=8 来控制,也可以设置为别的值。
对象没有变得不可达,并且能够从新生代的多次 GC 中存活下来,就会被拷贝到老年代,其占用的空间也比新生代要多,所以老年代内发生 GC 的次数明显要少得多,老年代的 GC 事件一般是在空间已满时发生,执行的过程根据 GC 类型的不同而有所区别。老年代满时触发 FullGC(Major GC),因为老年代中的对象比较 "能活",所以 FullGC 触发的频率较低。
具体来说,虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并且经历过一次 minor GC 仍然存活就会被转移到 Survivor,这时候它的 Age 也增加 1,对象在 Survivor 区每熬过一次 minor GC,年龄就增加 1,当增长到一定程度(默认 15 岁),就会晋升到老年代中,这个程度可以通过 - XX: MaxTenuringThreshold 设置。
当然按照年龄进入老年代也不是绝对的,虚拟机还支持动态年龄判定,当 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一半,年龄大于等于该年龄的对象就可以直接进入老年代,无需等到 MaxTenuringThreshold 设置的年龄。
在很多地方我们还能看到永久代的说法,其实就是 JVM 内存分区里的方法区,HotSpot 在 1.7 以前把方法区和堆放在一起做垃圾收集的,所以方法区又叫永久代。主要存放静态文件,如 Java 类、方法等。永久代对垃圾回收没有显著影响,但是现如今,例如 Spring 或者 JSP 都大量利用反射,动态代理,CGLib 生成大量的 Class,这时候我们需要设置一个比较大的永久代空间,防止方法区发生内存溢出。至于 1.8 已经使用 Metaspace 了。
上面说的是分代垃圾收集的思想,但是有个经常提到却还没有解答的问题,我们在每一次 GC 都会保留存活的对象,那么如何判断出哪些对象时存活的,哪些对象又是要清理的呢?这也是我们一开始提的问题中的一个,即垃圾收集回收什么对象?
顾名思义,引用计数法就是在每一个对象上绑定一个计数器,当有一个地方引用该对象时,引用计数值就加 1,引用失效时,计数值就会减 1,当计数值为 0 时,说明对象不再被使用,这时候就可以看作无效对象了。就我所知 C++ 的智能指针和 Objective C 中 ARC 都利用了引用计数,有关智能指针可以参考我的 C++11 智能指针 。
但是在 Java 虚拟机的实现中并没有采用引用计数法,其核心原因就是因为对象间的相互循环引用
- class C {
- public Object x;
- }
- C obj1、obj2 = new C();
- obj1.x = obj2;
- obj2.x = obj1;
- obj1、obj2 = null;
obj1 和 obj2 相互持有对方的引用,所以 GC 收集器无法回收它们。
在主流的支持 GC 的语言中,都是通过可达性分析来判断对象是否存活的。算法思想就是通过一系列的 GC Roots 作为起始点,然后往下搜索,能够连接到 GC Roots 的,都证明还是活的,如果断链子了,则证明不可达,也就是对象不是存活的。
来源: https://juejin.im/post/5a45b1d351882560b652a263