一、前言

  首先,小小测试,看是否已经掌握了JVM类加载的过程

  1.1、测试一

class Singleton {
private static Singleton sin = new Singleton();
public static int counter1;
public static int counter2 = 0; private Singleton() {
counter1++;
counter2++;
} public static Singleton getInstance() {
return sin;
}
} public class Test {
public static void main(String[] args) {
Singleton sin = Singleton.getInstance();
System.out.println(sin.counter1);
System.out.println(sin.counter2);
}
}

  输出结果为:

  1 3 ?

  1 0 ?  

  0 1?

  1.2、测试二:

class Singleton {
public static int counter1;
public static int counter2 = 2;
private static Singleton sin = new Singleton(); private Singleton() {
counter1++;
counter2++;
} public static Singleton getInstance() {
return sin;
}
} public class Test {
public static void main(String[] args) {
Singleton sin = Singleton.getInstance();
System.out.println(sin.counter1);
System.out.println(sin.counter2);
}
}

  

输出结果为:

  1 3 ?

  1 2 ?  

  0 1?

正确的输出结果如下:

测试一的结果为:1 0

测试二的结果为:1 3

如果对结果有疑惑或者不知道原因的园友需要了解类加载器的具体细节,相信看了本篇文章,一定会解开您的疑惑。废话不多说,直奔主题。

二、背景知识

  2.1、Java虚拟机通过装(加)载、连接、初始化一个Java类型,使该类型可以被正在运行的Java程序所使用。  

  ①装(加)载类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区中,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构,之后可以用Class对象进行相关的反射操作。

  ②连接分为三个子步骤    

    验证:确保被加载的类的正确性

    准备:为类的静态变量分配内存,并将其初始化为默认值

    解析: 把类中的符号引用转换为直接引用

  ③初始化为为类的静态变量赋予正确的初始值

  如下为流程图:

  2.2、关于初始化的细节  

  所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”时才初始化他们,下面六种情况符合首次主动使用要求。    

    ① 创建类的实例    

    ②访问某个类或接口的非常量静态域,或者对该非常量静态域赋值

    ③ 调用类的静态方法

    ④反射(如Class.forName(“com.test.Test”)(其中Test为一个类),而Test.class就不是首次使用)

    ⑤初始化一个类的子类

    ⑥Java虚拟机启动时被标明为启动类的类(Test)(Test为包含了程序入口main方法的类)

  2.3、如论如何,如果一个类型在其首次主动使用之前还没有被装(加)载和连接的话,那它必须在此时进行加载和连接,这样它才能够被初始化。

三、问题分析

  3.1、读到这里应该可以分析出为什么之前的程序会输出那样的结果,下面我们来一起分析一下整个过程。

  ①对于测试一的结果分析

  首先在main中调用了Singleton的getInstance类静态方法,符合第③条,需要初始化类,即初始化Singleton,首先需要装(加)载和连接,从硬盘中加载进内存,然后进入连接的验证,没有问题,OK,进入准备阶段,此时,将类变量sin、counter1、counter2分配内存,并初始化默认值null、0、0。紧接着,将符号引用转化为直接引用(暂  时不不要太了解,程序中就是将Singleton符号转化为在内存里直接对地址的引用,这样就可以通过地址直接访问Singleton类型了)。接下来是初始化过程,首先调用sin的构造方法,然后将counter1、counter2分别+1,即counter1 = 1,counter2 = 1,完成了sin静态变量的初始化,然后初始化静态变量counter1,但是由于没有对counter1赋初值,所以counter1还是为1,然而,程序中对counter2进行了赋初值操作,即将counter2赋值为0。这样便完成了类型的初始化,得到的counter1和counter2的结果为1和0,分析完毕。

  结果分析流程图如下:

    

    

②同理也可以对测试二进行结果分析

四、总结

  整个类的初始化三个阶段细节过程远比这个复杂得多,但是我们仍可以通过类的整个宏观流程来分析出正确的结果,对过程的分析也有助于我们写出正确的程序。真正做到知其然知其所以然。也感谢各位园友的观看,谢谢。

