什么是垃圾回收?(Garbage Collection)

对于高级语言动态内存分配而言,一些对象随着程序线程的消亡不再进行使用,为了使程序效率更加高效,特有垃圾回收的技术来保证程序运行状态下能够健康的使用宿主机内存

如何判断对象已死,需要回收?

  1. 引用计数法:对于一个对象而言,在对象中添加一个计数器,如果有人引用这个对象,计数器加一,反之,计数器减一,当计数器的值为0的时候,表示对象已经无人在使用,需要回收,但引用计数法也有缺点:
    1. 无法解决循环引用
    2. 有更多的不确定性
  2. 可达性分析算法:基本思路通过一系列称之为“GC Root”的节点开始向下搜索,如果对象到“GC Root”之间路不通,那么表示这个对象需要被回收,用数据结构的思想去解释,“GC Root”与对象之间形成了有向无环图,如果从“GC Root”去进行遍历,无法到达对象节点,那么表示这个对象已经无人再去使用,可以被定义为“GC Root”的路径有以下几点:
    1. 虚拟机栈帧中本地变量表引用的对象,例如当前方法中的参数、局部变量、临时变量
    2. 方法区中类属性静态属性引用的对象,例如Java类的引用类型静态变量
    3. 方法区中常量引用的对象,例如字符串常量池里的引用
    4. 本地方法栈Native方法所引用的对象
    5. 虚拟机内部的引用,例如基本数据类型对应的class对象,常驻虚拟机的异常对象(NullPointerException、OutOfMemory),系统类加载器
    6. 被同步锁持有的对象
    7. 反映虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存

对象引用

  1. 强引用:不会被垃圾回收
  2. 软引用:当系统内存不足时,才会被回收,如果系统内存充足,不会被回收
  3. 弱引用:比软引用弱一些,只会存活到下一次垃圾回收
  4. 虚引用:不会对对象的存在造成影响,它存在的目的是为了系统在回收完一个对象之后能够得到一个系统通知

对象逃逸

在使用可达性分析算法标记一个对象需要回收之后,这个对象不会马上死亡,它暂时会处于缓刑状态,此时发生第一次标记,随后会进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,假如对象没有覆盖finalize()方法,或者finalize()方法已经被系统调用过,虚拟机都会认为没有必要执行finalize()方法;如果这个对象被判定为有必要执行这个方法,它会被放在一个F-Queue队列中排队,由虚拟机产生的另外一条线程执行finalize()方法,如果在这个方法中对象再次和“GC Root”取得连接,那么这个对象就会逃逸,finalize()方法是对象自救的最后一次机会

垃圾回收算法

分代收集理论

分代收集算法是当前商用虚拟机常用的垃圾回收算法,它基于分代假说而设立:

  • 弱分代假说:绝大多数对象都是朝生夕灭的
  • 强分代假说:熬过越多次垃圾回收的对象就越难以消亡

针对以上两个结论,将整个堆划分为老年代和新生代,根据对象年龄的不同,会进行代与代之间的迁移,老年代相对于垃圾回收的次数较为低频,新生代对于垃圾回收的次数较为高频,同时新生代又划分为Eden区和surviver区,surviver区又分为from、to,三者默认8:1:1的比例

标记-清除算法

虚拟机将当前没有被引用的对象占用的内存区域标记为回收,并直接清除,缺点会造成大量的内存碎片

标记-复制算法

虚拟机将当前的内存区域划分为两块,每次分配只在一半上进行分配,等待到垃圾回收时,会标记那些需要挥手的内存区域,将未被标记的内存区域统一复制到另一半中,同时清除之前那一半的所有内存,缺点是浪费内存空间

标记-整理算法

虚拟机将当前需要回收的区域进行标记,并清除,之前将连续的内存碎片进行移动,使其内存变得规整,优点是会解决标记清除算法导致的内存碎片,缺点是操作耗时,大量对象复制移动会导致垃圾回收的时间加长

png

png