我们怎么判断对象是否已经死亡呢?
引用计数算法
算法的优点
使用引用计数器,内存回收可以穿插在程序的运行中,在程序运行中,当发现某一对象的引用计数器为0时,可以立即对该对象所占用的内存空间进行回收,这种方式可以避免FULL GC时带来的程序暂停
算法的劣势
采用引用计数器进行垃圾回收,最大的缺点就是不能解决循环引用的问题,例如一个父对象持有一个子对象的引用,子对象也持有父对象的引用,这种情况下,父子对象将一直存在于JVM的堆中,无法进行回收,如图所示:
根搜索算法
引用
-
强引用
-
软引用
Object obj = new Object(); SoftReference<Object> softRef = new SoftReference<Object>(obj); System.out.println(obj); System.out.println(softRef.get()); // 对象通过设置为null让对象失去引用,方便GC obj = null; // 当内存不足时,会自动触发GC操作,这里就无需手动GC try { byte[] b = new byte[30 * 1024 * 1024]; } catch (Exception e) {} finally { System.out.println(obj); System.out.println(softRef.get()); }
-
弱引用
Object obj = new Object(); WeakReference<Object> weakRef = new WeakReference<Object>(obj); System.out.println(obj); // java.lang.Object@7852e922 System.out.println(weakRef.get()); // java.lang.Object@7852e922 // 对象通过设置为null让对象失去引用,方便GC obj = null; // 这里通过手动触发GC操作。否则内存充足的情况下很难自动触发GC System.gc(); System.out.println(obj); // null System.out.println(weakRef.get()); // null
-
虚引用
换句话说,设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除之前做必要的清理工作Object obj = new Object(); ReferenceQueue<String> queue = new ReferenceQueue<String>(); PhantomReference<String> pr = new PhantomReference<String>(obj, queue); System.out.println(pr.get()); // null
-
引用队列
引用队列可以配合软引用、弱引用及幽灵引用使用,当引用的对象将要被JVM回收时,会将其加入到引用队列中。通过引用队列可以了解JVM垃圾回收情况// 引用队列 ReferenceQueue<String> rq = new ReferenceQueue<String>(); // 软引用 SoftReference<String> sr = new SoftReference<String>(new String("Soft"), rq); // 弱引用 WeakReference<String> wr = new WeakReference<String>(new String("Weak"), rq); // 幽灵引用 PhantomReference<String> pr = new PhantomReference<String>(new String("Phantom"), rq); // 从引用队列中弹出一个对象引用 Reference<? extends String> ref = rq.poll();
finalize()方法
回收方法区
方法区其实也是需要被回收的,并不是说虚拟机永久代中不需要垃圾收集。而是方法区中的垃圾收集的效率很低,新生代中一次垃圾收集可以回收70%-95%的空间,而永久代中的下效率要远远低于此。
方法区中的回收主要包括两部分内容:废弃的常量和无用的类
-
废弃的常量主要包括两大类 字面量和符号引用
字面量比较接近Java语言中的常量概念。回收废弃的常量和回收Java堆中的对象类似,如:要回收字符串“s”,当系统中没有任何String类型的对象引用常量池中的"s"时,也没有其他地方引用这个字面量,如果发生内存回收,而且有必要的话,则会将该字符串清理出常量池中。其中包括文本字符串、被声明为final的常量值等 而符号引用属于编译方面的概念。常量池中的其他类、接口、方法、字段的符号引用也与此类似,包括
1.类和接口的全限定名
2.字段的名称和描述符
3.方法的名称和描述符 -
无用的类: 要判断一个无用的类的条件非常的苛刻,需要满足:
1.该类的所有实例都被回收,即:Java堆中不存在该类的任何实例
2.该类的Classloader已经被回收
3.该类对用的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问到该类的方法。
当满足以上三个条件时,也未必说是一定要被回收。也仅仅是可以。