虚拟机学习系列 - 2 - 垃圾收集概述
目录
虚拟机学习系列 - 1 - 运行时数据区域
虚拟机学习系列 - 2 - 垃圾收集概述
虚拟机学习系列 - 3 - 垃圾收集算法
虚拟机学习系列 - 4 - 垃圾收集器
虚拟机学习系列 - 5 - 内存分配与回收策略
虚拟机学习系列 - 6 - JDK工具
虚拟机学习系列 - 附 - 虚拟机参数
虚拟机学习系列 - 附 - OQL(对象查询语言)
垃圾收集很关键的一个问题就是什么样的对象应该被收集,我想所有人都很在意这个事情
那么就要看看java里面有关对象存活的问题了
对于虚拟机来说,如何判断一个对象是有用没用该不该收集?
下面来看看虚拟机如何判断一个对象活着还是挂了
1.引用计数算法(Reference Counting)
给每个对象添加一个引用计数器,有地方引用到这个对象,那么计数器就+1,这个引用失效的时候计数器就-1。显然,计数器的数值=0的时候就是没有人用的时候,对象也就没有活着的意义了
2.根搜索算法(GC Root Tracing)
以 GC Root 这类对象为起点,从这些节点搜索,搜索走过的路径称为引用链(Reference Chain)。当一个对象无法通过任何引用链到 GC Root 时,则证明这个对象是不可引用的,也就是说它可以长眠了
我的理解就是 GC Root 与一个对象 无法构成连通图的时候,这个对象就可以等着被清理了
这里有个很关键的词, GC Root
什么可以作为 GC Root? 以下几种对象可以作为 GC Root
a.虚拟机栈(栈帧中的本地变量表)中引用的对象
b.方法区中的类静态属性引用的对象
c.方法区中的常量引用对象
d.本地方法栈中JNI的引用对象
为了记忆方便,下面给出一个根搜索算法的简单例子,和书中的一样《深入理解java虚拟机》
显然这里object1、2、3、4都是gc root可达,而object5、6、7则是不可达,虽然6、7也是被5引用的,但是他们三个都是等着被枪毙的
那么这些无用对象的命运最终会怎样呢,有的人会选择坐以待毙,有的人则奋起反抗
他们如何反抗,那就要提到一个刺眼的字样finalize
想必大家被面试的时候基本上都遇到过“final,finally和finalize有什么联系和区别”
我想他们的联系可能是他们都姓final吧,有p联系啊,又不是学英语背词根
下面就简单说一下finalize ,先挑关键的说,很不幸,关键内容不是他们反抗的部分
1.finalize 最多被执行一次
2.即使执行,也不保证一定会执行完
3.执行时可以自救,但是只有一次机会
对于第一条,大家都能很容易的接受,第三条是说:如果obj这个对象很不幸是要被枪毙的,但是他在finalize 里面实行了自救,那么它就复活了,但是如果他又被列入被枪毙的名单之中,那么他只能等死了,finalize 不会再被执行,这条也能勉强接受,已经多活了一次,又奢求什么呢
对于第二条,我觉得是最可气的,你要么就让我执行完要么就别让我执行,让我执行一半多憋屈啊
不过人家也是有道理的,比如你在里面有意无意的写了个死循环,你让虚拟机如何是好,情何以堪啊
之所以先列出了这三点,就是不想推荐大家使用finalize
finalize如何自救,其实很简单,让别人引用到自己就好了,当然你不要拿一个等死的人来就好。
例子就不写,反正也不推荐使用finalize
和垃圾回收相关的还有一个很重要的概念 - 引用
我们平时大多使用的引用方式是类似这样的A a = new A();
还有一些其他的引用方式,下面简单说明一下
引用分类
1.强引用(Strong Reference)
2.弱引用(Weak Reference)
3.软引用(Soft Reference)
4.虚引用(Phantom Reference)
像上面的例子,就是强引用。
只要强引用在,那么虚拟机就不会回收这个对象
弱引用呢,当系统将要发生内存溢出之前,将把这些对象列入回收范围并进行第二次回收, 如果内存依然不足,则抛出异常
软引用,短命鬼,只能活到下次垃圾回收
虚引用,对对象的生存时间没有影响,为对象设置虚引用关联唯一目的就是希望在这个对象被回收的时候收到系统通知(本人暂时一次也没有用过)
这里暂不对他们做更细致的讨论,如果有必要,以后单独写一篇博客讨论
最后说一下方法区的垃圾回收
其实方法区也是有垃圾回收的,只是效果不是很理想罢了
给出方法区对象的回收规则
对于常量,则与堆中对象回收类似
对于无用的类,要满足一下3点才行:
1.该类的所有实例都已经被回收
2.加载该类的ClassLoader被回收
3.该类对应的java.lang.Class没有在任何对方被引用(无法在任何地方通过反射调用)
最后贴出笔者笔记截图