虚拟机把描述类的数据从Class文件加载到内存,并对数据进行检验、转换解析和初始化,最终形成了可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。在Java语言里,类型的加载、连接和初始化过程都是在程序运行时期完成的。
类的生命周期:
类从被加载到虚拟机内存中开始,到卸载出内存,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initiallization)、使用(Using)和卸载(Unloading)这7个阶段。其中验证、准备、解析3个部分统称为连接(Linking),这七个阶段的发生顺序如下图:
有且只有以下四种情况必须立即对类进行”初始化”(称为对一个类进行主动引用):
- 遇到new、getstatic、putstatic、invokestatic这四条字节码指令时(使用new实例化对象的时候、读取或设置一个类的静态字段、调用一个类的静态方法)。
- 使用java.lang.reflet包的方法对类进行反射调用的时候。
- 当初始化一个类的时候,如果发现其负类没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,虚拟机会初始化主类(包含main方法的那个类)。
类加载的过程
加载
加载是类加载的第一个阶段。有两种时机会触发类加载:
1、预加载。虚拟机启动时加载,加载的是JAVA_HOME/lib/下的rt.jar下的.class文件,这个jar包里面的内容是程序运行时非常常常用到的,像java.lang.*、java.util.*、java.io.*等等,因此随着虚拟机一起加载。要证明这一点很简单,写一个空的main函数,设置虚拟机参数为"-XX:+TraceClassLoading"来获取类加载信息,运行一下:
1 [Opened E:\\MyEclipse10\\Common\\binary\\com.sun.java.jdk.win32.x86_64_1.6.0.013\\jre\\lib\\rt.jar] 2 [Loaded java.lang.Object from E:\\MyEclipse10\\Common\\binary\\com.sun.java.jdk.win32.x86_64_1.6.0.013\\jre\\lib\\rt.jar] 3 [Loaded java.io.Serializable from E:\\MyEclipse10\\Common\\binary\\com.sun.java.jdk.win32.x86_64_1.6.0.013\\jre\\lib\\rt.jar] 4 [Loaded java.lang.Comparable from E:\\MyEclipse10\\Common\\binary\\com.sun.java.jdk.win32.x86_64_1.6.0.013\\jre\\lib\\rt.jar] 5 [Loaded java.lang.CharSequence from E:\\MyEclipse10\\Common\\binary\\com.sun.java.jdk.win32.x86_64_1.6.0.013\\jre\\lib\\rt.jar] 6 [Loaded java.lang.String from E:\\MyEclipse10\\Common\\binary\\com.sun.java.jdk.win32.x86_64_1.6.0.013\\jre\\lib\\rt.jar] 7 [Loaded java.lang.reflect.GenericDeclaration from E:\\MyEclipse10\\Common\\binary\\com.sun.java.jdk.win32.x86_64_1.6.0.013\\jre\\lib\\rt.jar] 8 [Loaded java.lang.reflect.Type from E:\\MyEclipse10\\Common\\binary\\com.sun.java.jdk.win32.x86_64_1.6.0.013\\jre\\lib\\rt.jar] 9 [Loaded java.lang.reflect.AnnotatedElement from E:\\MyEclipse10\\Common\\binary\\com.sun.java.jdk.win32.x86_64_1.6.0.013\\jre\\lib\\rt.jar] 10 [Loaded java.lang.Class from E:\\MyEclipse10\\Common\\binary\\com.sun.java.jdk.win32.x86_64_1.6.0.013\\jre\\lib\\rt.jar] 11 [Loaded java.lang.Cloneable from E:\\MyEclipse10\\Common\\binary\\com.sun.java.jdk.win32.x86_64_1.6.0.013\\jre\\lib\\rt.jar] 12 ...
2、运行时加载。虚拟机在用到一个.class文件的时候,会先去内存中查看一下这个.class文件有没有被加载,如果没有就会按照类的全限定名来加载这个类。
加载时类加载过程的一个阶段,在加载阶段,虚拟机需要完成以下3件事情:
(1)通过一个类的全限定名来获取定义此类的二进制字节流
(2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
(3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
虚拟机规范对这三点的要求并不具体,因此虚拟机实现与具体应用的灵活度都是相当大的。例如第一条,根本没有指明二进制字节流要从哪里来、怎么来,因此单单就这一条,就能变出许多花样来:
· 从zip包中获取,这就是以后jar、ear、war格式的基础
· 从网络中获取,典型应用就是Applet
· 运行时计算生成,典型应用就是动态代理技术
· 由其他文件生成,典型应用就是JSP,即由JSP生成对应的.class文件
· 从数据库中读取,这种场景比较少见
总而言之,在类加载整个过程中,这部分是对于开发者来说可控性最强的一个阶段。
加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中。然后再内存中实例化