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. Linux学习:netstat命令

    Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态等.对于开发来说,很多时候用于查看端口占用情况. 执行netstat命令,其输出结果可以分成两部分: 1)一是“Active ...

  2. Bosch 英语面试准备分享

    上周从一个朋友那里了解到长沙一家德国外企Bosch在招人,看了下只有MES工程师是对编程经验有要求的,抱着试一试的态度,就投了简历. 没想到对方周一就给我回电话,希望我好好准备一下英语面试,过段时间去 ...

  3. BZOJ 1609: [Usaco2008 Feb]Eating Together麻烦的聚餐

    1609: [Usaco2008 Feb]Eating Together麻烦的聚餐 Description 为了避免餐厅过分拥挤,FJ要求奶牛们分3批就餐.每天晚饭前,奶牛们都会在餐厅前排队入内,按F ...

  4. 公共 DNS server IP 地址

    公共 DNS server IP 地址 名称 DNS server IP 地址 CNNIC SDNS 1.2.4.8 210.2.4.8 114 DNS 114.114.114.114 114.114 ...

  5. Android Paint、Canvas、Matrix使用讲解(一、Paint)

    http://blog.csdn.net/tianjian4592/article/details/44336949 好了,前面主要讲了Animation,Animator 的使用,以及桌面火箭效果和 ...

  6. c# datagridviewcomboboxcell值无效的解决办法

    一直认为是数据库存储的数据和datagridviewcomboboxcell对不上导致,今天碰到两者对应上了,预览的时候还是提示错误, 查看了下网上其他大神的解决方法,是数据库字段类型有误,查看了下, ...

  7. 配置SecureCRT连接Linux CentOS

    链接地址:http://f.dataguru.cn/thread-144513-1-1.html 环境:Linux:centos5.8虚拟机:VirtualBox本机:windows至于怎么安装Cen ...

  8. HDU 3909 DLX

    http://blog.csdn.net/sr_19930829/article/details/39756513 http://www.kuangbin.net/archives/hdu4069-d ...

  9. Unbuntu 14.04 下chrome browser bookmark 显示中文乱码解决方案

    来源:http://blog.csdn.net/loveaborn/article/details/29579787 网上有人给出这个问题的解决是通过修改文件/etc/fonts/conf.d/49- ...

  10. 练习 jquery+Ajax+Json 绑定数据 分类: asp.net 练习 jquery+Ajax+Json 绑定数据 分类: asp.net

    练习 jquery+Ajax+Json 绑定数据