垃圾回收器(Garbage Collection CG)
想要了解GC首先要思考GC需要完成的三件事情
1.哪些内存需要要回收
2.什么时候回收
3.如何回收?
对象已死
哪些内存需要回收?
1.引用计数法
- 每个对象都包含一个 引用计数器,用于存放引用计数(其实就是存放被引用的次数)
- 每当有一个地方引用此对象时,引用计数
+1
- 当引用失效( 比如离开了局部变量的作用域或是引用被设定为
null
)时,引用计数-1
- 当引用计数为
0
时,表示此对象不可能再被使用,因为这时我们已经没有任何方法可以得到此对象的引用了
这是一些网上的资料里经常会出现的解释.
客观的说,**引用计数法(Reference Counting)**的实现简单,判定效率也很高,如微软公司的COM技术,Python等都使用了引用计数算法进行内存管理,但在主流的java虚拟机里没有选用引用计数法管理内存,其主要原因是大家常提到的对象间的循环引用问题难以解决
在主流的商用语言都是通过**可达性分析算法(Reachability Analysis)**来判定对象是否存活,其基本思路是每个对象的引用都有机会成为树的根节点(GC Roots),可以被选定作为根节点条件如下:
- 位于虚拟机栈的栈帧中的本地变量表中所引用到的对象(其实就是我们方法中的局部变量)同样也包括本地方法栈中JNI引用的对象。
- 类的静态成员变量引用的对象。
- 方法区中,常量池里面引用的对象,比如我们之前提到的
String
类型对象。 - 被添加了锁的对象(比如synchronized关键字)
- 虚拟机内部需要用到的对象。
引用(Refrence)
在jdk1.2之前,Java中的引用定义很传统,如果引用类型的数据中存出的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用,这很纯粹,但一个对象只存在引用和未引用两种状态过于狭隘----假如现在的内存空间仍然足够,则对象仍能保存在内存中;当内存资源紧张时则对其回收.这种引用类型怎么描述呢?
jdk1.2之后java对引用的概念进行了扩充,即我们经常看到的**强引用(Strong Reference),软引用(Soft Reference),弱引用(Weak Reference),虚引用(Phantom Reference),**这四种引用强度一次逐渐减弱
强引用(Strong Reference)
在代码之中普遍存在(Object object = new Object()) 只要强引用存在,垃圾回收器永远不会回收被引用的对象
软引用(Soft Reference)
用来描述哪些有用但并非必要的对象,系统发生内存溢出异常之前,将会把这些对象列进回收范围进行第二次回收,第二次回收仍没有足够内存时,则抛出内存溢出异常,JDK1.2以后提供了SoftReference类来实现软引用
弱引用(Weak Reference)
同样用来描述非必要对象,被弱引用关联的对象只能生存到下一次垃圾回收发生前,当GC开始工作时无论内存是否足够,都会回收被弱引用关联的对象,JDK1.2以后提供了WeakReference类来实现弱引用
虚引用(Phantom Reference)
是最弱的一种引用关系,对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例,其目的再于能在对象被回收前收到一个系统通知,JDK1.2以后提供了PhantomReference类来实现虚引用
生存或者死亡__finalize()
即使在可达性分析算法中不可达的对象,也不是非死不可的,这时候他们暂时出于缓刑阶段,一个对象真正死亡,至少要经过两次标记过程,对象在进行可达性分析后发现没有与GC Roots相连接的引用链,此时会被标记并且进行一次筛选,筛选条件是此对象是否有必要执行finalize()方法,当对象没有重写(@Override覆盖)finalize()方法,或者finalize()方法已经被虚拟机调用,虚拟机视这两种情况为不执行
当对象被判定为执行finalize()方法时,对象会被放置在一个F-Queue的队列中,并在稍后由一个虚拟机自动建立的,低优先级FInalizer线程去执行它,虽然执行,但虚拟机并不会等待他运行结束(异步),这样处理是为了避免finalize()方法阻塞或挂起,将导致整个F-Queue中其他对象死等,甚至最终导致整个回收系统崩溃,
finalize()的运行代价高昂,不确定性大,无法保证每个对象的调用顺序,它能做的所有工作,使用try-finally或者其他方式都可以做的更好
回收方法区
在方法区中进行垃圾回收性价比一般比较低,在堆,尤其是新生代(分代模型Eden)中,常规的垃圾回收可以回收70%-95%的空间,远远大于永久代回收比率
永久代的垃圾回收主要分两部分(哪些需要回收?):废弃常量和无用的类,回收废弃常量与回收java堆中的对象非常类似,以常量池中字面量的回收为例,假如一个字符串"ABC"已经进入了常量池中,但是当前系统没有任何一个String对象是叫做"ABC",即没有任何String对象对其引用,这是发生内存回收,而且有必要的话,这个"abc"常量就会被系统清理出常量池
判断一个常量是否是废弃常量比较简单,但判断一个类是否是无用的类则苛刻许多,其主要条件有三个
1.类的所有实例都已被回收,
2.类的加载器(ClassLoader)被回收
3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方反射该类
虚拟机在一个类满足上述三个条件时,可以(即非必须)对类进行回收,是否对类进行回收,HotSpot提供了-Xnoclassgc参数对其进行控制