类的生命周期
上图展示的是类生命周期流向
① 在与初始化时机相关的类装载时机问题上,Java 虚拟机规范并没有对其做严格的定义,这就使得 JVM 在实现上可以根据自己的特点提供采用不同的装载策略。
② 类的初始化:Java 虚拟机规范为类的初始化时机做了严格定义:"initialize on first active use"--" 在首次主动使用时初始化"。
首次主动使用的情形:
创建某个类的新实例时--new、反射、克隆或反序列化;
调用某个类的静态方法时;
使用某个类或接口的静态字段或对该字段赋值时(final字段除外);
调用Java的某些反射方法时
初始化某个类的子类时
在虚拟机启动时某个含有main()方法的那个启动类
除了以上几种情形以外,所有其它使用JAVA类型的方式都是被动使用的,他们不会导致类的初始化。
③ 类的卸载:只有没有任何引用指向Class对象的时候,这时候才会卸载类,结束类的生命周期
④ 在同一个classLoader里面是class实例只有一个。
根据ClassLoader的介绍,当第一次使用外部类时,ClassLoader就会把该类加载到JVM,然后就可以创建该类的实例,即对象。类一旦加载,就会一直驻留内存,直到应用终止。一个类在ClassLoader类中肯定只能加载一个。hashtable是用散列的方式来存储对象,散列算法采用的基数就是hashcode,也就是说根据某个对象的hashcode和某种算法从而找到table中的某个位置,取到这个位置存储的对象。在java中hashcode默认使用对象的内存地址的。 可以试一下:使用Class.forName()获取一个类的Class对象,多次获取每一次得到的hashcode必然一样
hashcode不同说明这个class实例重新加载了一次
如下:
A.class已经被加载hashcode为1 如果A.class不卸载 再次加载肯定失败 jvm不允许重复加载
除非A.class已经卸载 所以再次加载后产生一个新的A.class实例,其hashcode为2(当然还有可能为1)
两次的hashcode不同基本可以确定A.class被重新加载
⑤ ClassLoader所加载的东西是类的元信息,这些信息应该是被保留在一块特殊的内存空间内的,System.gc()是影响不了这块空间的。如果加载的元信息过多,这块空间一样会溢出,就是咱们平时见的out of memory。System.gc()应该是回收内存堆里的空间,不会来回收这里。(这块区域就是method area,方法区,所以sun的jdk调优要调整maxPermSize。而ibm的jdk没有独立的方法区,也是放在heap上)加载类,类只占用其本身说明的空间,没有多少。
A class or interface may be unloaded if and only if its class loader is unreachable. Classes loaded by the bootstrap loader may not be unloaded.
⑥ System.gc()是强制回收,但它并不是立即回收,它只是通知虚拟机你想进行一次强制内存回收,至于什么时候真正执行,这还得看虚拟机的策略。所以说它是强制的,但不是立即的。而jvm究竟什么时间进行,那是jvm自己的事,程序无权干涉
⑦ 垃圾收集器系统有自己的一套方案来判断哪个内存块是应该被回收的,哪个是不符合要求暂不回收的。垃圾收集器在一个Java程序中的执行是自动的,不能强制执行,即使程序员能明确地判断出有一块内存已经无用了,是应该回收的,程序员也不能强制垃圾收集器回收该内存块。程序员唯一能做的就是通过调用System. gc 方法来"建议"执行垃圾收集器,但其是否可以执行,什么时候执行却都是不可知的。没有引用也不一定回收的,仅仅是可能被回收。虚拟机垃圾收集的触发是由虚拟机自行决定的,
⑧ 堆很大而栈很小,但是我们碰到的总是堆溢出而不是栈溢出.因为你自己定义变量,怎么也多不到哪里去。
参考
java 类的生命周期
http://www.javaeye.com/topic/77006
解析 Java 类和对象的初始化过程
http://www.ibm.com/developerworks/cn/java/j-lo-clobj-init/index.html
SUN JVM的内存管理方式
http://www.javadby.com/ServletyuJSP/20071216/4398.html