码迷,mamicode.com
首页 > 其他好文 > 详细

类加载机制

时间:2015-05-22 20:57:29      阅读:159      评论:0      收藏:0      [点我收藏+]

标签:

1、类文件结构

           说明: java虚拟机要对类文件进行加载和执行,那么必须要能够理解类文件结构,而对于虚拟机而言,平台无关性和语言无关性是其最重要的两大特征,那么就势必要对类文件结构进行规范化和结构化,这样才能保证无论是什么语言编译成的字节码文件,java虚拟机都能够正常加载和执行。因此,对于字节码文件(即.class文件)的简单理解是进一步理解虚拟机运行机制的基本步骤。

          Class类文件规范:亦称字节码文件,是由虚拟机规范规定了其结构形式的文件。Class文件是一组以8位为基础单位的二进制流,各个数据项目严格按照顺序紧凑排列在Class文件中,中间没有任何分隔符,以保证整个Class文件中存储的内容全部是程序运行的必要数据,没有空隙。当遇到需要占用8位字节以上空间的数据时,则会按照高位在前的方式分割成若干个8位字节进行存储。

          根据虚拟机规范的规定,Class文件格式中只有两种数据类型:无符号数和表。无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节,2个字节,4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值、或者按照UTF-8编码构成字符串值。表是多个无符号数或其它表作为数据项构成的复合数据类型,所有表都习惯性地以"_info"结尾。无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式,这时候称这一系列连续的某一类型的数据为某一类型的集合。

       Class文件内容:既然虚拟机是语言无关,那我们可以以java语言作为范本进行学习。回顾一下,我们在定义一个类的时候,都需要或者说可以定义些什么内容。首先,类的修饰符,是abstract,或public、protected、private,然后是类名,再接着,是否有继承或是实现父类或接口,这些是类的基本约束。再接着来看类的内容,我们可以定义类成员变量(static)和实例成员变量,接着是定义类的行为——类方法,类方法又有方法名,返回值,参数值,还有异常列表等。

    由上面这些定义的内容,我们可以猜到,当这个定义的类被编译成Class文件时,Class文件中应该要包含些什么内容了

     Class文件的结构:首先,最简单的一个问题,虚拟机必须判定输入的文件是不是一个Class文件,java虚拟机通过识别输入文件的首4个字节的魔数(0xCAFEBABE)来确定其是否Class文件。接着虚拟机由于一直在不断地改进和更新,所以不断有新的版本出现,新的版本能兼容旧的版本,但旧的版本可能就完全无法读取新的虚拟机编译而成的Class文件了,因此,虚拟机就必须对Class文件进行版本的识别和检查,也就是说,Class文件必须要有版本号的数据(Class文件的第5到第8个字节)。

    紧接着是Class文件的常量池入口,常量池是Class文件结构中与其它项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时也是在Class文件中第一个出现的表类型数据项目。由于常量池中常量的数量不固定,因此在常量池的入口之前有一个u2类型的容量计数值。常量池之中主要存放两大常量:字面量(Literal)和符号引用(Symbolic Reference)。字面量即是java语言层面中的常量,如文本字符串(如"adb"等字面量),被声明成final的常量值。符号引用则属于编译原理方面的概念,包括三类常量:类和接口的全限定名,字段的名称和描述符,方法的名称和描述符。这些符号引用在虚拟机中如果不经过转换则无法与实际内存相连接,即无法被虚拟机直接使用,在虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析并翻译到具体的内存地址中。每项常量都是一个表,而由于各个常量的类型不一,大小也不相同,所以同样需要一个u1类型的数据来标记常量的类型,以确定其后的常量表的格式。

    在常量池之后,紧接着的2个字节代表访问标志,即在前面说到的,这个Class是类还是接口,是用哪个修饰符来修饰,abstract,public等,还有,如果是类的话,是否被声明为final,等等。

    访问标志之后,则是类索引、父索引与接口索引的集合。类索引和父类索引都是一个u2类型的数据,而接口索引集合是一组u2类型的数据的集合,Class文件中由这三项数据来确定这个类的继承关系。类索引用来确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名,接口索引集合用来描述这个类实现了哪些接口,这些被实现的接口将按实现或继承的顺序从左到右的顺序排列在接口的索引集合中。类索引、父类索引和接口索引都按顺序排列在访问标志之后。

    接下来就是字段表了,此处字段表存的就是前文说的类成员变量或实例成员变量,但不包括方法内部声明的变量。如果类存在父类,则除非子类覆盖了父类的字段定义,否则在子类中不会列出从超类或父接口中继承而来的字段,但有可能列出原来java代码中不存在的字段,譬如在内部类为了保持对外部类的访问性,会自动添加指向外部类实例的字段。另外,java中是不允许出现相同的字段名的,但对于字节码来说,如果两个字段的描述符不一致,则字段重名是合法的。

    字段表之后就是方法表集全了。方法表集合与字段表集合的结构形式几乎完全一致。此处,方法中的代码的存放位置则是方法表的属性表中的一项名为"Code"的属性里面。与字段表集合相对应的,如果父类方法在子类中没有被重写(Override),则方法表集合中就不会出现来自父类的方法信息。

    最后来对上面说到的属性表作个解释。属性表是Class文件格式中最具扩展性的一种数据项目,在Class文件,字段表,方法表中都可以携带自己的属性表集合,以用于描述某些场景专有的信息(如方法表中专有的代码信息),具体的属性表的各个属性项目若有兴趣可以翻看《深入理解java虚拟机》这本书,也可以直接翻看虚拟机规范。

 2.   类加载:类加载器是个树型结构,如果Parent能找到要加载的类的话,就加载Parent的,这样可以确保JDK自带的类不被项目中自定义的同名类覆盖

            java应用环境中不同的class分别由不同的ClassLoader负责加载。
