垃圾回收算法
约 1032 字大约 3 分钟
2025-07-05
Mimor GC:young区的GC,新生代
Major GC:old区的GC,老年代
Full GC:old区+young区:新生代和老年代
判断一个对象是否是垃圾
引用计数法
对于某个对象而言,只要应用程序中持有该对象的引用,就说明该对象不是垃圾,如果一个对象没有任 何指针对其引用,它就是垃圾。 **弊端 😗*如果AB相互持有引用,导致永远不能被回收。(A引用B,B引用A,而没有其他任何对象引用A或B)
可达性分析
通过GC Root的对象,开始向下寻找,看某个对象是否可达
能作为GC Root:类加载器、Thread、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量等
(就是能作为上帝视角的对象)
虚拟机栈(栈帧中的本地变量表)中引用的对象。 方法区中类静态属性引用的对象。 方法区中常量引用的对象。 本地方法栈中JNI(即一般说的Native方法)引用的对象。
什么时候回收垃圾?
GC是由JVM自动完成的,根据JVM系统环境而定,所以时机是不确定的。 当然,我们可以手动进行垃圾回收,比如调用System.gc()方法通知JVM进行一次垃圾回收,但是具体什么时刻运行也无法控制。也就是说System.gc()只是通知要回收,什么时候回收由JVM决定。但是不建议手动调用该方法,因为GC消耗的资源比较大。
手动调用System.gc();
老年代空间不够用了
新生代空间不够用了
方法区空间不不够用了
垃圾回收算法
标记-清除算法(最简单却最浪费时间)CMS用
- 标记:找出内存中需要清除的对象,并进行标记
此时堆中所有的对象都会扫描一次,从而确定需要清除的对象,比较耗时
- 清除:清除掉需要回收对象,释放出对应的空间
又要扫描一次
缺点:
- 标记和清除两个过程都比较耗时,效率不高
- 会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无 法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
标记-复制算法:(浪费一半内存,增加效率)新生代常用
将内存平分为两块空间,标记和上面一样,然后复制为标记对象至留白的空间(连续的存储),然后把原空间的所有对象一次全部清除
缺点:
- 空间利用率低
标记-整理算法:(实现很复杂)老年代常用
复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。更关键的是,如果 不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都有 100%存活的极端情况,所以老年代一般不能直接选用这种算法。
标记过程仍然与"标记-清除"算法一样,但是后续步骤不是直接对可回收对象进行清理,而是让所有存活 的对象都向一端移动,然后直接清理掉端边界以外的内存。
其实上述过程相对"复制算法"来讲,少了一个"保留区"
ps:(不同区对垃圾回收算法的选择)
- Young区:复制算法(对象在被分配之后,可能生命周期比较短,Young区复制效率比较高
- Old区:标记清除或标记整理(Old区对象存活时间比较长,复制来复制去没必要,不如做个标记再清理)