类的加载过程
一,加载
定义:获取.class文件的字节流,转为方法区的数据结构,在内存中创建Class对象作为该类的数据访问入口
内容:【1】,根据类的全限定名获取二进制字节流
【2】,根据字节流代表的静态存储结构转化为方法区的运行时数据结构
【3】,在内存中生成该类的java.lang.Class对象,作为方法区该类的数据访问入口(注:内存指的不一定是堆, 如HotSpot虚拟机是存放在方法区)
从.class文件获取字节流方式:
【1】,zip包读取 如:jar,ear,war等格式文件
【2】,网络获取 如:Applet
【3】,运行时计算生成 如:动态代理,java.lang.reflect.Proxy中使用ProxyGenerator.generateProxyClass为特定接口生成形式为"*$Proxy"的代理类的二进制字节流
【4】,其他文件 如:jsp文件生成对应的Class类
【5】,数据库 如:中间件服务器SAP Netweaver
其他:
①,类加载过程中,在内存中创建Class对象,这里的内存不一定是堆内存,根据虚拟机而定,如:HotSpot虚拟机是存放在方法区
②,加载阶段和连接阶段交叉进行,加载未结束连接阶段的字节码验证可能就开始了
③,类加载可以分为非数组类和数组类加载
非数组类:通过1,系统提供的类加载器 2,用户自定义的类加载器(重写类加载器的loadClass方法)
数组类:不使用类加载器创建,由虚拟机直接创建
数组类创建规则:1.如果数组组件是引用类型,数组会在加载该引用类型的类加载器的类名称上进行标识
2.如果数组组件不是引用类型,java虚拟机将数组标记为和引导类加载器关联
3.数组可见性和它的组件类型一致,如不是不是引用类型默认是public
二,验证(连接)
定义:对Class文件的字节流包含的信息进行验证是否符合虚拟机要求,避免危害虚拟机安全
为什么需要验证:Class文件不一定都是Java源码编译而来,例如:使用十六进制编辑器直接编写Class文件,而这些非Java源码编译而来的Class文件不一定格式正确
内容:【1】,文件格式验证 (文件)
检查字节流是否符合Class文件格式规范,是否能被当前版本的虚拟机处理
如: ①,是否已魔数0xCAFEBABE开头
②,主次版本是否在当前虚拟机处理范围之内
③,常量池是否有不支持的常量类型
④,指向常量的索引值是否指向不存在的常量和不符合类型的常量
⑤,CONSTANT_Utf8_info型的常量是否符合UTF8编码
... ...
【2】,元数据验证 (数据类型)
对字节码信息进行语义分析,是否符合Java语言规范
如:①,是否有父类(除java.lang.Object)
②,父类是否继承了不允许被继承的类(final修饰类)
③,非抽象类是否实现了父类和接口中要求实现的所有方法
④,类的字段和方法是否和父类出现冲突(如:覆盖父类final字段,错误的重载等)
... ...
【3】,字节码验证 (方法)
通过数据流和控制流分析,确定程序语义是否合法
如:①,操作数栈的数据类型与指令代码序列都能配合工作不会出现类型不一致的情况
②,跳转指令不会跳转到方法体外
③,方法体内的类型转换是有效的,如将父类对象赋值给子类数据类型是不合法的
关闭StackMapTable对于字节码验证优化配置:
-XX:-UseSplitVerifier
-XX:+FailOverToOldVerifier
【4】,符号引用验证
对类自身以外的信息进行匹配性校验
目的:确保解析动作能正常执行
发生的时间:解析阶段中虚拟机将符号引用转为直接引用的时候进行符号引用验证
校验内容:①,符号引用中通过字符串描述的全限定名是否能找到对应的类
②,指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段
③,符号引用中的类,字段,方法的访问性是否可被当前类访问
... ...
可能的错误:java.lang.NoSuchFieldError, java.lang.NoSuchMethodError等
三,准备(连接)
定义:为类的静态变量在方法区分配内存,设置初始零值
其他:
public static final int value = 123; //准备阶段:设置为123
public staitc int value = 123; //准备阶段:设置为0,将value赋值为123的putstatic指令编译后存放于类构造器<clinit>()方法中, 初始化阶段:设置为123