您的位置:首页 > 编程语言 > Java开发

JVM类加载机制

2017-07-27 17:22 381 查看
虚拟机把描述类的数据从Class文件加载到内存,并多数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

类加载的步骤



1. 加载

查找并加载类的二进制文件(.class文件或者其他)。

a) 通过一个类的全限定名来获取其定义的二进制字节流。

b) 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

c) 在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。

2. 连接

a. 验证

验证二进制字节码的正确性

验证的内容

- 类文件结构的检查

类文件的结构检查主要验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理,该验证的主要目的是保证输入的字节流能正确地解析并存储于方法区之内。经过该阶段的验证后,字节流才会进入内存的方法区中进行存储,后面的三个验证都是基于方法区的存储结构进行的。

- 语义检查(元数据验证)

对类的元数据信息进行语义校验(其实就是对类中的各数据类型进行语法校验),保证不存在不符合Java语法规范的元数据信息。

例:比如final类不能被继承,final方法不能被覆盖

- 字节码验证

该阶段验证的主要工作是进行数据流和控制流分析,对类的方法体进行校验分析,以保证被校验的类的方法在运行时不会做出危害虚拟机安全的行为。

- 二进制兼容性检查

这是最后一个阶段的验证,它发生在虚拟机将符号引用转化为直接引用的时候(解析阶段中发生该转化,后面会有讲解),主要是对类自身以外的信息(常量池中的各种符号引用)进行匹配性的校验

例:类people调用car类的run方法,会检查这个方法存不存在,如果不存在会抛出NoSuchMethodException异常

b. 准备

准备阶段为类的静态变量赋予类型默认的初始值,这些内存都将在方法区中分配。

例:int 默认值是0,boolean默认值是false

c. 解析

解析阶段是把字符引用转换成真实引用(把Car.run引用转换为方法区run方法对应的物理地址)

3. 初始化

给类的静态变量赋予正确的初始值

例:

class Test{
private static Test test =new Test();
private static int count1;
private static int count2=0;
private Test(){
count1++;
count2++;
}
public Test getTest(){
return test;
}
}


这段代码在Test类加载各个时点对应的a的数值

准备:(给静态变量赋初始值)

test=null

count1=0

count2=0

初始化:自上往下按顺序给静态变量赋正确初始值

给test赋值test=Test对象对应的空间的物理地址(Test@5f1121f6)[此时调用了Test的构造函数count1=1,count2=1]

为count1赋值count1=1(count1没有初始值不改变);

为count2赋值count2=0(count2有初始值0,值由1=>0);

private static int count2=0;
//效果一样
private static int count2;
{
count2=0;
}


类加载的时机

创建类的实例

访问某各类或者接口的静态属性或者对该属性赋值。

调用类的静态方法

反射

初始化一个类的子类

JVM启动时被标记为启动类的类(main方法所在的类)

获取类的二进制流的方式

从本地的.class文件加载

网络上下载.class文件(java.net.UrlClassLoader类)

从zip或者jar包中加载class文件

从专有数据库中提取.class文件

将java源文件动态编译为.class文件

Class类

类加载把.class文件中的二进制数据读取到内存中,将其放在放在运行时数据区的方法区内,然后在堆区创建了一个class对象保存了这个类在方法区内的数据结构,可以作为方法区这些数据的访问入口(反射API)

并且只有JVM可以创建class对象;

类加载器

JVM自带的类加载器

根类加载器(启动类加载器)Bootstrap ClassLoader:它使用C++实现,它负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。启动类加载器是无法被Java程序直接引用的。

扩展类加载器:Extension ClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。

系统类加载器:Application ClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

用户自定义的类加载器

自定义类加载器需要继承自抽象类java.lang.ClassLoader,这些类加载器需要由启动类加载器加载到内存中之后才能去加载其他的类。

使用Applet的时候,就用到了特定的ClassLoader,因为这时需要从网络上加载java class,并且要检查相关的安全信息,应用服务器也大都使用了自定义的ClassLoader技术。(java.net.UrlClassLoader)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jvm java