类的加载机制

目录

什么是类的加载机制

  • 简单的说,就是虚拟机把类的数据从Class文件加载到内存,对数据进行检验,转换解析和初始化,最终形成可以被虚拟机直接使用的java类型

类加载的过程

  • 类加载的过程:加载(注意和类加载区别开来),验证,准备,解析,初始化

加载

  • 通过一个类的全限定名来获取其定义的二进制流
  • 将这个字节流所代表的静态存储结构(类信息,静态常量、常量)转化成方法区的运行时数据结构
  • 在内存中生成一个代表这个类的Class对象,作为方法区中这些数据的访问入口

验证

  • 目的:确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的安全。Class文件可以从任何途径来,包括我们手动编辑一个Class文件,如果我们不对他进行验证,那么就可能会出现安全问题。
  • 文件格式验证:是否符合Class文件的格式的规范,能够被当前版本的虚拟机处理。通过这个验证,可以保证字节流能够正确解析并存储于方法区,格式上符合一个Java类型信息的要求。只有通过这个验证后,字节流才会进入内存的方法区进行存储。
  • 元数据验证,对字节码描述的信息进行语义分析,比如这个类是否有父类,是否继承了final修饰的类,保证描述的信息符合java语言规范。
  • 字节码验证,通过数据流和控制流分析,确定程序语义是和合法的,符合逻辑的。这个阶段对类的方法体进行验证分析,保证在类的方法运行时不会危害虚拟机。比如说验证类型转换是否合理,比如说父类不能转为子类
  • 符号引用验证,对常量池中的各种符号引用的信息进行匹配性校验,比如验证符号引用中通过字符串描述的全限定名是否能找到对应的类。确保解析动作能够正常执行。

准备

  • 为类的静态变量分配内存并设置类常量的初始值
public static int value = 123
  • 这段代码经过准备阶段后,value的值是0
public static final int value = 123
  • 被static和final同时修饰的,经过准备阶段后,value是123

解析

  • 虚拟机将常量池内的符号引用(类和接口的全限定名、字段的名称和描述符、方法的名称和描述符)替换为直接引用(可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄)的过程

初始化

  • 准备阶段变量已经赋过系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源。说白了就是初始化静态变量指定的值和执行静态代码块

必须初始化的情况

  • 用new创建对象(准确来说是new指令)的时候
  • 读取设置一个类的静态字段,调用类的静态方法
  • 使拥reflect包的方法对类进行反射调用的时候
  • 初始一个类的时候,如果一个类的父类还没有初始化的时候,触发父类的初始化
  • 主类(main方法的类)
public class SuperClass
{
    public static int value = 123;

    static
    {
        System.out.println("SuperClass init");
    }
}

public class SubClass extends SuperClass
{
    static
    {
        System.out.println("SubClass init");
    }
}

public class Test
{
    public static void main(String[] args)
    {
        System.out.println(SubClass.value);
    }
}
//输出:SuperClass init
123
  • 通过子类来获取父类的静态字段,只会触发父类的初始化不会触发子类的初始化
public static void main(String[] args)
    {
        SuperClass s[]=new SuperClass[10];
    }
  • 并没有输出SuperClass init,也就是没有初始化这个类,通过反编译发现用的指令是newarray,所以并没有触发初始化,如果不是创建数组而是创建对象的话,会输出SuperClass init,通过反编译发现指令是new而不是newarray
class ConstClass
{
    public static final String HELLOWORLD =  "Hello World";

    static
    {
        System.out.println("ConstCLass init");
    }
}

public class TestMain
{
    public static void main(String[] args)
    {
        System.out.println(ConstClass.HELLOWORLD);
    }
}
  • 并没有输出ConstClass init,也就是说明并没有触发该类初始化,这是因为在编译阶段通过常量传播优化,这个字符串已经存储在NotInitialization类的常量池中,也就是实际上对这个常量的引用变为NotInitialization类对自身常量池的引用了

