持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情
GC是什么?
GC:即垃圾收集,是指JVM用于释放那些不再使用的对象所占用的内存。
有了GC,程序员就不需要再手动的去控制内存的释放。
JAVA提供的GC功能可以自动监测对象是否超过了作用域,从而达到自动回收内存的目的,Java的GC会自动进行管理,调用方法:System.gc() 或者Runtime.getRuntime().gc();
如何判断对象可以被回收?
1. 引用计数法
在JAVA中,如果要操作对象则必须要用引用,任何对象被引用,引用计数器都会加1,当引用失败时,该对象的引用计数器则减1,如果对象的引用计数器为0,则表示该对象没有引用可以被回收了。
题外话:
这引用计数法,是不是可以理解为有没有人喜欢,如果没有人喜欢你,则判断为废物,则可以回收,反之亦然。
优缺点
优点:
- 实时更新,无需等到内存不够才回收,对象计数器的值为0则被回收。
- 更新对象计数器时,仅对该对象有影响。
缺点:
- 无法解决循环依赖的问题(重点)
- 浪费资源,内存足够,仍在计算,且对象的计数器维护需要一定的开销。
为了解决引用计数法的循环引用问题,Java 使用了可达性分析的方法。
2. 可达性分析(根搜索法)
通过一系列的“GC Roots”对象作为起点搜索。如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的。
要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记
过程。两次标记后仍然是可回收对象,则将面临回收。
哪些对象我们称之为"GC Roots"对象呢?
作为GC Roots对象那么它自身肯定得满足一个条件,那就是他自己一定在很长一段时间内都不会被GC回收掉。
GC RootS的类型:
-
虚拟机栈中的本地变量所引用的对象。
-
方法区中静态引用。
-
JNI中的引用。
垃圾回收算法
1. 标记清除算法(Mark-Sweep)
标记清除法最基础的垃圾回收算法,分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清
除阶段回收被标记的对象所占用的空间。
缺点:每次回收都需要遍历两次内存空间;容易产生大量内存碎片,会造成不连续的内存空间,当需要一块较大的内存时而无法满足,导致需要重新发送GC。
为了解决内存碎片化的问题,提出了复制算法
2. 复制算法
按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉。
优点:解决了内存碎片化的问题,且效率高。
缺点:利用率低,只能使用一半内存。
3. 标记整理法(Mark-Compact)
标记整理法分为两个阶段,标记和整理,标记阶段会先把存活的对象和可回收的对象标记出来。整理阶段会把存活的对象往内存一端移动,移动完对象后再清除存活对象边界之外的对象。
缺点:虽然解决了之前两种算法的问题,但是其效率是最低的,其不仅需要移动对象,还要重新维护移动对象的引用地址。
4. 分代收集法
分代收集法是目前大部分JVM所采用的方法,其核心思想是根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将GC堆划分为老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法。
- 新生代,采取复制算法。
目前大部分JVM的GC对于新生代都采取复制算法,因为新生代中每次垃圾回收都要回收大部分对象,即要复制的操作比较少,但通常并不是按照1:1来划分新生代。
- 老年代,采用标记整理算法,或者标记清除算法都可。