已死的对象就是不可能被任何途径使用的对象,有以下几种方法判断一个对象是否已经死了;
引用计数
给对象添加一个引用计数器,每当有一个地方引用他,计算器就加1;当引用失效时,计数器减1;任何时刻计数器为0的对象就是死的对象。
1. 这种方式被很多技术所采用,如FlashPlayer(AS3)、Python等,但是Java没有采用这种算法,原因是它很难解决对象之间相互循环引用的问题,例如 ObjectA.param=ObjectB,ObjectB.param=ObjectA,对象A和B相互引用但是除此之外他们再无任何其他引用,这样他们的引用计数都不为0,永远不会被回收
根搜索
java采用的是根搜索算法,这个算法的基本思路是通过一系列名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所经过的路径称为引用链,当GC Roots到一个对象不可达时,这个对象就是不可用的
1. Java中可作为GC Roots的对象包括:
1)虚拟机栈中引用的对象
2)方法区中类静态属性和常量引用的对象
3)本地方法栈中Native方法引用的对象
引用方式
简单描述引用就是一块内存中存储的数值代表的是另外一块内存的起始地址,则成这块内存代表着一个引用,这种描述很纯粹但是是不太正确的,可以作为理解时用
1. 引用分为强引用、软引用、弱引用、虚引用
1)强引用:如Object obj=new Object();只要强引用存在,GC肯定不会回收被引用的对象
2)软引用:非必需的对象,当系统要发生内存溢出之前,会把这些对象列入回收范围
3)弱引用:更加非必需的对象,弱引用关联的对象只能生存到下一次垃圾回收之前
4)虚引用:也叫幽灵引用或者幻影引用,它是最弱的一种引用,虚引用不会引用一个对象的生命周期,也无法通过一个虚引用获取一个对象实例,只是虚引用的对象被回收时会有一个通知返回回去
两次标记
- public class FinalizeEscapeGc {
- public static FinalizeEscapeGc SAVE_HOOK=null;
- public void isAlive() {
- System.out.println(”yes,i am still alive;”);
- }
- @Override
- protected void finalize() throws Throwable {
- // TODO Auto-generated method stub
- super.finalize();
- System.out.println(”finalize method executed!”);
- FinalizeEscapeGc.SAVE_HOOK=this;
- }
- public static void main(String[] args) throws InterruptedException {
- SAVE_HOOK=new FinalizeEscapeGc();
- SAVE_HOOK=null;
- System.gc();
- //因为finalize方法优先级很低,所以暂停0.5秒以等待它
- Thread.sleep(500);
- if(SAVE_HOOK!=null) {
- SAVE_HOOK.isAlive();
- }else {
- System.out.println(”no,i am dead;”);
- }
- SAVE_HOOK=null;
- System.gc();
- //因为finalize方法优先级很低,所以暂停0.5秒以等待它
- Thread.sleep(500);
- if(SAVE_HOOK!=null) {
- SAVE_HOOK.isAlive();
- }else {
- System.out.println(”no,i am dead;”);
- }
- }
- }
public class FinalizeEscapeGc { public static FinalizeEscapeGc SAVE_HOOK=null; public void isAlive() { System.out.println("yes,i am still alive;"); } @Override protected void finalize() throws Throwable { // TODO Auto-generated method stub super.finalize(); System.out.println("finalize method executed!"); FinalizeEscapeGc.SAVE_HOOK=this; } public static void main(String[] args) throws InterruptedException { SAVE_HOOK=new FinalizeEscapeGc(); SAVE_HOOK=null; System.gc(); //因为finalize方法优先级很低,所以暂停0.5秒以等待它 Thread.sleep(500); if(SAVE_HOOK!=null) { SAVE_HOOK.isAlive(); }else { System.out.println("no,i am dead;"); } SAVE_HOOK=null; System.gc(); //因为finalize方法优先级很低,所以暂停0.5秒以等待它 Thread.sleep(500); if(SAVE_HOOK!=null) { SAVE_HOOK.isAlive(); }else { System.out.println("no,i am dead;"); } } }
finalize简单来说就是 当垃圾回收器认为一个对象没有存在意义时,会调用该对象的这个方法,释放该对象在堆中占用的内存。此方法继承于始祖Object。
回收方法区
JVM规范中说过方法区(永久代)可以不实现垃圾回收,但是被上了免死金牌的永久代也是可以杀的,只是杀的少些而已,不像新生代,一次垃圾收集就可以杀掉70%~95%的对象
1.该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例
2.加载该类的ClassLoader已经被回收
3.该类对应的java.lang.Class对象没有在任何地方被引用,无法再任何地方通过反射访问该类的方法
在大佬使用反射,动态代理,CGLib等ByteCode框架,动态生成JSP以及OSGI这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。