一篇文章总结幸运快三平台出租了JVM类加载机制

1.加载幸运快三平台出租 haozbbs.com Q1446595067
把二进制表示的Class文件读进JVM中
2.连接

验证(验证字节码的有效性)
准备(初始化静态变量的值)
解析(把符号引用转直接引用)

3.初始化
为静态变量赋值
二、什么时候需要开始加载类?

1.主动引用时需要加载类

使用new 关键字实例化对象
读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)
调用一个类的静态方法

2.被动引用时不需要加载类

在子类中引用父类的静态属性,不会触发子类的加载
通过数组定义来引用类,不会触发此类的初始化(会由虚拟机自动生成的、直接继承于Object的子类,创建动作由字节码指令newarray触发,含有数组中应有的属性和方法,都实现在这个类里)
常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,不会触发定义常量的类的初始化(加final)

3.关于接口的特殊说明
当一个类在初始化时,要求其父类全部都已经初始化过,但是一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候才会初始化
三、详细过程解析
1.加载

普通类的加载过程:

通过一个类的全限定名来获取定义此类的二进制字节流
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

数组类的加载过程:

如果数组的组件类型是引用类型,那就递归采用本节中定义的加载过程去加载这个组件类型,数组C将在加载该组件类型的类加载器的类名称空间上被标识
如果数据的组件类型不是引用类型,虚拟机会把数组C标识为与引导类加载器关联
数组类的可见性与它的组件类型的可见性一致,如果组件类型不是引用类型,那数组类的可见性将默认为publish

2.验证

目的:为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并不会危害虚拟机自身的安全。

检验动作

文件格式验证(魔数、主次版本、常量)
元数据验证(是否有父类、父类继承是否正确)
字节码验证(主要目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的)
符号引用验证(描述的全限定名是否能找到对应的类;类中是否存在符合方法的字段的描述以及简单名称所描述的方法和字段;访问性是否可被当前类访问)

关闭检验动作:-Xverify:none
3.准备

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配
这时候进行内存分配的仅包括类变量(被static修饰的变量),不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在java堆中
这里初始值是数据类型的零值,实际的值会在初始化阶段才会执行赋值,如果有加上final 那么在准备阶段该变量会直接初始化为属性所指定的值

4.解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程

引用的分类:

符号引用(以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可;与虚拟机实现的内存布局无关,引用的目标不一定已经加载到内存中;实现的内存布局可以各不相同,但它们能能接受的符号引用必须都是一致的,因为符号引用的字面量形式明确定义在Class文件格式中)
直接引用(直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄;直接引用和虚拟机实现的内存布局是相关的,同一符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同;有了直接引用,引用的目标必定已经在内存中存在)

发生解析的具体时间:

可在类被被加载器加载时就对常量池中的符号引用进行解析,也可等到一个符号引用将要被使用前才去解析
虚拟机实现可以对第一次解析的结果进行缓存,从而避免解析动作重复进行

解析的动作:

类或接口的解析(把一个从未解析过的符号引用N解析为一个类或接口C的直接引用)
字段解析(会先解析引用字段的类或接口的符号引用,解析后假设用C表示,再判断C类与C类父类中是否包含目标字段)
类与接口方法解析(先解析引用方法的类或接口的符号引用,解析成功用C表示这个类,再判断C类中是否包含目标方法)

5.初始化

在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源。

初始化阶段是执行类构造器<clinit>()方法的过程:

<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的
<clinit>()方法与类的构造函数不同,不需要显示调用父类构造器,因为在调用子类的<clinit>()前,父类方法已经执行完毕
父类<clinit>()方法先执行,意味着父类中定义的静态语句块要优先于子类的变量操作
<clinit>()方法不是必须的,如果没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为该类生成<clinit>()方法
接口有变量初始化赋值的操作,所以接口与类一样都会生成<clinit>()方法,接口的实现类在初始化时不会执行接口的<clinit>()方法
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁,同步,有多个线程去初始化一个类,那么只有一个线程去执行这个类的<clinit>()方法。

猜你喜欢

转载自blog.51cto.com/13864167/2140244