【JVM】JVM之类加载器的更多相关文章

  1. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  2. JVM的艺术—类加载器篇(二)

    分享是价值的传递,喜欢就点个赞 引言 今天我们继续来深入的剖析类加载器的内容.上节课我们讲了类加载器的基本内容,没看过的小伙伴请加关注.今天我们继续. 什么是定义类加载器和初始化类加载器? 定义类加载 ...

  3. JVM的艺术—类加载器篇(三)

    JVM的艺术-类加载器篇(三) 引言 今天我们继续来深入的剖析类加载器的内容.上篇文章我们讲解了类加载器的双亲委托模型.全盘委托机制.以及类加载器双亲委托模型的优点.缺点等内容,没看过的小伙伴请加关注 ...

  4. Java虚拟机笔记 – JVM 自定义的类加载器的实现和使用2

    1.用户自定义的类加载器: 要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定类的名 ...

  5. JVM学习--(六)类加载器原理

    我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...

  6. JVM启动过程 类加载器

    下图来自:http://blog.csdn.net/jiangwei0910410003/article/details/17733153 package com.test.jvm.common; i ...

  7. JVM学习记录-类加载器

    前言 JVM设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外面去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称为“类 ...

  8. 【深入理解JVM】:类加载器与双亲委派模型

    类加载器 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过一个类的全限定名来获取定义此类的二进制字 ...

  9. JVM体系结构之二:类加载器之2:JVM 自定义的类加载器的实现和使用

    一.回顾一下jdk自带的类加载器: 1.java虚拟机自带的加载器     根类加载器(Bootstrap,c++实现)     扩展类加载器(Extension,java实现)     应用类加载器 ...

  10. JVM概述和类加载器

    JVM概述 1.Java虚拟机所管理的内存包括以下几个运行时数据区域:   ①.程序计数器     程序计数器是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器,字节码解释器工作时就是 ...

随机推荐

  1. 实现Ogre的脚本分离 - 天龙八部的源码分析(一)

    目的 在研究天龙八部游戏的源码之时, 发现 Ogre 材质的模板部分被单独放在一个 material 文件之内, 继承模板的其他材质则位于另外的文件, 当我使用Ogre 官方源码, 加载脚本时其不会查 ...

  2. 【原创】.NET之我见

    最近在准备面试,自己也顺带巩固了下基础,加上自己对码农的一些理解都写在这里了,水平不行,欢迎吐槽 //.NET基础 1.Class 和struct 区别 类是一种“引用类型”.创建类的对象时,对象赋值 ...

  3. 如何使用硬盘安装debian8.3?

    环境:能开机的windowsxp系统+debian8.3cd+wingrub 大致原理:使用wingrub编辑启动菜单,编辑root根目录,加载相对根目录的linux内核kernel,加载initrd ...

  4. 《JAVA与模式》之抽象工厂模式

    场景问题 举个生活中常见的例子——组装电脑,我们在组装电脑的时候,通常需要选择一系列的配件,比如CPU.硬盘.内存.主板.电源.机箱等.为讨论使用简单点,只考虑选择CPU和主板的问题. 事实上,在选择 ...

  5. 我的ORM之十三 -- 性能参数

    我的ORM索引 测试环境 台式机: 主板:映泰Z77 CPU:i5 3470(3.2GHz) 内存:DDR3 1600 8G(单条) 硬盘:创见 SSD 256G ORM从过程上,可以分两个大的部分: ...

  6. STC12C5A60S2笔记7(定时器)

    1. 基本特性 STC12C5A60S2单片机集成了两个16位定时/计数器. 1)寄存器 1.1)TMOD 定时器工作方式控制寄存器,包括13位寄存器.16位寄存器.8位寄存器等: 1.2)TCON ...

  7. 图解集合3:CopyOnWriteArrayList

    初识CopyOnWriteArrayList 第一次见到CopyOnWriteArrayList,是在研究JDBC的时候,每一个数据库的Driver都是维护在一个CopyOnWriteArrayLis ...

  8. 嗅探、中间人sql注入、反编译--例说桌面软件安全性问题

    嗅探.中间人sql注入.反编译--例说桌面软件安全性问题 今天这篇文章不准备讲太多理论,讲我最近遇到的一个案例.从技术上讲,这个例子没什么高深的,还有一点狗屎运的成分,但是它又足够典型,典型到我可以讲 ...

  9. C#Light Everywhere

    C#语法嵌入式脚本,0.1Beta版本咯,可用于各种环境,欢迎测试. 可以解决各种热更新问题 比如Unity在AOT环境下,比如各种不能采用动态加载DLL的场合. 如果遇到bug,请给我留言,我会从速 ...

  10. Android多线程分析之四:MessageQueue的实现

    Android多线程分析之四:MessageQueue的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前面两篇文章<Androi ...