GC是什么时候执行的, 如何判断对象是否可以被回收,

GC是什么时候执行的

 

垃圾回收是一种回收无用内存空间并使其对未来实例可用的过程。

Eden 区:当一个实例被创建了,首先会被存储在堆内存年轻代的 Eden 区中。

Survivor 区(S0 和 S1):作为年轻代 GC(Minor GC)周期的一部分,存活的对象(仍然被引用的)从 Eden 区被移动到 Survivor 区的 S0 中。类似的,垃圾回收器会扫描 S0 然后将存活的实例移动到 S1 中。

(译注:此处不应该是Eden和S0中存活的都移到S1么,为什么会先移到S0再从S0移到S1?)

死亡的实例(不再被引用)被标记为垃圾回收。根据垃圾回收器选择的不同,要么被标记的实例都会不停地从内存中移除,要么回收过程会在一个单独的进程中完成。

老年代: 老年代(Old or tenured generation)是堆内存中的第二块逻辑区。当垃圾回收器执行 Minor GC 周期时,在 S1 Survivor 区中的存活实例将会被晋升到老年代,而未被引用的对象被标记为回收。

老年代 GC(Major GC):相对于 Java 垃圾回收过程,老年代是实例生命周期的最后阶段。Major GC 扫描老年代的垃圾回收过程。如果实例不再被引用,那么它们会被标记为回收,否则它们会继续留在老年代中。

内存碎片:一旦实例从堆内存中被删除,其位置就会变空并且可用于未来实例的分配。这些空出的空间将会使整个内存区域碎片化。为了实例的快速分配,需要进行碎片整理。基于垃圾回收器的不同选择,回收的内存区域要么被不停地被整理,要么在一个单独的GC进程中完成。

垃圾回收中实例的终结

在释放一个实例和回收内存空间之前,Java 垃圾回收器会调用实例各自的 finalize() 方法,从而该实例有机会释放所持有的资源。虽然可以保证 finalize() 会在回收内存空间之前被调用,但是没有指定的顺序和时间。多个实例间的顺序是无法被预知,甚至可能会并行发生。程序不应该预先调整实例之间的顺序并使用 finalize() 方法回收资源。

  • 任何在 finalize过程中未被捕获的异常会自动被忽略,然后该实例的 finalize 过程被取消。
  • JVM 规范中并没有讨论关于弱引用的垃圾回收机制,也没有很明确的要求。具体的实现都由实现方决定。
  • 垃圾回收是由一个守护线程完成的。

 

如何判断对象是否可以被回收

jvm 虚拟机会通过 可达性分析法,从gc root开始向下搜索,被搜索走过的路径称为引用链,当一个对象没有被GC roots引用链连接时,则证明此对象不可用,那么虚拟机就判断这个对象是可以被回收的。

 

GC roots 的对象有哪些

1 虚拟机栈(栈帧中的本地变量表)中引用的对象

2 方法区中类静态属性引用的对象

3 方法区中常量引用的对象

4 本地方法栈中Native方法引用的对象

 

可达性算法中的不可达对象并不是立即死亡的,对象拥有一次自我拯救的机会。对象被系统宣告死亡至少要经历两次标记过程,第一次是经过可达性分析发现没有与GC roots 相连接的引用链,第二次是由虚拟机自动建立的Finallizer队列中判断是否需要执行finalize()方法。

当对象变成(GC roots)不可达是,GC会判断该对象是否覆盖了 finalize方法,若未覆盖,则直接将其回收,若对象为执行finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则对象”复活“。

每个对象只能触发异常finalize()方法

由于finalize()方法运行代价高昂,不确定性大,无法保证各个对象的调用顺序,不推荐使用。

 

posted @ 2021-03-07 21:21  余一洋  阅读(1033)  评论(0编辑  收藏  举报