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. 依赖和用jar包得区别

    有个项目,需要用到第三方开发的一个jar文件,我先是把生成的jar文件直接拷贝到我的项目的libs目录下,项目自动加载了引用的jar包,在java文件中使用也没有问题,但是由于引用的jar文件中有自定 ...

  2. BZOJ 1600: [Usaco2008 Oct]建造栅栏

    1600: [Usaco2008 Oct]建造栅栏 Time Limit: 5 Sec  Memory Limit: 64 MB Description 勤奋的Farmer John想要建造一个四面的 ...

  3. C++数据结构之图

    图的实现是一件很麻烦的事情,很多同学可能在学数据结构时只是理解了图的基本操作和遍历原理,但并没有动手实践过.在此,我说说我的实现过程. 首先,在草稿纸上画一个图表,这里是有向图,无向图也一样,如下: ...

  4. SVN基础命令手册

    SVN版本号:1.5 及更新版本号 名词说明: WC:Working Copy 你的工作区 Versioned:受控的:受版本号控制的 SVN是什么? SVN是开源的版本号控制系统. 比CVS很多其它 ...

  5. MSSQLServer的备份与还原

    最近用到了mssql2000的数据备份还原到2008上, 在备份2000时,一定注意要备份成一个文件,就是目标那里只添加一个就好,(否则待会还原数据库时要添加这两个文件,要不就报“备份了几个簇,只提供 ...

  6. iOS开发中NSDate时间戳的转换--

    NSTimeInterval time =(NSTimeInterval )[model.day floatValue]; NSDate *date = [NSDate dateWithTimeInt ...

  7. BZOJ 1734: [Usaco2005 feb]Aggressive cows 愤怒的牛( 二分答案 )

    最小最大...又是经典的二分答案做法.. -------------------------------------------------------------------------- #inc ...

  8. 将时间显示为“刚刚”“n分钟/小时前”等

    在很多场合为了显示出信息的及时性,一般会将时间显示成“刚刚”,“5分钟前”,“3小时前”等,而不是直接将时间打印出来.比如微博,SNS类应用就最长用到这个功能.而一般存储在数据库中的时间格式为 Uni ...

  9. 转:git教程 ~~非常好的入门教程

    --------------------------------------- notes: cdmkdir dirnametouchls > filenamelsecho "this ...

  10. Hadoop 2.x(YARN)安装配置LZO

    今天尝试在Hadoop 2.x(YARN)上安装和配置LZO,遇到了很多坑,网上的资料都是基于Hadoop 1.x的,基本没有对于Hadoop 2.x上应用LZO,我在这边记录整个安装配置过程 1. ...