JVM类加载过程学习总结

先不说JVM类加载的原理,先看实例:

NormalTest类,包含了一个静态代码块,执行的任务就是打印一句话。

/**
* 在正常类加载条件下,看静态代码块是否会执行
* @author jianying.wcj
* @date 2013-6-21
*/
public class NormalTest { static {
System.out.println("hello world!");
}
}

TestStatic类, 有三行代码,其中两行被注释,测试过程是,在执行其中任意一行代码的时候,注释掉其余两行。

public class TestStatic {

    public static void main(String[] args) throws ClassNotFoundException {
/**
* 实验1
*/
Class.forName("NormalTest");
/**
* 实验2
*/
//NormalTest nt = new NormalTest();
/**
* 实验3
*/
//TestStatic.class.getClassLoader().loadClass("NormalTest");
}
}

测试的输出的结果是: 在执行 Class.forName("NormalTest")的时候,输出了“Hello world!”,在执行NormalTest nt = new NormalTest();的时候也输出了“Hello world!” 但是在执行代码TestStatic.class.getClassLoader().loadClass("NormalTest");却没有输出“Hello world!” 下面分析一下原因或者说看看这三行代码的内部实现的异同。 以上三行代码其实在执行的时候都会去加载NormalTest.class,这里可以不准确的说以上三行代码是三种加载类的方式。从实验的输出来看,可以确定实验1 和实验2 在加载NormalTest的时候执行了静态代码块,而实验3 直接调用ClassLoader来loadclass的时候没有执行静态代码块。执行静态代码块的过程其实就是初始化类的过程,话说到这,说白了,前两种方式加载类的时候对类进行了初始化,而第三种没有,那么看看部分代码的实现。

 public static Class<?> forName(String className)  throws ClassNotFoundException {
return forName0(className, true, ClassLoader.getCallerClassLoader());
}

上面这段代码是Class.forName()的定义,实现里直接调用了forName0(),forName0的方法签名是:

 private static native Class forName0(String name, boolean initialize,
ClassLoader loader)throws ClassNotFoundException;

这是个本地方法,本地方法的C++实现先不研究(抛砖引玉一下,谁有好的研究可以分享一下),这个本地方法第二个参数是initialize,这个参数的true或false就是告诉虚拟机,在根据类的全限定名name加载类的时候,要对类进行初始化。在forName调用forName0的时候,看以看到initialize设置成了true,所以我们的类的静态代码块就被执行了(类被初始化了)。 实验2在new一个对象的时候,也会在加载类的时候触发其初始化方法,这个的实现在虚拟机实现的C++代码里,JVM虚拟机规范指出,在执行new指令创建一个对象的时候要对加载的类进行初始化(《深入理解java虚拟机》第七章有说)。实验3在执行ClassLoader.load一个class的时候属于被动加载类,根据虚拟机规范不会对类进行初始化。对于new和ClassLoader.load加载类的方式,在java代码层面已经看不到是否需要对类进行初始化的标志了,内部实现在JVM的C++实现中(C++实现逻辑待哥们的水平提高提高再做分析总结)。

上面都是根据实例的总结,下面来点官方的资料学习总结。

  1. 首先看下关于类加载的时候是否初始化的虚拟机规范:

    虚拟机规范则是严格规定了有且只有四种情况必须立即对类进行初始化(而加载、验证、准备自然需要在此之前开始):

    1) 遇到new 、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行初始化,则需要先触发其初始化。生成这4条指令的最常见的java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。

    2) 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。

    3) 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

    4) 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。

    可见:当满足上述4中条件之一的任何一种情况都会执行类的静态代码块,而除上述4中情况外,则不会对类的初始化(注意加粗的有且只有4个字)。

2.类从加载到卸载,整个过程可以描述为7个阶段:

加载:虚拟机需要完成以下三件事情:

  • a) 通过一个类的权限定名来获取此类定义的二进制字节流。
  • b) 将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构
  • c) 在java堆中生成一个代表这个类的java.lang.Class对象,作为访问方法区的入口

验证:

  • 连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。(备注:虽然在java语言是相对安全的,但是在字节码层面,上述java代码无法做到的事情都是可以实现的,至少在语义上是可以表达出来的。所以对字节流进行验证是相当必要的)

准备:

  • 准备阶段是正式为变量分配内存并设置变量初始值的阶段,这些内存都将在方法区中进行分配。