类加载器

  • 通过一个类的全限定名来获取其定义的二进制字节流的代码模块称为类的加载器
  • 一个类需要加载他的类加载器和这个类本身来确定在虚拟机的唯一性,也就是说两个类来源一个Class文件,如果加载他们的类加载器不同,那么这两个类不同

    public static void main(String[]args) throws Exception{
        ClassLoader myLoader=new ClassLoader(){
            @Override
            public Class<?>loadClass(String name)throws ClassNotFoundException{
                try{
                    String fileName=name.substring(name.lastIndexOf(".")+1)+".class";
                    InputStream is=getClass().getResourceAsStream(fileName);
                    if(is==null){
                        return super.loadClass(name);
                    }
                    byte[]b=new byte[is.available()];
                    is.read(b);
                    return defineClass(name,b,0,b.length);
                }
                catch(IOException e){
                    throw new ClassNotFoundException(name);
                }
            }
        }
        ;
        Object obj=myLoader.loadClass("Person").newInstance();
        System.out.println(obj.getClass());
        System.out.println(obj instanceof Person);//false
    }
  • 在这里自己定义一个类加载器,而在这里实际上是存在两个Person类,一个是我们自己定义的类加载器加载的,一个是系统应用程序类加载器加载的

  • 从虚拟机的角度,只有两种不同的类加载器,一种是启动类加载器,C++实现,是虚拟机的一部分。另一部分就是其他的类加载器,有java实现,继承自抽象类ClassLoader,属于虚拟机之外。
  • 从java开发人员的角度看,分为启动类加载器,扩张类加载器,应用类加载器。

启动类加载器(Bootstrap ClassLoader)

  • 加载的是JAVA_HOME/lib下的类库,启动类加载器无法被java程序直接引用

扩展类加载器(Extension ClassLoader)

  • 这个类加载器由sun.misc.Launcher$ExtClassLoader实现,它负责用于加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量指定所指定的路径中所有类库,开发者可以直接使用扩展类加载器。

应用程序类加载器(Application ClassLoader)

  • 由sun.misc.Launcher$AppClassLoader实现,负责加载用户类路径(java.class.path)上指定的类库,开发者可以直接使用这个类加载器

双亲委派模型

  • 图描述的就是所谓的双亲委托模型,除了顶层的启动类加载器外,其他加载器都有他的父加载器,但这不是继承关系,而是使用组合来复用父加载器的代码
  • 工作过程:如果一个类收到类加载的请求,它首先不会自己去尝试去加载这个类,而是把这个请求委托给父类加载器去完成,所有的加载请求最后都会传授到顶层的启动类加载器,只有当父类加载器在他的搜索范围找不到所需的类的时候,也就是无法完成加载请求的时候,子加载器才会尝试去加载。

为什么要双亲委派模型

  • 例如java.lang.Object,存放于rt.jar中,无论哪一个类加载器要去加载这个类,最终都是由Bootstrap ClassLoader去加载,因此Object类在程序的各种类加载器环境中都是一个类。相反,如果没有双亲委派模型,由各个类加载器自己去加载的话,如果用户自己编写了一个java.lang.Object,并放在CLASSPATH下,那么系统会出现多个不同的Objcet类

  • 如果两个类加载器都要加载一个Class,如果没有双亲委托,各自去加载,那么会加载两份Class文件

双亲委托模型的实现

protected synchronized Class<?> loadClass(String name, boolean resolve)
307         throws ClassNotFoundException
308     {
309         // First, check if the class has already been loaded检查是否已经被加载
310         Class c = findLoadedClass(name);
            //如果还没被加载
311         if (c == null) {
312             try {
                    //父加载器是否为null,因为启动类加载器是C++代码编写的,所以父加载器为null的时候,说明此时父加载器是启动类加载器。如果父加载器不为空,让父加载器去加载
313                 if (parent != null) {
314                     c = parent.loadClass(name, false);
315                 } else {
316                     c = findBootstrapClass0(name);
317                 }
318             } catch (ClassNotFoundException e) {
319                 // If still not found, then invoke findClass in order如果父加载器无法加载,调用本身的方法的来进行类加载器
320                 // to find the class.
321                 c = findClass(name);
322             }
323         }
324         if (resolve) {
325             resolveClass(c);
326         }
327         return c;
328     }

我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)

作者:jiajun 出处: http://www.cnblogs.com/-new/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。

