7 虚拟机类加载机制

1 概述?

虚拟机的类加载机制是指,虚拟机把描述类的数据从Class?件加载到内存,并对数据进?验证、准备、解析、初始化等,最终形成可以被虚拟机直接使?的Java类型。

与那些在编译时需要进?链接的语?不同,在Java语???,类型的加载、链接和初始化过程都是在程序运?期间完成的,这种策略虽然会令类加载时稍微增加?些性能开销,但是会为Java应?程序提供?度的灵活性,Java天?可以动态扩展的语?特性就是依赖运?期动态加载和动态链接这个特点实现的。

2 类加载的时机?

类从被加载到虚拟机内存中开始,到卸载出内存为?,它的整个?命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使?(Using)和卸载(Unloading)7个阶段。

3 类加载的过程?

加载?

Loading is the process of finding the class file that represents the class or interface type with a particular name and reading it into a byte array. Next the bytes are parsed to confirm they represent a Class object and have the correct major and minor versions. Any class or interface named as a direct superclass is also loaded. Once this is completed a class or interface object is created from the binary representation.

加载是类加载(Class Loading)过程的?个阶段,虚拟机需要完成以下3件事情:

  • 通过?个类的全限定名来获取定义此类的?进制字节流。
  • 将这个字节流所代表的静态存储结构转化为?法区的运?时数据结构。
  • 在内存中?成?个代表这个类的java.lang.Class对象,作为?法区这个类的各种数据的访问?口。

第一条中的获取二进制字节流可以从多个地方获取。例如ZIP包(JAR),网络(Applet),运行时计算生成(java.lang.refelct.Proxy)等。

验证?

验证是为了确保Class?件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机??的安全。

虽然Java语?本?是相对安全的语?,但是Class?件并不?定要求?Java编译?来,它可以使?任何途径产?,甚?包括??六进制编辑器直接编写来产?Class?件。虚拟机如果不检查输 ?的字节流,对其完全信任的话,很可能会因为载?了有害的字节流?导致系统崩溃。

  • ?件格式验证:验证字节流是否符合Class?件格式的规范,并且能被当前 版本的虚拟机处理。
  • 元数据验证:对字节码描述的信息进?语义分析,以保证其描述的信息符合Java语?规范的要求,

准备?

Preparing involves allocation of memory for static storage and any data structures used by the JVM such as method tables. Static fields are created and initialized to their default values, however, no initializers or code is executed at this stage as that happens as part of initialization.

准备阶段为类变量分配内存并设置类变量(仅包括被被static修饰的变量)初始值,这些变量所使?的内存都将在?法区中进?分配。实例变量将会在对象实例化时随着对象一起分配在Java堆中。初始值通常情况下是数据类型的默认值.

public static int value = 123; //初始值为0

如果类字段的字段属性表中存在ConstantValue属性,那在准备阶段变量value就会被初始化为ConstantValue属性所指定的值:

public static final int value = 123; // 初始值为123

解析?

解析阶段将常量池内的符号引?替换为直接引?。

符号引?(Symbolic References): 符号引?以?组符号来描述所引?的?标,符号可以是任何形式的字?量,只要使?时能?歧义地定位到?标即可。符号引?与虚拟机实现的内存布局?关,引?的?标并不?定已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引?必须都是?致的,因为符号引?的字?量形式明确定义在Java虚拟机规范的Class?件格式中。

直接引?(Direct References): 直接引?可以是直接指向?标的指针、相对偏移量或是?个能间接定位到?标的句柄。直接引?是和虚拟机实现的内存布局相关的,同?个符号引?在不同虚拟机实例上翻译出来的直接引??般不会相同。如果有了直接引?,那引?的?标必定已经在内 存中存在。

初始化?

Initialization of a class or interface consists of executing the class or interface initialization method <clinit>.

What is the difference between <init> and <clinit>? StackOverflow

  • <init> is the (or one of the) constructor(s) for the instance, and non-static field initialization.
  • <clinit> are the static initialization blocks for the class, and static field initialization.
class X {

   static Log log = LogFactory.getLog(); // <clinit>

   private int x = 1;   // <init>

   X(){
      // <init>
   }

   static {
      // <clinit>
   }
}

对于初始化阶段,虚拟机规范严格规定了有且只有5种情况必须?即对类进?“初始化”:

  • 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进?过初始化,则需要先触发其初始化。?成这4条指令的最常见的Java代码场景是:使?new关键字实例化对象的时候、读取或设置?个类的静态字段(被final修饰、已在编译期把结果放?常量池的静态字段除外)的时候,以及调??个类的静态?法的时候。
  • 使?java.lang.reflect包的?法对类进?反射调?的时候,如果类没有进?过初始化,则需要先触发其初始化。(例如,Class.forName)
  • 当初始化?个类的时候,如果发现其?类还没有进?过初始化,则需要先触发其?类的初始化。
  • 当虚拟机启动时,?户需要指定?个要执?的主类(包含main()?法的那个类),虚拟机会先初始化这个主类。
  • 当使?JDK 1.7的动态语??持时,如果?个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、 REF_putStatic、REF_invokeStatic的?法句柄,并且这个?法句柄所对应的类没有进?过初始化,则需要先触发其初始化。

