1.概述
Java内存运行时区域的各个部分里:
其中程序计数器、虚拟机栈、本地方法栈3各区域随线程而生,随线程而灭。栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。每个栈帧中分配多少内存基本上是在类结构定下来是就已知了,因此这几个区域的内存分配和回收都具备确定性,不需过多考虑。
而堆和方法区不一样,只有在程序处于运行期间才能你该知道创建哪些对象,这部分内存分配和回收是动态的,因此主要关注。
2.对象死亡判别
1.引用计数法
给对象中添加一个引用计数器,每当有一个地方引用时,计数器的值加1,;当引用失效时,计数器值减1,任何时刻计数器值为0的对象就是不会再被使用的对象。其实现简单,效率也较高,但是由于很难解决对象之间相互循环引用的问题。因此主流的Java虚拟机并没有这样来管理内存。
2.可达性分析
主流的实现都是可达性分析。基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点往下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连接,那么证明这个对象是不可用的。
可作为GC Roots的对象包括以下几种:
1.虚拟机栈(栈帧中的本地变量表)中引用的对象;
2.方法区中静态属性引用的对象;
3.方法去中场量引用的对象;
4.本地方法栈中(即一般所说的Native方法)引用的对象。
原因:GC管理的主要区域是Java堆,一般情况下只针对堆进行垃圾回收。方法区、栈和本地方法区不被GC所管理,因而选择这些区域内的对象作为 GC roots,被GC roots引用的对象不被GC回收。
如果要细化GC Roots,GC Roots则包括:
1,所有年老带对象
3,所有jni句柄
4,所有上锁对象
5,jvmti持有的对象
6,代码段code_cache
7,所有classloader,及其加载的class
8,所有字典
9,flat_profiler和management
10,最重要的,所有运行中线程栈上的引用类型变量。
3.引用
无论是引用计数法还是可达性分析,都谈的是“引用”,JDK1.2之前说Java的引用:如果reference类型的数据中存储的数值代表的是另一块内存的钱,就 成为这块内存代表着一个引用。因此JDK1.2后,将引用分为:强引用、软引用、弱引用、虚引用4种。4.生存死亡
public class FinalizeEscapeGC { public static FinalizeEscapeGC SAVE_HOOK = null; public void isAlive(){ System.out.println("yes,i am steal alive!"); } protected void finalize() throws Throwable{ 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 method executed. yes,i am steal alive! no, i am dead.
因为一个对象的finalize方法只会被系统自动调用一次,如果面临下一次回收,它的finalize()方法不会被再次执行,只有被回收。由于finalize()方法运行代价高昂,不确定性大,无法保证各个对象的调用顺序,因此不要使用。
5.回收方法区
虽然永久代的垃圾回收效率极低,但是也会回收,主要两部分内容:废弃常量呵呵无用的类。
废弃常量的回收和堆中的对象类似,也是看是否有引用。
无用的类的回收需要满足以下3个条件:
1.该类所有的实例已被回收,也就是Java堆中不存在该类的任何实例;
2.加载该类的ClassLoader已被回收;
3.该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的任何实例。
即使满足条件也只是可被回收,并不像对象一定会被回收,是否对类进行回收虚拟机有自己的参数控制。
在大量使用反射、动态代理、CGLib等ByteCode框架、动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备自动卸载类的功 能,以保证永久代不会溢出。