【转载】JVM类加载机制小结

本文转载自http://shift-alt-ctrl.iteye.com/blog/1845137

 

一.类加载   

    虚拟机把class文件加载至内存之后,对字节码数据进行校验/解析/初始化等操作,最终形成可被VM直接使用的java类型,这就是虚拟机类加载机制.类的加载完全可以在运行时进行,这给VM提供了动态加载类提供了可行性.

    类生命周期过程大概分为:加载-->校验-->解析-->初始化-->使用-->卸载;类在被"使用"之前必须进行前4个阶段,其中初始化工作可以被延迟进行,直到类需要创建实例/static方法或属性被调用(非编译常量)/反射机制中使用类的成员(Member)时被触发.

  1. 加载:通过类的全限定名的方式获取class文件的字节流.事实上,加载器可以从任何类型文件(zip,jar,txt等)或者网络中获取class字节流.参见ClassLoader类
  2. 校验:检查加载至内存的字节流是否符合当前JVM的规范要求,字节流信息需要被当前JVM(考虑到JVM的版本问题)正确的识别,包括字节流的格式/字节码的语义(语法)/类的继承关系/变量的声明合法性等进行全面校验,如果校验成功,则表明此class字节流是安全的/没有被修改的/可以被正常使用的.
  3. 准备:准备阶段是一个过渡阶段,在类文件校验合法之后,将会得出类的结构图,此时有必要对类的属性(static)进行默认值设定,比如static int默认为0,即使指定了值.不过对于final类型的,将会以"常量"特例被处理,直接持有原始值.
  4. 解析:解析阶段是JVM将常量池中的符号引用转换成直接引用的过程.
  5. 初始化:到此为止,一个类的"前戏"工作基本结束,接下来就可以"服务"了,此时类的属性(static)将会被设定为用户指定的值(区别于准备阶段),而且还会执行类的static区块.同时也会连带执行父类的static区块和静态属性的值初始化.对于类的static区块在多线程环境中被同步执行,如果有多个线程同事执行static区块,事实上最终只会在有一个线程执行.

二.类加载器

    对于任意一个类,它的唯一性将有它的类加载器和其类型来决定;如果一个类被多个classLoader加载(最终执行load操作的classLoader必须不同),那么它们最终获得的class引用将不相等(即"!="),即使用==,equals(),instanceof,isInstance(),isAssignableFrom()都得不到预期的值,事实上可以简单的认为,他们是不同的"类"(类型).

  1. 引导类加载器:Bootstrap-classLoader,此类主要负责加载JAVA API,即处于$JAVA_HOME$\lib下的class文件,这些lib位置可以在外部通过-Xbootclasspath指定,但是此目录下的jar文件不能被重命名或者人为增加,否则将不能被正常加载;引导类加载器不能被java程序获得,尝试获得引导类加载器将返回null.
  2. 扩展类加载器:负责加载$JAVA_HOME$\lib\ext目录下的class,开发者可以人为的在此目录下添加jar文件,以便被此JVM之上的所有应用所收益.此加载器可以被程序获取,一般为classpath类加载器的父类.(sun.misc.Launcher$ExtClassLoader)
  3. 应用类加载器:Application ClassLoader,负责加载应用的classpatch下的所有class文件,其中ClassLoader.getSystemClassLoader()获得的就是此加载器.(sun.misc,Launcher$AppClassLoader);自定义类加载器的父加载器一般设定为它.

JVM并没有使用继承关系来组织这三种类加载器,而是采取了组合关系(即classLoader.setParent(...)).

    JVM类加载过程和类关系维护采取了"双亲委派模型":对于任何类加载器(包括自定义类加载器)对于指定类的加载,首先交付给"父-类加载器"查找或加载,如果父-类加载器已经加载则直接获取class引用,否则继续传递当前类加载器的父-类加载器,直到引导类加载器,如果此时引导类加载器也没有持有class信息,则抛出异常(异常信息只作为中断信号),此时当前类加载器(直接接收加载请求的类加载器,比如自定义类加载器)将会尝试加载类信息...通过这种"委派关系",能够巧妙的(当然不是最佳的手段)规避类被重复加载的可能.

 

三.编译与泛型

    java中的泛型,是伪泛型,只是简单的在API级别做了"模样",但是对于类的编译过程,则会导致泛型类型的擦除,即编译过程或者编译之后的文件中,最终泛型仍然以"原生类型"表示.即在运行时无法直接还原"泛型",java还支持了反射机制,为了让反射机制能够和"泛型"配合,那么java最终提供了Generic(例如GenericArrayType)和ParameterizedType来配合获得泛型(和参数化类--类型)的信息,这些信息虽然在编译时被擦除,但是元数据(metadata)仍然被有效的class文件中.

    例如:List<Integer>在编译之后,API级别上就成了List,"丢失"了参数化的信息.那么方法 void invoke(List<Integer> list) 和void invoke(List<String> list),那么将不能共存在一个类中,因为编译之后它们是一样的.(识别方法的特性,有方法签名 + 参数列表,而定);但是还有一中特例,如果方法的参数列表中或者返回值中或者当前类是参数化的,那么对于方法的返回值类型也参与了"方法标识";例如上述两个方法,如果第二个方法改成Integer invoke(List<String> list)或者<T> T invoke(List<T> list)将被成功编译.