"有且只有"这5种场景中的?为称为对?个类进?主动引?。除此之外,所有引?类的?式都不会触发初始化,称为被动引?

下面代码运?之后,只会输出"SuperClass init!",?不会输出"SubClass init!"。可通过-Xlog:class+load=info打印类加载信息。

主动使用和被动使用

被动使用1

//被动使用类字段演示一:通过子类引用父类的静态字段,不会导致子类初始化
public class NotInitialization1 {
    public static void main(String[] args) {
        System.out.println(SubClass.value);
    }
}

class SuperClass {
    static {
        System.out.println("SuperClass init!");
    }
    public static int value = 123;
}

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

被动使用2

// 被动使用类字段演示二:通过数组定义来引用类,不会触发此类的初始化
public class NotInitialization2  {
    public static void main(String[] args) {
        SuperClass[] sca = new SuperClass大专栏  7 虚拟机类加载机制o">[10];
    }
}

4 类加载器?

类与类加载器?

对于任意?个类,都需要由加载它的类加载器和这个类本??同确?其在Java虚拟机中的唯?性,每?个类加载器, 都拥有?个独?的类名称空间。

?较两个类是否“相等”,只有在这两个类是由同?个类加载器加载的前提下才有意义,否则,即使这两个类来源于同?个Class?件,被同?个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。

不同的类加载器对instanceof关键字运算的结果的影响

//类加载器与instanceof关键字演示
public class ClassLoaderTest {
    static class MyClassLoader extends ClassLoader {
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            try {
                String fileName = name.substring(name.lastIndexOf(".") + 1)
                            + ".class";
                InputStream is = getClass()
                    .getResourceAsStream(fileName); // 找到class文件
                if (is == null)  return super.loadClass(name);
                byte[] b = new byte[is.available()];
                is.read(b);  // 读取class
                return defineClass(name, b, 0, b.length); // 转化为class实例
            } catch (IOException e) {
                throw new ClassNotFoundException(name);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        MyClassLoader myClassLoader = new MyClassLoader();
        Object obj = myClassLoader
            .loadClass("com.unstandingJVM.ClassLoaderTest").newInstance();
        // 输出:class com.unstandingJVM.ClassLoaderTest
        System.out.println(obj.getClass());
        //输出: false
        System.out.println(obj instanceof com.unstandingJVM.ClassLoaderTest);
    }
}

双亲委派模型?

从Java虚拟机的角度来讲,只存在两种不同的类加载器:?种是启动类加载器(Bootstrap ClassLoader),这个类加载器使?C++语?实现 ,是虚拟机??的?部分;另?种就是所有其他的类加载器,这些类加载器都由Java语?实现,独?于虚拟机外部,并且全都继承?抽象类 java.lang.ClassLoader。

  • Bootstrap ClassLoader(启动类加载器): It is usually implemented as native code because it is instantiated very early as the JVM is loaded. It loads JDK internal classes, typically loads rt.jar and other core libraries located in $JAVA_HOME/jre/lib directory.
  • Extension ClassLoader(扩展类加载器): It loads classes from the JDK extensions directory, usually $JAVA_HOME/lib/ext directory.
  • Application ClassLoader(应?程序类加载器): It loads classes from the current classpath that can be set while invoking a program using -cp or -classpath command line options.
  • User Defined ClassLoaders(?定义类加载器): It can alternatively be used to load application classes. A user defined classloader is used for a number of special reasons including run time reloading of classes or separation between different groups of loaded classes typically required by web servers such as Tomcat.
public class ViewClassLoader {
    public static void main(String[] args) {
        // 启动类加载器,HashMap位于$JAVA_HOME
        System.out.println(java.util.HashMap.class.getClassLoader());
        // 扩展类加载器,位于$JAVA_HOME/lib/ext
        System.out.println(javafx.collections.FXCollections
                    .class.getClassLoader());
        // 应用程序类加载器,位于当前目录
        System.out.println(ViewClassLoader.class.getClassLoader());
        // 自定义类加载器
        ClassLoaderTest.MyClassLoader myClassLoader =
                new ClassLoaderTest.MyClassLoader();
        Object obj = myClassLoader
                .loadClass("com.unstandingJVM.ClassLoaderTest").newInstance();
        System.out.println("class loader for testClassLoader class:"
                + obj.getClass().getClassLoader());
    }
}
// 输出结果
// null  #启动类加载器
// [email protected] # 扩展类加载器
// [email protected] # 应用程序类加载器
// [email protected] # 自定义类加载器

类加载器的?作过程是:如果?个类加载器收到了类加载的请求,它?先不会??去尝试加载这个类,?是把这个请求委派给?类加载器去完成,每?个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当?加载器反馈???法完成这个加载请求(它的搜索范围中没有找到所需的类)时,?加载器才会尝试??去加载。类加载器之间的这种关系被称为类加载器的双亲委派模型(Parents Delegation Model)。

到了Java SE9,classloader越来越复杂,详细见The Class Loader Hierarchy

ref?

