【JVM】GC(一):垃圾回收判断

GC是由JVM自动完成的,根据JVM系统环境而定,所以时机是不确定的。 当然,我们可以手动进行垃圾回收, 比如调用System.gc()方法通知JVM进行一次垃圾回收,但是具体什么时刻运行也无法控制。也就是说 System.gc()只是通知要回收,什么时候回收由JVM决定。 但是不建议手动调用该方法,因为消耗的资源比较大。

一般以下几种情况会发生垃圾回收:(1)当Eden区或者S区不够用了 (2)老年代空间不够用了 (3)方法区空间不够用了 (4)System.gc()

1.对象回收

堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)

大多数情况下,对象在新生代中 Eden 区分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次Minor GC。那 Minor GC 与 Full GC 有什么不同呢?

  • 新生代GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快
  • 老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC经常会伴随至少一次的Minor GC(并非绝对),Major GC的速度一般会比Minor GC的慢10倍以上

1.1 引用计数法

给对象添加一个引用计数器,每当有一个地方引用,计数器就加1。当引用失效,计数器就减1。任何时候计数器为0的对象就是不可能再被使用的。

这个方法实现简单,效率高,但是目前主流的虚拟机中没有选择这个算法来管理内存,最主要的原因是它很难解决对象之前相互循环引用的问题。所谓对象之间的相互引用问题。

在这里插入图片描述
看上面代码,除了对象a和b相互引用着对方之外,这两个对象之间再无任何引用。但是它们因为互相引用对方,导致它们的引用计数器都不为0,于是引用计数器法无法通知GC回收器回收它们。

1.2 可达性分析算法

这个算法的基本思想就是通过一系列的称为”GC Roots“的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的话,则证明此对象时不可用的。

  • 当一个对象达到GCRoots没有任何引用链的话,此对象不可用(从GR向下搜索)
  • GCRoots:一组必须活跃的引用
    • 虚拟机(栈帧中的本地变量表)中引用的对象,比如 f() {A a = new A();},那么在 f() 未完成调用前,A 都是 GC Root(暂时)
    • 本地方法栈中JNI(native)中引用的对象
    • 方法区中静态属性引用的对象,比如 class B {public static A = new A();},那么在JVM中 A 一直都是 GC Root
    • 方法区中常量引用的对象,比如 class B {public static final A = new A();},那么在JVM中 A 一直都是 GC Root

在这里插入图片描述

注:关于 GC Roots 的证明,可以参考这篇文章

1.3 finalize()

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。

1) 第一次标记并进行一次筛选

筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize方法,或者finzlize方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”,对象被回收。

2)第二次标记

如果这个对象被判定为有必要执行finalize() 方法,那么这个对象将会被放置在一个名为:F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。

这样做的原因是,如果一个对象finalize() 方法中执行缓慢,或者发生死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。


finalize() 方法是对象脱逃死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象要在finalize() 中成功拯救自己----只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。

  • 不可达对象 + 重写finalize( 为当前对象加链 ) = 自救
  • 不推荐

2.类信息回收

需要满足以下三个条件:

  • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例
  • 加载该类的 ClassLoader 已被回收
  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

虚拟机可以对满足上述3个条件的无用类进行回收,这里仅仅是”可以“,而并不是和对象一样不适用了就必然会被回收。

这里一定注意,元数据区也是存在GC并且可以动态扩展的,默认-XX:MetaSpaceSize值为21MB的高水位线。一旦触及FullGC将被触发卸载没有用的类,然后高水位线会被重置。新的高水位线取决于GC后释放的元空间。如果释放的空间少,这个高水位线则上升;如果释放空间过多,则高水位线下降。

3.常量回收

运行时常量池主要回收的是废弃的常量。那么,我们怎么判断一个常量时废弃常量呢?

假如在常量池中存在字符串"abc",如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 ”abc“ 就是废弃常量,如果这时发生内存回收的话而且有必要的话,”abc“ 会被系统清理出常量池。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A minor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值