类读取操作过程总体预测

当他们用java指示运转某一类的main表达式开启流程时,具体来说须要透过类读取器把主类读取到 JVM

public class Math {public static final int initData = 666;public static User user = new User();public int compute() { //两个方式相关联几块栈帧缓存地区int a = 1;int b = 2;int c = (a b) * 10;return c;}public static void main(String[] args) {Math math = new Math();math.compute();}}

透过Java指示继续执行标识符的大体上业务流程如下表所示:

自动草稿

从上图他们能窥见发动初始化的地方性是作业系统下层帮他们同时实现的,鼓励类读取器也并非由java撰写的。

在或者说读取他们要运转的类以后要做许多预备组织工作,这当中许多地方性都并非javaSmalltalk能处置的,因而无须做过多的探求。

所以类读取在读取类的操作过程中出现了什么样事呢?约莫能分成下列八个期:

自动草稿

读取:在硬碟上搜寻并透过IO初始化二进制码文档,采用到类时才会读取,比如初始化类的main()方式,new第一类之类,在读取期会在缓存中生成两个代表这个类的java.lang.Class第一类,作为方式区这个类的各种数据的访问入口

验证:校验二进制码文档的正确性

预备:给类的静态变量分配缓存,并赋予默认值

解析:将符号引用替换为直接引用,该期会把一些静态方式(符号引用,比如main()方式)替换为指向数据所存缓存的指针或句柄等(直接引用),这是所谓的静态链接操作过程(类读取期间完成),动态链接是在流程运转期间完成的将符号引用替换为直接引用

初始化:对类的静态变量初始化为指定的值,继续执行静态标识符块

自动草稿

PS:类被读取到方式区中后主要包含 运转时常量池、类型信息、字段信息、方式信息、类读取器的引用、相关联class实例的引用等信息。类读取器的引用:这个类到类读取器实例的引用相关联class实例的引用:类读取器在读取类信息放到方式区中后,会创建两个相关联的Class 类型的第一类实例放到堆(Heap)中, 作为开发人员访问方式区中类定义的入口和切入点。

所以类是在jvm开启时就全部读取了吗?

答案是否定的,事实上,主类在运转操作过程中如果采用到其它类,会逐步读取这些类。jar包或war包里的类并非一次性全部读取的,是采用到时才读取。请看下面例子

public class TestDynamicLoad {static {System.out.println(“*************读取主开启类************”);}public static void main(String[] args) {new A();System.out.println(“*******读取测试********”);B b = null;//B不会读取,除非这里继续执行new B();}}class A{static {System.out.println(“*******读取A类********”);}public A() {System.out.println(“*******初始化A类********”);}}class B{static {System.out.println(“*******读取B类********”);}public B() {System.out.println(“*******初始化B类********”);}}

运转结果:*************读取主开启类*******************读取A类***************初始化A类***************读取测试********

类读取器和双亲委派监督机制

上面的类读取操作过程主要是透过类读取器来同时实现的,Java里有如下表所示几种类读取器:

Bootstrp loader Bootstrp读取器是用C 语言写的,它是在Java虚拟机开启后初始化的,它主要负责读取%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类。

ExtClassLoader Bootstrp loader读取ExtClassLoader,并且将ExtClassLoader的父读取器设置为Bootstrp loader.ExtClassLoader是用Java写的,具体来说就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要读取%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。

AppClassLoader Bootstrp loader读取完ExtClassLoader后,就会读取AppClassLoader,并且将AppClassLoader的父读取器指定为 ExtClassLoader。AppClassLoader也是用Java写成的,它的同时实现类是 sun.misc.Launcher$AppClassLoader,另外他们知道ClassLoader中有个getSystemClassLoader方式,此方式返回的正是AppclassLoader.AppClassLoader主要负责读取classpath所指定的位置的类或者是jar文档,它也是Java流程默认的类读取器。

类读取器初始化操作过程:

参见类运转读取全操作过程图可知当中会创建JVM开启器实例sun.misc.Launcher。 sun.misc.Launcher初始化采用了单例模式设计,保证两个JVM虚拟机内只有两个 sun.misc.Launcher实例。 在Launcher构造方式内部,其创建了两个类读取器,分别是 sun.misc.Launcher.ExtClassLoader(扩展类读取器)和sun.misc.Launcher.AppClassLoader(应 用类读取器)。 JVM默认采用Launcher的getClassLoader()方式返回的类读取器AppClassLoader的实例读取他们 的应用流程。

jdk源标识符如下表所示:

//Launcher的构造方式public Launcher() {Launcher.ExtClassLoader var1;try {//构造扩展类读取器,在构造的操作过程中将其父读取器设置为nullvar1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError(“Could not create extension class loader”, var10);}try {//构造应用类读取器,在构造的操作过程中将其父读取器设置为ExtClassLoader,//Launcher的loader属性值是AppClassLoader,他们一般都是用这个类读取器来读取他们自己写的应用流程this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError(“Could not create application class loader”, var9);}Thread.currentThread().setContextClassLoader(this.loader);

双亲委派监督机制

前面说了,java中有三个类读取器,问题就来了,碰到两个类须要读取时,它们之间是如何协调组织工作的,即java是如何区分两个类该由哪个类读取器来完成呢。 在这里java采用了委托模型监督机制,这个监督机制简单来讲,就是类装载器有载入类的需求时,会先请示其Parent采用其搜索路径帮忙载入,如果Parent 找不到,所以才由自己依照自己的搜索路径搜索类

下面举两个例子来说明,为了更好的理解,先弄清楚几行标识符:

Public class Test{Public static void main(String[] arg){ClassLoader c = Test.class.getClassLoader(); //获取Test类的类读取器System.out.println(c);ClassLoader c1 = c.getParent(); //获取c这个类读取器的父类读取器System.out.println(c1);ClassLoader c2 = c1.getParent();//获取c1这个类读取器的父类读取器System.out.println(c2);}}结果:……AppClassLoader…………ExtClassLoader……Null

能窥见Test是由AppClassLoader读取器读取的,AppClassLoaderParent读取器是ExtClassLoader,但是ExtClassLoaderParentnull是怎么回事呵,朋友们留意的话,前面有提到Bootstrap Loader是用C 语言写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体,所以在java流程标识符里试图打印出其内容时,他们就会看到输出为null

他们来看下应用流程类读取器AppClassLoader读取类的双亲委派监督机制源码,AppClassLoader 的loadClass方式最终会初始化其父类ClassLoader的loadClass方式,该方式的大体上逻辑如下表所示:

具体来说,检查一下指定名称的类是否已经读取过,如果读取过了,就不须要再读取,直接 返回。

如果此类没有读取过,所以,再判断一下是否有父读取器;如果有父读取器,则由父加 载器读取(即初始化parent.loadClass(name, false);).或者是初始化bootstrap类读取器来加 载。

如果父读取器及bootstrap类读取器都没有找到指定的类,所以初始化当前类读取器的 findClass方式来完成类读取。

源标识符如下表所示:

ClassLoader.java

protected Class loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// 检查当前类读取器是否已经读取了该类Class c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) { //如果当前读取器父读取器不为空则委托父读取器读取该类c = parent.loadClass(name, false);} else {//如果当前读取器父读取器为空则委托鼓励类读取器读取该类c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();//都会初始化URLClassLoader的findClass方式在读取器的类路径里搜寻并读取该类c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 – t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}

URLClassLoader.java

protected Class findClass(final String name)throws ClassNotFoundException{final Class result;try {result = AccessController.doPrivileged(new PrivilegedExceptionAction

1.本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2.分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3.不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4.本站提供的源码、模板、插件等其他资源,都不包含技术服务请大家谅解!
5.如有链接无法下载或失效,请联系管理员处理!
6.本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!