  1. https://www.baeldung.com/java-classloaders

原文地址:https://www.cnblogs.com/dajunjun/p/11698466.html

时间: 2024-10-15 12:18:24

7 虚拟机类加载机制的相关文章

Java虚拟机类加载机制

原文出处: 朱小厮 看到这个题目,很多人会觉得我写我的java代码,至于类,JVM爱怎么加载就怎么加载,博主有很长一段时间也是这么认为的.随着编程经验的日积月累,越来越感觉到了解虚拟机相关要领的重要性.闲话不多说,老规矩,先来一段代码吊吊胃口. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public cla

虚拟机类加载机制(3)——线程上下文类加载器

之所以将线程上下文类加载器(Thread Context ClassLoader)单独拿出来写,确实是因为它涉及的东西比较多,既然带有线程两个字,一定也是非常重要的一个东西. 我们首先来回顾一下类加载器的双亲委派模型. 在上一章<虚拟机类加载机制(2)——类加载器>中我们解释了何为类加载器的“双亲委派模型”,知道了双亲委派模型给我们带了一个好处就是Java类随着它的类一起具备了一种带有优先级的层次关系.简单的例子就是Object类在程序的各种类加载环境中都会由启动类加载器来加载,换言之,它无论

虚拟机类加载机制——类加载时机

由于道行不够深,所以此篇类加载机制的讲解主要来自于<深入理解Java虚拟机——JVM高级特性与最佳实践>的第7章 虚拟机类加载机制. 在前面<初识Java反射>中我们在开头提到要了解Java反射,就得要了解虚拟机的类加载机制.在这里,我们来试着窥探一下何为类加载. “虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,类型的加载.连接和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制.”这句话确实读着好懂,但到底类加载做了什么呢?我们都知道

Java虚拟机类加载机制——案例分析

原文出处: 朱小厮 在<Java虚拟机类加载机制>一文中详细阐述了类加载的过程,并举了几个例子进行了简要分析,在文章的最后留了一个悬念给各位,这里来揭开这个悬念.建议先看完<Java虚拟机类加载机制>这篇再来看这个,印象会比较深刻,如若不然,也没什么关系~~下面是程序代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package jvm.cla

深入理解JVM读书笔记三: 虚拟机类加载机制

Java虚拟机类加载机制是把Class类文件加载到内存,并对Class文件中的数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程. 7.1概述 与那些在编译时需要进行链接工作的语言不同,在Java语言里面,类型的加载和链接过程都是在程序运行期间完成的(其实C++也是分为静态链接库和动态链接库的),这样会在类加载时稍微增加一些性能开销,但是却能为Java应用程序提供高度的灵活性,Java中天生可以动态扩展的语言特性就是依赖运行期动态加载和动态链接这个特点实现的. 7.

深入理解Java虚拟机-----------虚拟机类加载机制

虚拟机类加载机制 类从被加载到虚拟机内存开始,到卸载出内存为止,整个生命周期包括:加载,验证,准备,解析,初始化,使用,卸载等7个阶段.其中,验证,准备,解析3个部分称为连接. 以上7个阶段中,加载,验证,准备,初始化和卸载五个阶段的顺序是确定的,类的加载过程必需按照这种顺序按部就班的开始(开始并不意味着按部就班的"进行"或"完成",因为这些阶段通常是互相交叉地混合式进行的).而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运

[转]Java虚拟机类加载机制浅谈

Java语言是一种编译后再经过解释器执行的过程, 解释器主要就是如何处理解释Class文件的二进制字节流.JVM主要包含三大核心部分:运行时数据区,类加载器和执行引擎. 虚拟机将描述类的数据从Class文件加载到内存,并对数据进行校验.准备.解析和初始化,最终就会形成可以被虚拟机使用的Java类型,这就是一个虚拟机的类加载机制.Java中的类是动态加载的,只有在运行期间使用到该类的时候,才会将该类加载到内存中,Java依赖于运行期动态加载和动态链接来实现类的动态使用. 一个类的整个生命周期如下:

虚拟机类加载机制详解

目录: 1.类加载的时机 2.类加载的过程 3.类加载器 一.类加载的时机 类从被加载到虚拟机内存中开始,到卸载除内存为止,他的整个生命周期包括:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(Initialization).使用(Using)和卸载(Unloading),这七个阶段的发生顺序如下图 上图中,加载.验证.准备.初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须要按照这种顺序按部就班地开始,而解析阶

转载---虚拟机类加载机制

虚拟机类加载机制  虚拟机把描述的类的数据从class文件加载到内存后,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制.  类加载的时机 类被加载到虚拟机内存开始,到卸载出内存为止.它的整个生命周期包括:类加载(Loading),验证(Verification),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Using)和卸载(Unloading)7个阶段.其中验证,准备,解析

【JVM】虚拟机类加载机制

什么是类加载 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. <[JVM]类文件结构>讲的是Class文件结构,即我们编写的Java代码(.java文件)经过编译后生成Class文件(.class文件).这一章讲述的是如何将这个Class文件加载到内存并最终形成虚拟机直接使用Java类型的过程. 1.类加载的时机 类的生命周期 类的生命周期 其中,加载.验证.准备.初始化和卸载这5个顺序