jvm系列 (五) ---类的加载机制的更多相关文章

  1. JVM:Java 类的加载机制

    虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验,转换,解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制. 类的生命周期 类从被加载到虚拟机内 ...

  2. jvm系列(一):java类的加载机制

    java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装 ...

  3. JVM(1):Java 类的加载机制

    原文出处: 纯洁的微笑 java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang. ...

  4. Jvm类的加载机制

    1.概述 虚拟机加载Class文件(二进制字节流)到内存,并对数据进行校验.转换解析和初始化,最终形成可被虚拟机直接使用的Java类型,这一系列过程就是类的加载机制. 2.类的加载时机 类从被虚拟机加 ...

  5. JVM-01:类的加载机制

    本文从 纯洁的微笑的博客 转载 原地址:http://www.ityouknow.com/jvm.html 类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内 ...

  6. Java虚拟机JVM学习02 类的加载概述

    Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...

  7. 深入java虚拟机学习 -- 类的加载机制

    当看到"类的加载机制",肯定很多人都在想我平时也不接触啊,工作中无非就是写代码,不会了可以百度,至于类,jvm是怎么加载的我一点也不需要关心.在我刚开始工作的时候也觉得这些底层的内 ...

  8. 24.类的加载机制和反射.md

    目录 1类的加载连接和初始化 1.1类的加载过程 1.2类的加载器 1.2.1类的加载机制 1类的加载连接和初始化 1.1类的加载过程 类的加载过程简单为分为三步:加载->连接->初始化 ...

  9. 深入java虚拟机学习 -- 类的加载机制(续)

    昨晚写 深入java虚拟机学习 -- 类的加载机制 都到1点半了,由于第二天还要工作,没有将上篇文章中的demo讲解写出来,今天抽时间补上昨晚的例子讲解. 这里我先把昨天的两份代码贴过来,重新看下: ...

随机推荐

  1. echarts_部分图表配置_dataZoom精确控制显示数据数量

    echarts为我们提供了dataZoom组件,当数据过多时就有了它的用武之地,业务场景:数据返回100调可是为了前端显示效果默认只显示20条,其他数据由dataZoom控制显示隐藏: functio ...

  2. React Native编译器的配置以及基础知识

    入职新公司,这边打算采用RN来写界面,所以学习一波这一块的知识. 采用的是WebStorm来编译,据同事说,比他采用atom编译要多很多语法提示. 下载地址:https://www.jetbrains ...

  3. WordPress中函数钩子hook的作用及基本用法

    WordPress 的插件机制实际上只的就是这个 Hook 了,它中文被翻译成钩子,允许你参与 WordPress 核心的运行,是一个非常棒的东西,下面我们来详细了解一下它.钩子分类 钩子分为两种,一 ...

  4. HTML5——JSON的方法实现对象copy

    JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式.它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言 ...

  5. Java 架构师之路(2)

    一.技术 J2EE技术是架构师的基础.1.<Java编程思想> 初学Java时阅读这本书觉得好难,阅读第二遍时才觉得讲的很细致.这是一本不怕多读的好书. 2.<J2EE应用与BEA ...

  6. 深入了解IAT原理

    ---------------------------编辑时突然死机自动保存也没有用真的痛苦回头补上------------------ 输入表中的这些间接跳转是无法正常运行的,因为在正常情况,操作系 ...

  7. MySQL编程基础

    本文是关于MySQL编程中的一些基础知识,包括变量和运算符.常用语句.函数. 一.变量与运算符 1.用户会话变量声明:SET @变量名 = 表达式;//即:用户会话变量无需提前定义,直接用赋值语句赋值 ...

  8. Canvas 旋转风车绘制

    写在前面:   亲爱的朋友们大家好,鄙人自学前端,第一次写博客,写的不好的地方,烦请同学们谅解,如果本文对你有一丁点帮助,还请劳驾您给我点个赞,您的认可将是我坚持下去的强大动力!谢谢! 在进行教学之前 ...

  9. 安装 CentOS 时, BIOS 设置界面,找不到虚拟镜像

    安装 CentOS 时, 遇到 BIOS 设置界面,找不到虚拟镜像  1. 启动电脑或重启电脑,当电脑还没有进入window图标界面,按F2或DEL 2. 左下角有一个 Advanced Mode(F ...

  10. Flask-RESTful接口开发学习笔记一:实现简单的GET请求

    1.首先安装需要依赖的第三方包: (1):Flask (2):Flask-RESTful 如何安装就不在啰嗦,pip也好,下载到本地再安装也好,看你随意. 2.我们先实现GET请求. Q:什么是GET ...