一个jvm中默认的classloader有Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader,分别各司其职:

•Bootstrap ClassLoader    负责加载java基础类,主要是 %JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等

•Extension ClassLoader     负责加载java扩展类,主要是 %JRE_HOME/lib/ext 目录下的jar和class

•App ClassLoader           负责加载当前java应用的classpath中的所有类。

其中Bootstrap ClassLoader是JVM级别的,由C++撰写;Extension ClassLoader、App ClassLoader都是java类,都继承自URLClassLoader超类。
Bootstrap ClassLoader由JVM启动,然后初始化sun.misc.Launcher ,sun.misc.Launcher初始化Extension ClassLoader、App ClassLoader。

下图是ClassLoader的加载类流程图,以加载一个类的过程类示例说明整个ClassLoader的过程。

技术分享
 


Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader三者的关系如下:

Bootstrap ClassLoader是Extension ClassLoader的parent,Extension ClassLoader是App ClassLoader的parent。

但是这并不是继承关系,只是语义上的定义,基本上,每一个ClassLoader实现,都有一个Parent ClassLoader。

可以通过ClassLoader的getParent方法得到当前ClassLoader的parent。Bootstrap ClassLoader比较特殊,因为它不是java class所以Extension ClassLoader的getParent方法返回的是NULL。


由于classloader 加载类用的是全盘负责委托机制。所谓全盘负责,即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有 Class也由这个classloader负责载入,除非是显式的使用另外一个classloader载入。

所以,当我们自定义的classloader加载成功了com.company.MyClass以后,MyClass里所有依赖的class都由这个classLoader来加载完成。

 

 

Points:

   1、不同的类装载器分别创建的同一个类的字节码数据属于完全不同的对象,没有任何的关联。

   2、类加载机制的好处:

     1、避免重复加载,当父类已经加载了该类的时候,就没有必要子ClassLoader再加载一次。

     2、考虑安全因素,如果不使用这种委托模式,那么随时可以使用自定义的String来动态替代Java核心API中定义的类型,存在安全隐患,而父类委托方式就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载的。

类加载机制

标签:

原文地址:http://www.cnblogs.com/Jenny5/p/4523128.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!