目录
Java对象死亡判定
判定对象是否存活
引用计数算法
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的
优缺点
-
缺点
1、两个对象再无任何引用,实际上这两个对象已经不可能再被访问,但是它们因为互相引用着对方,导致它们的引用计数都不为零,引用计数算法也就无法回收它们
2、占用了一些额外的内存空间来进行计数 -
优点
原理简单,判定效率也很高
可达性算法
通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GCRoots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的
- 当前主流的商用程序语言(Java、C#,上溯至前面提到的古老的Lisp)的内存管理子系统,都是通过可达性分析(Reachability Analysis)算法来判定对象是否存活的
GC Roots的对象包括以下几种
GC Roots的对象包括以下几种
- 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等
- 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量
- 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用
- 在本地方法栈中JNI(即通常所说的Native方法)引用的对象
- Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器
- 所有被同步锁(synchronized关键字)持有的对象
- 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
引用
引入引用的目的
对于描述一些“食之无味,弃之可惜”的对象就显得无能为力。譬如我们希望能描述一类对象:当内存空间还足够时,能保留在内存之中,如果内存空间在进行垃圾收集后仍然非常紧张,那就可以抛弃这些对象
引用类型
- **强引用(Strongly Re-ference)**普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象
- **软引用(Soft Reference)**描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用
- **弱引用(Weak Reference)**描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用
- **虚引用(PhantomReference)**也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供了PhantomReference类来实现虚引用
不可达不代表死亡
宣告一个对象死亡,至少要经历两次标记过程
-
1、第一次标记,如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记,随后进行一次筛选
-
筛选的条件
筛选的条件是此**对象是否有必要执行finalize()**方法, -
【没有必要执行】假如对象**没有覆盖finalize()方法**,或者**finalize()方法已经被虚拟机调用过**,那么虚拟机将这两种情况都视为“没有必要执行”
-
【有必要执行】该对象将会被放置在一个名为**F-Queue的队列**之中,并在稍后由一条**由虚拟机自动建立的、低调度优先级的Finalizer线程去执行它们的finalize()方法**
-
2、第二次标记,
**finalize()方法是对象逃脱死亡命运的最后一次机会**,稍后收集器将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——**只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移出“即将回收”的集合**;**如果对象这时候还没有逃脱,那基本上它就真的要被回收了**。
-
**finalize()方法都只会被系统自动调用一次**,如果对象面临下一次回收,它的finalize()方法不会被再次执行
回收方法区
主要回收内容
废弃的常量和不再使用的类型
- 判定一个类型是否属于“不再被使用的类”
- 1、该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例
- 2、 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常是很难达成的
- 3、该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法
回收方法区相关JVM参数
- -Xnoclassgc
- -verbose:class
- -XX:+TraceClass-Loading
- +TraceClass-Loading
- +TraceClassUnLoading
回收方法区应用场景
在大量使用反射、动态代理、CGLib等字节码框架,动态生成JSP以及OSGi这类频繁自定义类加载器的场景中,通常都需要Java虚拟机具备类型卸载的能力,以保证不会对方法区造成过大的内存压力。