jvm系列 (五) ---类的加载机制
类的加载机制
目录
什么是类的加载机制
- 简单的说,就是虚拟机把类的数据从Class文件加载到内存,对数据进行检验,转换解析和初始化,最终形成可以被虚拟机直接使用的java类型
类加载的过程
- 类加载的过程:加载(注意和类加载区别开来),验证,准备,解析,初始化
加载
- 通过一个类的全限定名来获取其定义的二进制流
- 将这个字节流所代表的静态存储结构(类信息,静态常量、常量)转化成方法区的运行时数据结构
- 在内存中生成一个代表这个类的Class对象,作为方法区中这些数据的访问入口
验证
- 目的:确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的安全。Class文件可以从任何途径来,包括我们手动编辑一个Class文件,如果我们不对他进行验证,那么就可能会出现安全问题。
- 文件格式验证:是否符合Class文件的格式的规范,能够被当前版本的虚拟机处理。通过这个验证,可以保证字节流能够正确解析并存储于方法区,格式上符合一个Java类型信息的要求。只有通过这个验证后,字节流才会进入内存的方法区进行存储。
- 元数据验证,对字节码描述的信息进行语义分析,比如这个类是否有父类,是否继承了final修饰的类,保证描述的信息符合java语言规范。
- 字节码验证,通过数据流和控制流分析,确定程序语义是和合法的,符合逻辑的。这个阶段对类的方法体进行验证分析,保证在类的方法运行时不会危害虚拟机。比如说验证类型转换是否合理,比如说父类不能转为子类
- 符号引用验证,对常量池中的各种符号引用的信息进行匹配性校验,比如验证符号引用中通过字符串描述的全限定名是否能找到对应的类。确保解析动作能够正常执行。
准备
- 为类的静态变量分配内存并设置类常量的初始值
public static int value = 123
- 这段代码经过准备阶段后,value的值是0
public static final int value = 123
- 被static和final同时修饰的,经过准备阶段后,value是123
解析
- 虚拟机将常量池内的符号引用(类和接口的全限定名、字段的名称和描述符、方法的名称和描述符)替换为直接引用(可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄)的过程
初始化
- 准备阶段变量已经赋过系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源。说白了就是初始化静态变量指定的值和执行静态代码块
必须初始化的情况
- 用new创建对象(准确来说是new指令)的时候
- 读取设置一个类的静态字段,调用类的静态方法
- 使拥reflect包的方法对类进行反射调用的时候
- 初始一个类的时候,如果一个类的父类还没有初始化的时候,触发父类的初始化
- 主类(main方法的类)
public class SuperClass
{
public static int value = 123;
static
{
System.out.println("SuperClass init");
}
}
public class SubClass extends SuperClass
{
static
{
System.out.println("SubClass init");
}
}
public class Test
{
public static void main(String[] args)
{
System.out.println(SubClass.value);
}
}
//输出:SuperClass init
123
- 通过子类来获取父类的静态字段,只会触发父类的初始化不会触发子类的初始化
public static void main(String[] args)
{
SuperClass s[]=new SuperClass[10];
}
- 并没有输出SuperClass init,也就是没有初始化这个类,通过反编译发现用的指令是newarray,所以并没有触发初始化,如果不是创建数组而是创建对象的话,会输出SuperClass init,通过反编译发现指令是new而不是newarray
class ConstClass
{
public static final String HELLOWORLD = "Hello World";
static
{
System.out.println("ConstCLass init");
}
}
public class TestMain
{
public static void main(String[] args)
{
System.out.println(ConstClass.HELLOWORLD);
}
}
- 并没有输出ConstClass init,也就是说明并没有触发该类初始化,这是因为在编译阶段通过常量传播优化,这个字符串已经存储在NotInitialization类的常量池中,也就是实际上对这个常量的引用变为NotInitialization类对自身常量池的引用了
类加载器
- 通过一个类的全限定名来获取其定义的二进制字节流的代码模块称为类的加载器
一个类需要加载他的类加载器和这个类本身来确定在虚拟机的唯一性,也就是说两个类来源一个Class文件,如果加载他们的类加载器不同,那么这两个类不同
public static void main(String[]args) throws Exception{ ClassLoader myLoader=new ClassLoader(){ @Override public Class<?>loadClass(String name)throws ClassNotFoundException{ try{ String fileName=name.substring(name.lastIndexOf(".")+1)+".class"; InputStream is=getClass().getResourceAsStream(fileName); if(is==null){ return super.loadClass(name); } byte[]b=new byte[is.available()]; is.read(b); return defineClass(name,b,0,b.length); } catch(IOException e){ throw new ClassNotFoundException(name); } } } ; Object obj=myLoader.loadClass("Person").newInstance(); System.out.println(obj.getClass()); System.out.println(obj instanceof Person);//false }
在这里自己定义一个类加载器,而在这里实际上是存在两个Person类,一个是我们自己定义的类加载器加载的,一个是系统应用程序类加载器加载的
- 从虚拟机的角度,只有两种不同的类加载器,一种是启动类加载器,C++实现,是虚拟机的一部分。另一部分就是其他的类加载器,有java实现,继承自抽象类ClassLoader,属于虚拟机之外。
从java开发人员的角度看,分为启动类加载器,扩张类加载器,应用类加载器。
启动类加载器(Bootstrap ClassLoader)
- 加载的是JAVA_HOME/lib下的类库,启动类加载器无法被java程序直接引用
扩展类加载器(Extension ClassLoader)
- 这个类加载器由sun.misc.Launcher$ExtClassLoader实现,它负责用于加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量指定所指定的路径中所有类库,开发者可以直接使用扩展类加载器。
应用程序类加载器(Application ClassLoader)
- 由sun.misc.Launcher$AppClassLoader实现,负责加载用户类路径(java.class.path)上指定的类库,开发者可以直接使用这个类加载器
双亲委派模型
- 图描述的就是所谓的双亲委托模型,除了顶层的启动类加载器外,其他加载器都有他的父加载器,但这不是继承关系,而是使用组合来复用父加载器的代码
- 工作过程:如果一个类收到类加载的请求,它首先不会自己去尝试去加载这个类,而是把这个请求委托给父类加载器去完成,所有的加载请求最后都会传授到顶层的启动类加载器,只有当父类加载器在他的搜索范围找不到所需的类的时候,也就是无法完成加载请求的时候,子加载器才会尝试去加载。
为什么要双亲委派模型
例如java.lang.Object,存放于rt.jar中,无论哪一个类加载器要去加载这个类,最终都是由Bootstrap ClassLoader去加载,因此Object类在程序的各种类加载器环境中都是一个类。相反,如果没有双亲委派模型,由各个类加载器自己去加载的话,如果用户自己编写了一个java.lang.Object,并放在CLASSPATH下,那么系统会出现多个不同的Objcet类
如果两个类加载器都要加载一个Class,如果没有双亲委托,各自去加载,那么会加载两份Class文件
双亲委托模型的实现
protected synchronized Class<?> loadClass(String name, boolean resolve)
307 throws ClassNotFoundException
308 {
309 // First, check if the class has already been loaded检查是否已经被加载
310 Class c = findLoadedClass(name);
//如果还没被加载
311 if (c == null) {
312 try {
//父加载器是否为null,因为启动类加载器是C++代码编写的,所以父加载器为null的时候,说明此时父加载器是启动类加载器。如果父加载器不为空,让父加载器去加载
313 if (parent != null) {
314 c = parent.loadClass(name, false);
315 } else {
316 c = findBootstrapClass0(name);
317 }
318 } catch (ClassNotFoundException e) {
319 // If still not found, then invoke findClass in order如果父加载器无法加载,调用本身的方法的来进行类加载器
320 // to find the class.
321 c = findClass(name);
322 }
323 }
324 if (resolve) {
325 resolveClass(c);
326 }
327 return c;
328 }
我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)
作者:jiajun 出处: http://www.cnblogs.com/-new/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。
jvm系列 (五) ---类的加载机制的更多相关文章
- JVM:Java 类的加载机制
虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验,转换,解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制. 类的生命周期 类从被加载到虚拟机内 ...
- jvm系列(一):java类的加载机制
java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装 ...
- JVM(1):Java 类的加载机制
原文出处: 纯洁的微笑 java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang. ...
- Jvm类的加载机制
1.概述 虚拟机加载Class文件(二进制字节流)到内存,并对数据进行校验.转换解析和初始化,最终形成可被虚拟机直接使用的Java类型,这一系列过程就是类的加载机制. 2.类的加载时机 类从被虚拟机加 ...
- JVM-01:类的加载机制
本文从 纯洁的微笑的博客 转载 原地址:http://www.ityouknow.com/jvm.html 类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内 ...
- Java虚拟机JVM学习02 类的加载概述
Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...
- 深入java虚拟机学习 -- 类的加载机制
当看到"类的加载机制",肯定很多人都在想我平时也不接触啊,工作中无非就是写代码,不会了可以百度,至于类,jvm是怎么加载的我一点也不需要关心.在我刚开始工作的时候也觉得这些底层的内 ...
- 24.类的加载机制和反射.md
目录 1类的加载连接和初始化 1.1类的加载过程 1.2类的加载器 1.2.1类的加载机制 1类的加载连接和初始化 1.1类的加载过程 类的加载过程简单为分为三步:加载->连接->初始化 ...
- 深入java虚拟机学习 -- 类的加载机制(续)
昨晚写 深入java虚拟机学习 -- 类的加载机制 都到1点半了,由于第二天还要工作,没有将上篇文章中的demo讲解写出来,今天抽时间补上昨晚的例子讲解. 这里我先把昨天的两份代码贴过来,重新看下: ...
随机推荐
- 如何自己写一个公用的NPM包
以markdown-clear,创建过程为例,讲解整个NPM包创建和发布流程 1 如何创建一个包 1.1 创建并使用一个工程 在GitHub上新建一个仓库,其名markdown-clear clone ...
- 34. leetcode 447. Number of Boomerangs
Given n points in the plane that are all pairwise distinct, a "boomerang" is a tuple of po ...
- Tornado session 插件 pycket 定制时间和时间续租
功能描述:10分钟用户没有任何操作,跳转到登录页面. 分析:这个功能用session就能实现(由于pycket 的session内容是存储在memcached或者redis里面的.所以,session ...
- MySql学习笔记(四)
MYSQL如何查看系统帮助: 1.查看官方API文档: http://dev.mysql.com/doc/ 2.通过Mysql中的help命令 比如:help create database MYSQ ...
- cacti监控部署与配置
cacti是一套基于PHP,mysql,SNMP及RRDTool开发的网络流量测试图形分析工具 cacti是通过snmpget来获取数据,使用RRDtool绘画图形 ,而且完全可以不需要了解RRDto ...
- 中国大学MOOC-陈越、何钦铭-数据结构-2015秋 01-复杂度2 Maximum Subsequence Sum (25分)
01-复杂度2 Maximum Subsequence Sum (25分) Given a sequence of K integers { N1,N2, ..., NK }. ...
- InnoDB online DDL与快速索引创建
导读:在MySQL5.6之前版本,Innodb表的许多DDL操作是非常昂贵.许多ALTER TABLE操作的原理是通过创建新的空表,定义被要求的表选项和索引,然后逐行拷贝已存在记录到新表,在插入行时更 ...
- Oracle Test 日志
这是一个在本地写日志的软件,写一篇看看效果怎么样. 我主要想看看这个行间距ok不... 看看效果...
- Vue自定义事件
前面的话 父组件使用props传递数据给子组件,子组件怎么跟父组件通信呢?这时,Vue的自定义事件就派上用场了.本文将详细介绍Vue自定义事件 事件绑定 每个 Vue 实例都实现了事件接口 (Even ...
- [Python] wxPython 状态栏组件、消息对话框组件 学习总结(原创)
1.状态栏组件 1.基本介绍 上图: 红框框内的就是状态栏. 他可以分成若干个区块,比如上者分为了两个区块,并且比例是固定的,创建时可以指定 每个区块都能够显示 信息,一般通过 绑定事件 实时更新 各 ...