时间: 2024-12-27 14:53:48

【转载】JVM类加载机制小结的相关文章

JVM类加载机制小结

一.类加载        虚拟机把class文件加载至内存之后,对字节码数据进行校验/解析/初始化等操作,最终形成可被VM直接使用的java类型,这就是虚拟机类加载机制.类的加载完全可以在运行时进行,这给VM提供了动态加载类提供了可行性.     类生命周期过程大概分为:加载-->校验-->解析-->初始化-->使用-->卸载;类在被"使用"之前必须进行前4个阶段,其中初始化工作可以被延迟进行,直到类需要创建实例/static方法或属性被调用(非编译常量)

深入理解JVM之六:类加载机制

前言 虚拟机的类加载机制可以简单描述如下:Java虚拟机把描述类的数据从Class文件中加载到内存中,并对数据进行校验.解析和初始化,最终形成可以被虚拟机直接使用的Java类型.虚拟机加载进行类加载的过程是在程序运行期间完成的,在程序运行期间加载的好处是可以动态扩展,说白了就是在编译期间虚拟机是不知道要加载哪些类或者接口的,只有在程序运行的时候才知道需要加载的类.举个例子,我们在一个包下面写了很多类,我们在在别的包中可能只是需要其中的几个类,但是出于习惯我们可能会写成import test.*

java多线程之:深入JVM锁机制2-Lock (转载)

前文(深入JVM锁机制-synchronized)分析了JVM中的synchronized实现,本文继续分析JVM中的另一种锁Lock的实现.与synchronized不同的是,Lock完全用Java写成,在java这个层面是无关JVM实现的. 在 java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLock. ReadWriteLock(实现类ReentrantReadWriteLock),其实现都依赖 java.util.concurre

图解Tomcat类加载机制

说到本篇的tomcat类加载机制,不得不说翻译学习tomcat的初衷. 之前实习的时候学习javaMelody的源码,但是它是一个Maven的项目,与我们自己的web项目整合后无法直接断点调试.后来同事指导,说是直接把java类复制到src下就可以了.很纳闷....为什么会优先加载src下的java文件(编译出的class),而不是jar包中的class呢? 现在了解tomcat的类加载机制,原来一切是这么的简单. 类加载 在JVM中并不是一次性把所有的文件都加载到,而是一步一步的,按照需要来加

深入理解Java:类加载机制及反射

一.Java类加载机制 1.概述 Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能. 虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 2.工作机制 类装载器就是寻找类的字节码文件,并构造出类在JVM内部表示

深入JVM锁机制2-Lock

[本文转载于深入JVM锁机制2-Lock] 前文(深入JVM锁机制-synchronized)分析了JVM中的synchronized实现,本文继续分析JVM中的另一种锁Lock的实现.与synchronized不同的是,Lock完全用Java写成,在java这个层面是无关JVM实现的. 在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLock.ReadWriteLock(实现类ReentrantReadWriteLock),其实现都依

Java虚拟机类加载机制——案例分析

  在<Java虚拟机类加载机制>一文中详细阐述了类加载的过程,并举了几个例子进行了简要分析,在文章的最后留了一个悬念给各位,这里来揭开这个悬念.建议先看完<Java虚拟机类加载机制>这篇再来看这个,印象会比较深刻,如若不然,也没什么关系~~ 下面是程序代码: package jvm.classload; public class StaticTest { public static void main(String[] args) { staticFunction(); } st

Java虚拟机类加载机制

看到这个题目,很多人会觉得我写我的java代码,至于类,JVM爱怎么加载就怎么加载,博主有很长一段时间也是这么认为的.随着编程经验的日积月累,越来越感觉到了解虚拟机相关要领的重要性.闲话不多说,老规矩,先来一段代码吊吊胃口. public class SSClass{ 运行结果: SSClassSuperClass init!123 答案答对了嚒? 也许有人会疑问:为什么没有输出SubClass init.ok~解释一下:对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类来引用父

Java魔法堂:类加载机制入了个门

一.前言   当在CMD/SHELL中输入 $ java Main<CR><LF> 后,Main程序就开始运行了,但在运行之前总得先把Main.class及其所依赖的类加载到JVM中吧!本篇将记录这些日子对类加载机制的学习心得,以便日后查阅.若有纰漏请大家指正,谢谢!   以下内容均基于JDK7和HotSpot VM.   二.执行java的那刻     大家都知道通过java命令来启动JVM和运行应用程序,但实际的流程又是如何的呢?   1. 首先根据java后的运行模式配置项或