类加载过程
时间:2019-09-03
本文章向大家介绍类加载过程,主要包括类加载过程使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
一个类从加载到内存开始,一直到被卸载结束,它的整个生命周期包括
加载
、链接(验证、准备、解析)
、初始化
、使用
、卸载
阶段
类初始化条件
1. 遇到new、putstatic、getstatic及invokestatic这4条字节码指令时,如果类没有初始化,则立即进行初始化
这4个命令分别代表实例化一个类、设置&读取一个静态字段(没有被final修饰)、调用类的静态方法
2. 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有初始化
3. 当初始化一个类的时候,发现其父类没有初始化
4. 当虚拟机启动时,需用将执行启动的主类(有main()方法的那个类)进行初始化
5. 当使用动态语言时,如果一个java.lang.invoke.MethodHandle实例最终的解析结果是
REF_getStatic、REF_putStatic、REF_invokeStatic句柄时,并且这个句柄对应的类没有初始化
加载
加载指的是把class字节码文件从各个来源通过" 类加载器 "装载入内存中
"加载"是"类加载"这个过程的一个阶段,是 “类加载”过程中最先开始进行的操作,加载阶段,虚拟机需要完成三件事:
1、 根据类的全限定名获取定义此类的二进制字节流;
2、 将这个字节流代表的静态存储结构转换为方法区的运行时数据结构;
3、 在方法区中为这个类生成一个java.lang.Class对象,作为方法区这个类的访问入口。
数组的加载跟普通类型加载有所不同,因为数组本身不是通过类加载器加载产生的,数组类是虚拟机自动生成的
但是数组的类型是通过类加载器完成加载的,数组类的创建过程需要遵循以下规则:
1、 如果数组的类型是引用类型,则引用类型需要使用递归来进行加载,
并且数组需要被加载该数组类型的类加载器的命名空间上进行标识;
2、 如果数组的类型不是引用类型,是基本数据类型,Java虚拟机将会把数组标记为与引导类加载器关联;
3、 数组的可见性与数组类型的可见性保持一致,如果数组类型是基本类型,则默认可见性为public。
验证
验证是为了保证class文件中的内容是符合虚拟机规范的二进制字节流,防止通过执行一些不安全的二进制字节流而导致虚拟机奔溃。
验证是类加载的第二个阶段,这个阶段也是持续时间最长(从阶段连续性来说)
这个阶段从加载开始进行,一直进行到解析阶段结束。
1. 文件格式验证
这一阶段主要验证字节流是否符合class文件格式规范,并且能被虚拟机处理,保证输入的字节流能被正确的解析并且存储在方法区
2. 元数据验证
第二阶段主要是进行语法分析,以保证class文件符合Java语法规范
3. 字节码验证
这个阶段是语义分析,是验证过程中最复杂的一个阶段
主要目的是通过数据流和控制流,确定语义是合法并且符合逻辑的
这个阶段主要是针对方法体进行分析,以保证方法在运行过程中不会出现危害虚拟机的操作
准备
准备阶段是为类变量分配内存并且设置初始化值的阶段
这些变量所使用的内存都在方法区分配。
这个阶段进行初始化的数据只有静态字段,并且是赋值初始化值(final修饰的字段除外),不是代码中定义的值。
public static int value = 123; //value在方法区分配内存,并且设置初始值0
public static final int value = 123; //在准备阶段将会被赋值123,并且不会引起类的初始化过程
解析
解析阶段是将常量池内的符号引用替换为直接引用的过程
符号引用就是字符串,这个字符串包含足够的信息,以供实际使用时可以找到相应的位置。
比如说某个方法的符号引用,如:“java/io/PrintStream.println:(Ljava/lang/String;)V”。里面有类的信息,方法名,方法参数等信息。
直接引用,可以理解为一个内存地址,或者一个偏移量。
比如类方法,类变量的直接引用是指向方法区的指针;
而实例方法,实例变量的直接引用则是从实例的头指针开始算起到这个实例变量位置的偏移量
当第一次运行时,要根据字符串的内容,到该类的方法表中搜索这个方法。
运行一次之后,符号引用会被替换为直接引用,下次就不用搜索了。
初始化
只对static修饰的变量或语句进行初始化
初始化阶段是类加载过程的最后一步,这个阶段才开始真正的执行用户定义的Java程序。
在准备阶段,变量已经赋过一次系统要求的初始值
而在初始化阶段,则需要为类变量(非final修饰的类变量)和其他变量赋值,其实就是执行类的<clinit>()方法
虚拟机会保证父类的<clinit>()方法先于子类的<clinit>()执行,java.lang.Object的<clinit>()方法是最先执行的
<clinit>()方法对于类和接口来说不是必须的,
如果类或接口中没有定义类变量,也没有静态语句块,那么编译器将不为这个类或者接口生成<clinit>()方法
原文地址:https://www.cnblogs.com/loveer/p/11456239.html
- 通过shell脚本生成数据统计信息的报表 (笔记65天)
- 物化视图全量刷新的简单测试(63天)
- Golang语言社区--Go操作CSV文件
- TiDB 源码阅读系列文章(四)Insert 语句概览
- 食品安全溯源区块链解决方案探索
- Docker 简介与安装
- 经典Java面试题收集
- 关于update语句的性能测试(62天)
- 物化视图刷新的问题及分析(61天)
- 使用python+机器学习方法进行情感分析(详细步骤)
- 关于primary key和foreign key的问题处理(60天)
- Git 进阶指南
- Python 文本挖掘:使用情感词典进行情感分析(算法及程序设计)
- 可能是地球上最好用的 Mac 词典工具
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- php+js实现裁剪任意形状图片
- keras实现theano和tensorflow训练的模型相互转换
- python中round函数如何使用
- PHP array_reduce()函数的应用解析
- php微信公众号开发之简答题
- php5.x禁用eval的操作方法
- php微信公众号开发之图片回复
- php微信公众号开发之答题连闯三关
- swoole_process实现进程池的方法示例
- golang实现php里的serialize()和unserialize()序列和反序列方法详解
- keras 实现轻量级网络ShuffleNet教程
- Python应用实现处理excel数据过程解析
- Python实现爬取并分析电商评论
- python中怎么表示空值
- Keras自动下载的数据集/模型存放位置介绍