开发者社区> 调皮仔3683> 正文

Java高级之虚拟机加载机制

简介: Jvm要加载的是二进制流,可以是.class文件形式,也可以是其他形式,按照它加载的标准来设计就不会有太大问题。
+关注继续查看

Jvm要加载的是二进制流,可以是.class文件形式,也可以是其他形式,按照它加载的标准来设计就不会有太大问题。

以下主要就机制和标准两个问题分析一番:

首先来Java类文件的加载机制 ,跟变量的加载机制类似,它先把Class文件加载入内存,再对数据进行验证、解析和初始化,最终形成虚拟机可以直接使用的Java类型。由于Java是采用JIT机制,所以加载时会比较慢,但优点也明显,具有高度灵活性,支持动态加载和动态连接。

接下来就讲讲类的加载过程:

一个类加载的基本过程是按照下面的顺序 来,但也有不严格按照这个顺序来的,也有打乱顺序来的,如动态加载就得先初始化再解析。

1、加载
由虚拟机自行决定,但也有由于下面的阶段要执行而执行上面阶段的情况。

这时虚拟机会做三件事:
第一、通过全限定名读取文件的二进制流;
第二、把文件里的静态方法和变量放到方法区中;
第三、生成一个对象放入堆中,作为访问入口。
注意第一条,仅是读取二进制流,没说具体从什么文件中读,也没说从哪里读,所以造就Java很强的扩展性,可以从Jar、Zip中,也可以从网络层、数据库层等 。
主要是对象和方法区的声明。

2、验证
确保二进制流符合虚拟机的要求, 不符合会报VerifyError。

第一、文件格式验证,是否有魔数,是否符合Java文件的要求;
第二、元数据验证,是否符合Java代码规范,如abstract类是否直接被实例化,普通类有无间接或直接父类Object等;第三、字节码验证,对数据流和控制流进行分析,保证不会做出危害虚拟机的行为,如 是否调用不存在的指令,是否把父类赋值给子类,是否把对象赋值给一个非此类型的对象等;
第四、符号引用验证,主要是类、变量、方法描述是否能找的到,如全限定名是否能找到该文件,是否具有可访问性等。
主要对内部结构的判定

3、准备
为类变量赋初值,通常为0值如静态变量,而不会为实例变量赋值。

4、解析
将常量池中的符号引用转化为直接引用的过程。这里说的符号引用指变量类型,直接引用指可以直接定位到对象的句柄。类、方法、字段、接口解析,根据全限定名获得相关对象,拿到它的类型,若无对所在类访问权会抛出IllegalAccessError,无字段NoSuchFieldError,无方法NoSuchMethodError,是类不是接口会抛出IncompatibleClassChangeError

5、初始化
根据程序要求加载类和必要的资源。有且仅有四种情况,需要主动初始化后才能执行接下来的操作 ,所以要先执行上面的四步。
第一、有new或static关键字的类,new生成对象,static静态加载,这两个很明显要执行初始化了;
第二、使用类有父类,这没办法了;
第三、反射类里的方法,那肯定要初始化了对不对;
第四、执行的主类,用main方法的类。其他被动初始化的情况不需要考虑。

小例子:

public class SuperClass {
static {
System.out.println(“SuperClass!!”);
}
public static int value = 1;
}
public class SubClass extends SuperClass {
static {
System.out.println(“SubClass!!”);
}
}
public class TestClassLoad {
public static void main(String[] args) {
System.out.println(SubClass.value);
SuperClass superClass = new SubClass();
}
}
SuperClass!!
1
SubClass!!

执行结果说明一个问题: 子类调用父类变量的时候 ,子类没有初始化,因为 此时的代码关系跟子类无关 ;子类初始化的时候,父类也没有再初始化,因为 父类在当前方法体中已经初始化 过了。接口与父类的唯一区别在于, 接口初始化不会要求父接口,只有用到父接口才会初始化 ,同样的都会生成类构造器。

这个时候加载类构造器,会初始化类中所有变量,当然父类先于子类初始化

6、使用
加载完之后,该怎么样调用怎么样调用,绘图啊,计算啊等等

7、卸载
类不再被调用

两个类是否相等,主要在于第一使用同一个加载器加载,第二全限定名地址一致

为什么要提出上面的问题呢?接下来要讲讲虚拟机的一个加载机制。

在java虚拟机的角度来看,有两种类加载器,一种叫系统加载器(Bootstrap ClassLoader),一种叫自定义加载器(extends ClassLoader),这种呢又分为两个,一种叫应用加载器,一种叫扩展类加载器,一般默认为前者;而我们的应用程序加载主要由上面三个加载器相互配合完成的。三者的关系如Application–>Extension–>Bootsrap,双亲委派机制是指两两以组合的方式,子加载器先去调用父加载器的方法,没找到目标对象再去用子加载器

