JVM系列:四、Java类加载机制总结
2016-05-16 22:35
633 查看
Java程序是运行与虚拟机之上的,程序中使用到的类元数据由虚拟机先加载进内存才可以使用。
虚拟机的类加载机制:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。
类的生命周期共分为5步
1.加载 -> 2.连接【2.1.验证 -> 2.2.准备 -> 2.3.解析】 -> 3.初始化 -> 4.使用 -> 5.卸载
Java规范规定只有四种情况必须立即对类初始化
遇到new、getstatic、putstatic、invokestatic4条指令时,若没被初始化,则先触发初始化(这4条指令即实例化对象,读取或设置类的静态字段,调用类的静态方法)
使用java.lang.reflect包的方法进行反射调用时,若没被初始化,则先触发初始化
初始化一个类时,其父类还未初始化时,父类进行初始化
虚拟机启动时,先初始化主类(main方法的那个类)
加载:主要作用是获取定义类的二进制字节流,转化为方法区的运行时数据结构,最后在堆中生成这个类的java.lang.Class对象,作为方法区中该数据结构的访问入口。
验证:主要是为了确保Class文件的字节流信息符合当前虚拟机的要求。如果不符合Class文件存储格式,会抛出一个java.lang.VerifyError异常或其子类异常。过程大致包括四个检验:文件格式验证、元数据验证、字节码验证和符号引用验证。
准备:正式为类变量分配内存并设置类变量初始值,这些内存都将在方法区中分配。
解析:该阶段是虚拟机将常量池中的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法。
初始化:初始化阶段是执行类构造器<clinit>()。<clinit>()是由类中所有类变量的赋值动作和静态语句块(static{}块)合并产生的,且父类的<clinit>()先执行。该方法是同步安全的。
对于Java虚拟机来说,类加载器分为两种:启动类加载器和其他类加载器,其中其他类加载器继承自java.lang.ClassLoader的类加载器。
对于开发者来说,类加载器分为三种:
启动类加载器:C++语言实现,虚拟机自身的一部分。放在<JAVA_HOME>\lib目录中。
扩展类加载器:由sun.misc.Launcher$ExtClassLoader实现,负责加载<JAVA_HOME>\lib\ext目录中或java.ext.dirs系统变量指定的目录的所有类库。
应用程序类加载器:由sun.misc.Launcher$AppClassLoader实现,也称为系统类加载器。
还可以自定义自己的类加载器。类加载器有层级关系的:
![](https://img-blog.csdn.net/20160516221817022)
这些层级之间并不一定是继承来实现的,而是以组合实现的。
加载过程:一个类加载器收到类加载的请求,先把请求委派给父类加载器去完成,父类加载器反馈自己无法完成这个加载请求(搜索范围中没有找到所需的类),子加载器才会尝试自己去加载。
如rt.jar中的类,都是在启动类加载器中加载的,无论哪个类加载器要加载这个类,最终都委派给启动类加载器,因此类在程序的各个类加载器环境中都是同一个类。
这种机制称为双亲委派模型,当也有不符合这种模型的情况,如OSGi。
OSGi是当前最热门的Java模块化标准,它能实现模块化热部署,实现热部署的关键是每一个程序模块(bundle)都有一个自己的类加载器,当需要更换一个bundle时就把bundle连同类加载器一个换掉以实现热替换。
虚拟机的类加载机制:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。
一、类加载的步骤
Java的类型加载和连接过程都是在程序运行期间完成的。类的生命周期共分为5步
1.加载 -> 2.连接【2.1.验证 -> 2.2.准备 -> 2.3.解析】 -> 3.初始化 -> 4.使用 -> 5.卸载
Java规范规定只有四种情况必须立即对类初始化
遇到new、getstatic、putstatic、invokestatic4条指令时,若没被初始化,则先触发初始化(这4条指令即实例化对象,读取或设置类的静态字段,调用类的静态方法)
使用java.lang.reflect包的方法进行反射调用时,若没被初始化,则先触发初始化
初始化一个类时,其父类还未初始化时,父类进行初始化
虚拟机启动时,先初始化主类(main方法的那个类)
二、类加载的过程
类加载包括加载、验证、准备、解析和初始化。加载:主要作用是获取定义类的二进制字节流,转化为方法区的运行时数据结构,最后在堆中生成这个类的java.lang.Class对象,作为方法区中该数据结构的访问入口。
验证:主要是为了确保Class文件的字节流信息符合当前虚拟机的要求。如果不符合Class文件存储格式,会抛出一个java.lang.VerifyError异常或其子类异常。过程大致包括四个检验:文件格式验证、元数据验证、字节码验证和符号引用验证。
准备:正式为类变量分配内存并设置类变量初始值,这些内存都将在方法区中分配。
解析:该阶段是虚拟机将常量池中的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法。
初始化:初始化阶段是执行类构造器<clinit>()。<clinit>()是由类中所有类变量的赋值动作和静态语句块(static{}块)合并产生的,且父类的<clinit>()先执行。该方法是同步安全的。
三、类加载器
虚拟机外部的应用程序可以用类加载器来通过类的全限定名获取此类的二进制字节流。比较两个类是否“相等”,前提是由同一个类加载器加载的。对于Java虚拟机来说,类加载器分为两种:启动类加载器和其他类加载器,其中其他类加载器继承自java.lang.ClassLoader的类加载器。
对于开发者来说,类加载器分为三种:
启动类加载器:C++语言实现,虚拟机自身的一部分。放在<JAVA_HOME>\lib目录中。
扩展类加载器:由sun.misc.Launcher$ExtClassLoader实现,负责加载<JAVA_HOME>\lib\ext目录中或java.ext.dirs系统变量指定的目录的所有类库。
应用程序类加载器:由sun.misc.Launcher$AppClassLoader实现,也称为系统类加载器。
还可以自定义自己的类加载器。类加载器有层级关系的:
这些层级之间并不一定是继承来实现的,而是以组合实现的。
加载过程:一个类加载器收到类加载的请求,先把请求委派给父类加载器去完成,父类加载器反馈自己无法完成这个加载请求(搜索范围中没有找到所需的类),子加载器才会尝试自己去加载。
如rt.jar中的类,都是在启动类加载器中加载的,无论哪个类加载器要加载这个类,最终都委派给启动类加载器,因此类在程序的各个类加载器环境中都是同一个类。
这种机制称为双亲委派模型,当也有不符合这种模型的情况,如OSGi。
OSGi是当前最热门的Java模块化标准,它能实现模块化热部署,实现热部署的关键是每一个程序模块(bundle)都有一个自己的类加载器,当需要更换一个bundle时就把bundle连同类加载器一个换掉以实现热替换。
相关文章推荐
- XP下使用虚拟机安装配置Solaris[多图]
- Java 6 JVM参数选项大全(中文版)
- VirtualBox虚拟机XP与宿主机Ubuntu互访共享文件夹
- Linux下三大免费桌面虚拟机评测
- 当存储无可用空间时无法启动虚拟机
- Windows 8虚拟机不能全屏的解决方法
- 使用VMware Workstation虚拟机保护数据
- 用 GNOME Boxes 下载一个操作系统镜像
- android 使用虚拟机安装apk(图文教程)
- 深入解析JVM对dll文件和对类的装载过程
- Vmware虚拟机的安装及配置方法
- 深入解析java虚拟机
- Java虚拟机JVM性能优化(二):编译器
- Java程序员必须知道的5个JVM命令行标志
- 如何配置Apache服务器中的虚拟机
- apache虚拟机配置步骤和修改访问端口的方法(虚拟机端口映射)
- unity3d发布apk在android虚拟机中运行的详细步骤(unity3d导出android apk)
- 用C语言来实现一个简单的虚拟机
- 解析wamp5下虚拟机配置文档