解析:

  • 解析过程就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用替换成直接引用的过程。(至于什么是符号引用,什么是直接引用,参考这篇博客:http://blog.csdn.net/lantian0802/article/details/9152657

(ps:验证、准备、解析统称为连接)

初始化:

  • 为类的静态变量赋予正确的初始值,当然也包括执行静态代码块的内容。

ps: 直接引用和符号引用的一个生动的例子: 那么什么是符号引用,什么又是直接引用呢?我们来举个例子:我们要找一个人,我们现有的信息是这个人的身份证号是1234567890。只有这个信息我们显然找不到这个人,但是通过公安局的身份系统,我们输入1234567890这个号之后,就会得到它的全部信息:比如湖北省武汉市武汉大学张三,通过这个信息我们就能找到这个人了。这里,123456790就好比是一个符号引用,而湖北省武汉市武汉大学张三就是直接引用。在内存中也是一样,比如我们要在内存中找一个类里面的一个叫做show的方法,显然是找不到。但是在解析阶段,jvm就会把show这个名字转换为指向方法区的的一块内存地址,比如c17164,通过c17164就可以找到show这个方法具体分配在内存的哪一个区域了。这里show就是符号引用,而c17164就是直接引用。在解析阶段,jvm会将所有的类或接口名、字段名、方法名转换为具体的内存地址。

JVM类加载过程学习总结的更多相关文章

  1. 【深入Java虚拟机】一 JVM类加载过程

    首先Throws(抛出)几个自己学习过程中一直疑惑的问题: 1.什么是类加载?什么时候进行类加载? 2.什么是类初始化?什么时候进行类初始化? 3.什么时候会为变量分配内存? 4.什么时候会为变量赋默 ...

  2. JVM类加载过程详细分析

    双亲委派加载模型 为什么需要双亲委派加载模型 主要是为了安全,避免用户恶意加载破坏JVM正常运行的字节码文件,比如说加载一个自己写的java.util.HashMap.class.这样就有可能造成包冲 ...

  3. JVM类加载过程与双亲委派模型

    类加载过程 类加载过程为JVM将类描述数据从.class文件中加载到内存,并对数据进行解析和初始化,最终形成被JVM直接使用的Java类型.包含: 加载:获取该类的二进制字节流,将字节流代表的静态存储 ...

  4. 【搞定Jvm面试】 面试官:谈谈 JVM 类加载过程是怎样的?

    类加载过程 Class 文件需要加载到虚拟机中之后才能运行和使用,那么虚拟机是如何加载这些 Class 文件呢? 系统加载 Class 类型的文件主要三步:加载->连接->初始化.连接过程 ...

  5. 三、JVM — 类加载过程

    类加载过程 加载 验证 准备 解析 初始化 类加载过程 Class 文件需要加载到虚拟机中之后才能运行和使用,那么虚拟机是如何加载这些 Class 文件呢? 系统加载 Class 类型的文件主要三步: ...

  6. [jvm] -- 类加载过程篇

    类加载过程 系统加载 Class 类型的文件主要三步 加载 通过全类名获取定义此类的二进制字节流 将字节流所代表的静态存储结构转换为方法区的运行时数据结构 在内存中生成一个代表该类的 Class对象, ...

  7. 面试题:JVM类加载机制详解(一)JVM类加载过程 背1

    首先Throws(抛出)几个自己学习过程中一直疑惑的问题: 1.什么是类加载?什么时候进行类加载? 2.什么是类初始化?什么时候进行类初始化? 3.什么时候会为变量分配内存? 4.什么时候会为变量赋默 ...

  8. JVM 类加载过程

    类从加载到虚拟机到卸载,它的整个生命周期包括:加载(Loading),验证(Validation),准备(Preparation),解析(Resolution),初始化(Initialization) ...

  9. JVM类加载原理学习笔记

    (1)类的生命周期包括了:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(Initialization).使用(Usin ...

随机推荐

  1. GROUPING SETS、ROLLUP、CUBE

    大家对GROUP BY应该比较熟悉,如果你感觉自己并不完全理解GROUP BY,那么本文不适合你.还记得当初学习SQL的时候,总是理解不了GROUP BY的作用,经过好长时间才终于明白GROUP BY ...

  2. Hadoop_Lucene

    http://codelife.me/blog/2012/11/03/jackson-polymorphic-deserialization/ http://itindex.net/blog/2012 ...

  3. perl 安装Net::ZooKeeper

    <pre name="code" class="python"><pre name="code" class=" ...

  4. BZOJ 1093: [ZJOI2007]最大半连通子图( tarjan + dp )

    WA了好多次... 先tarjan缩点, 然后题意就是求DAG上的一条最长链. dp(u) = max{dp(v)} + totu, edge(u,v)存在. totu是scc(u)的结点数. 其实就 ...

  5. BZOJ 2599: [IOI2011]Race( 点分治 )

    数据范围是N:20w, K100w. 点分治, 我们只需考虑经过当前树根的方案. K最大只有100w, 直接开个数组CNT[x]表示与当前树根距离为x的最少边数, 然后就可以对根的子树依次dfs并更新 ...

  6. PigCms 回复消息 "域名授权错误! 您使用的微信平台或源码为盗版"

    本文地址:http://duwei.cnblogs.com/ Pigcms 将自动回复的API 写死了, 这里提供一个可用的API 在 PigCms/Lib/Action/Home/Weixinact ...

  7. 使用adb签名并安装Android程序

    首先需要准备Android SDK包,我是在windows上操作的,在PATH中配置  YOUT_SDK_PATH\android-sdk-windows\platform-tools 和  YOUT ...

  8. kingso_module - Taocode

    kingso_module - Taocode 模块介绍 Merger 功能介绍 Merger的功能: 合并多台Searcher机器的部分查询结果,得到最终的完整查询结果 向Detail集群请求最终展 ...

  9. freemarker序列的拆分

    freemarker序列的拆分 1.简易说明 序列的拆分能够是数组.字符串.布尔值等等 2.实现源代码 <#--freemarker序列的拆分--> ${"hudjfkskhd你 ...

  10. CSS - ResetCss

    /* KISSY CSS Reset 理念:清除和重置是紧密不可分的 特色:1.适应中文 2.基于最新主流浏览器 */ /* 清除内外边距 */ body, h1, h2, h3, h4, h5, h ...