伪代码如下:

loadClass(String name,boolean resolve){
Class c=findLoadedClass()
if(c==null){
if(parent !=null)
c=parent.loadClass(name,false);
c=findBootstrapClassOrNull(name);
}catch(ClassNotFoundException e){ }
if(c==null)
c=findClass(name);
}

Java提倡我们去把自己调用类的逻辑写在findClass里,这样有助于双亲委派机制的正常使用。

破坏1、重写loadClass

破坏2、使用线程上下文加载器去让父加载器去调用子加载器的方法

破坏3、热加载 现在常用的做法是 自定义类加载器并 将原bug模块覆盖-OSGI

但由于自定义加载器之间的规则如果混乱,出现同时互相引用的问题,那么会最终找不到类,而出现线程死锁和内存泄露的问题。

关于热修复,也被称为插件,目前比较流行的有HotFix、Nuwa、DroidFix、AndFix等,这些框架均可以在github或其他地方找到,原理如上,方法多样,有覆盖的、有重定向的等等,通过配置、设置action等方式;而作为插件需要满足以下条件:

1、可以独立安装,但不可独立运行

2、具有向下兼容性,即可拓展性

3、只能运行在宿主程序中,而且可以被禁用、替换

原文链接:http://geek.csdn.net/news/detail/245578

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
《UNIX环境高级编程(第3版)》——2.2 UNIX标准化
Single UNIX Specification(SUS,单一UNIX规范)是POSIX.1标准的一个超集,它定义了一些附加接口扩展了POSIX.1规范提供的功能。POSIX.1相当于Single UNIX Specification中的基本规范部分。
1353 0
UNIX环境高级编程笔记之标准I/O库
一、总结   文件I/O一章讲了不带缓冲的I/O,本章讲的是带缓冲的I/O。不带缓冲针对的是内核的系统调用,而带缓冲针对的是用户空间的标准库函数,是基于带缓冲的I/O实现的。不带缓冲的I/O通过文件描述符的方式来引用一个文件,而带缓冲的I/O则通过文件流(stream)的方式来引用文件。
729 0
UNIX环境高级编程笔记之进程环境
本章讲的都是一些非常基础的知识,目的是为了下一章讲进程控制做铺垫,所以,本章就不做过多的总结了,直接看图吧。
731 0
滴滴一面(高级java)面试题分享
volatile语义中的内存屏障volatile的内存屏障策略非常严格保守,非常悲观且毫无安全感的心态:在每个volatile写操作前插入StoreStore屏障,在写操作后插入StoreLoad屏障;在每个volatile读操作前插入LoadLoad屏障,在读操作后插入LoadStore屏障;由于内存屏障的作用,避免了volatile变量和其它指令重排序、线程之间实现了通信,使得volatile表现出了锁的特性。
27 0
深入理解JVM虚拟机读书笔记——类的加载机制
注:本文参考自周志明老师的著作《深入理解Java虚拟机(第3版)》,相关电子书可以关注WX公众号,回复 001 获取。
12 0
《UNIX环境高级编程(第3版)》——2.3 UNIX系统实现
在McKusick等[1996]的1.1节中给出了UNIX系统家族树的详细历史。UNIX的各种版本和变体都起源于在PDP-11系统上运行的UNIX分时系统第6版(1976年)和第7版(1979年)(通常称为V6和V7)。
1943 0
高级JAVA开发必备技能:时区的规则发生变化时,如何同步JDK的时区规则(实战方案,建议收藏)
高级JAVA开发必备技能:时区的规则发生变化时,如何同步JDK的时区规则(实战方案,建议收藏)
38 0
UNIX环境高级编程笔记之进程控制
  本章重点介绍了进程控制的几个函数:fork、exec族、_exit、wait和waitpid等,主要需要掌握的是父进程和子进程之间的运行机制,怎么处理进程的正常和异常终止、以及怎么让进程执行不同的程序等知识点。下一章将进一步说明一个进程和其他进程之间的关系——会话和作业控制。
702 0
UNIX环境高级编程---标准I/O库
前言:我想大家学习C语言接触过的第一个函数应该是printf,但是我们真正理解它了吗?最近看Linux以及网络编程这块,我觉得I/O这块很难理解。以前从来没认识到Unix I/O和C标准库I/O函数压根不是一码事。
947 0
java高级用法之:JNA类型映射应该注意的问题
java高级用法之:JNA类型映射应该注意的问题
20 0
+关注
调皮仔3683
来自广州的开发仔一枚,从事互联网金融系统和电子商务系统的技术研发
115
文章
1
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载