类加载机制深度解析(代码片段)

qishanmozi qishanmozi     2023-04-21     472

关键词:

一、类加载过程

多个java文件经过编译打包生成可运行jar包,最终由java命令运行某个主类的main启动程序,这里需要先通过类加载器把主类加载到JVM
主类在运行过程中如果使用到其他类,会逐步加载这些类。
注意:jar包里的类不是一次性全部加载的,是使用到时才加载,不过类似于java.lang.Object这种支持JVM运行的类会在启动时便被加载。
类加载过程
加载>>验证>>准备>>解析>>初始化>>使用>>卸载
  • 加载:在硬盘上查找并通过IO将字节码文件读入到内存中,使用到类时才会被加载,例如调用类的main()方法,new对象等
  • 验证:验证字节码文件的正确性
  • 准备:为类的静态变量分配内存空间,并给静态变量赋予初始值
  • 解析:将符号引用替换为这直接引用,该阶段会把一些静态方法(符号引用,比如 main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链 接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用 
  • 初始化:对类的静态变量初始化为指定的值,执行静态代码块
技术图片
二、类加载器和双亲委派机制
类的加载主要通过类加载器来实现,java中的类加载器如下:
  • 启动类加载器(BootstrapClassLoader):负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等
  • 扩展类加载器(ExtClassLoader):负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包
  • 应用程序加载器(AppClassLoader):负责加载ClassPath路径下的类包,主要是java开发人员自己写的类生成的字节码文件
  • 自定义加载器:开发人员可以通过继承ClassLoader这个类来自定义自己的类加载器,只需要实现findClass()这个方法即可,如果要打破双亲委派机制则需要额外自己实现loadClass()这个方法修改代码使其不使用双亲委派的方式。
类加载器示例:
package jvm;

public class TestJDKClassLoader 
    public static void main(String[] args) 
        System.out.println(String.class.getClassLoader());
        System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());
        System.out.println(TestJDKClassLoader.class.getClassLoader().getClass().getName());
        System.out.println(ClassLoader.getSystemClassLoader().getClass().getName());
    
  
结果:
null//启动类加载器是使用C++语言实现,所以无法打印
sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$AppClassLoader
 
双亲委派机制的逻辑大致如下:
1.首先加载指定名称的类是否已被加载过,如果加载过就不需要重复加载,直接返回。
2.如果此类没有被加载,那么判断是否有父类加载器,如果有,则委派给父加载器加载,如果没有则直接委派给启动类加载器加载。
3.如果父加载器及bootstrapClassLoader均没有找到目标类则有当前类加载器的findClass完成加载。
总结:加载器加载时将加载动作逐级向上委托直到最高级的启动类加载器,再从最高级向下逐级进行目标类加载,如果在某一级加载到了目标类则不再向下继续。
设计双亲委派机制的目的:
  • 沙盒安全机制:java开发人员自己写的java.lang.class不会被加载,防止核心API库被篡改。
  • 避免类的重复加载:当父亲类加载器已经加载到目标类时,字加载器便不会在进行加载,保证的被加载类的唯一性。
在自定义类加载器示例:
自定义类加载器主要是重写findclass()方法:
package jvm;

import java.io.FileInputStream;
import java.io.IOException;

//自定义类加载器
public class MyClassloaderTest extends ClassLoader
    private String classPath;

    //初始化时指定字节码目录所在目录
    public MyClassloaderTest(String classPath) 
        this.classPath = classPath;
    
	//将字节码文件加载到内存中
    private byte[] loadByte(String name) throws IOException 
        name = name.replaceAll(".","/");
        FileInputStream fis = new FileInputStream(classPath+"/"+name+".class");
        int len = fis.available();
        byte[] bt = new byte[len];
        fis.read(bt);
        fis.close();
        return bt;
    

    //通过该类返回Class对象
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException 
        try 
            byte[] data = loadByte(name);
            return defineClass(name,data,0,data.length);
         catch (IOException e) 
            e.printStackTrace();
            throw new ClassNotFoundException();
        
    

 
 
打破双亲委派机制
如果要打破双亲委派机制只需要重写loadClass()这个方法
package jvm;

import java.io.FileInputStream;
import java.io.IOException;

//自定义类加载器
public class MyClassloaderTest extends ClassLoader
    private String classPath;

    public MyClassloaderTest(String classPath) 
        this.classPath = classPath;
    

    private byte[] loadByte(String name) throws IOException 
        name = name.replaceAll(".","/");
        FileInputStream fis = new FileInputStream(classPath+"/"+name+".class");
        int len = fis.available();
        byte[] bt = new byte[len];
        fis.read(bt);
        fis.close();
        return bt;
    

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException 
        synchronized (getClassLoadingLock(name)) 
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            /*
            * 
            * 原双亲委派逻辑代码位置
            * 
            * */
            if (c == null) 
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
//                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            
            if (resolve) 
                resolveClass(c);
            
            return c;
        
    

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException 
        try 
            byte[] data = loadByte(name);
            return defineClass(name,data,0,data.length);
         catch (IOException e) 
            e.printStackTrace();
            throw new ClassNotFoundException();
        
    

 
测试类:
 package jvm;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test 
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException 
        MyClassloaderTest classloader = new MyClassloaderTest("F:	est");
        Class clazz = classloader.loadClass("java.lang.String");
        Object o = clazz.newInstance();
        Method method = clazz.getDeclaredMethod("sout", null);
        method.invoke(o,null);
        System.out.println(clazz.getClassLoader().getClass().getName());
    

 



jvm类加载机制一(代码片段)

类加载的过程什么是类加载?Java编译器会将我们编写好的代码编译成class字节码文件,JVM会把这些class字节码文件加载到内存中,并对加载的数据进行校验、准备、解析并初始化,这个过程就是类加载机制。类加载分为三个阶段... 查看详情

29.类加载机制类加载过程加载验证准备解析初始化总结(代码片段)

29.类加载机制29.1.类加载过程类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。它们开始的顺序如下图所示:其中类加载的... 查看详情

类加载机制(代码片段)

类是在运行期间动态加载的。类的生命周期 包括以下7个阶段:加载(Loading)验证(Verification)准备(Preparation)解析(Resolution)初始化(Initialization)使用(Using)卸载(Unloading)其中解析过程在某些情况下可以在初始化阶... 查看详情

java技术专题深度分析加载器与双亲委派机制「入门篇」(代码片段)

加载器与双亲委派机制类加载器是怎么被创建出来的?什么是双亲委派机制?为什么要有这种机制?Class实例和类加载器究竟是在JavaHeap中,还是在方法区中?类加载器:可以实现通过一个类的全限定名称来获取... 查看详情

jvm类加载器命名空间深度解析与实例分析(代码片段)

一、创建Sample1、创建实例publicclassMyPersonprivateMyPersonmyPerson;publicvoidsetMyPerson(Objectobj)this.myPerson=(MyPerson)obj;  2、创建测试类publicclassMyTest20publicstaticvoidmain(String[]args)throwsExceptio 查看详情

虚拟机类加载机制(代码片段)

概述Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制。类的生命周期一个类型从被加载到虚拟机内存中... 查看详情

jvm类加载机制全面解析,一篇完整彻底搞懂(代码片段)

我是目录:1、概述:2、类的生命周期:3、类加载器:4、类加载机制——双亲委派机制1、概述:2、类的生命周期:包括7个阶段:加载、验证、准备、解析、初始化、使用和卸载。(其中验证、准... 查看详情

jvm第六卷---类加载机制(代码片段)

JVM第六卷---类加载机制类加载机制加载链接验证准备解析初始化----<cinit>()V方法发生的时机练习类加载器启动类加载器扩展类加载器双亲委派模式线程上下文类加载器自定义类加载器破坏双亲委派模型的几种做法类加载机制J... 查看详情

两道面试题,带你透彻解析java类加载机制(代码片段)

在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题:publicclassGrandpa static System.out.println("爷爷在静态代码块"); publicclassFatherextendsGrandpa static System.out.println("爸爸在静态代码 查看详情

深度思考:老生常谈的双亲委派机制,jdbctomcat是怎么反其道而行之的?(代码片段)

要说双亲委派机制,还得从类加载器的类型谈起一、类加载器的类型类加载器有以下种类:启动类加载器(BootstrapClassLoader)扩展类加载器(ExtensionClassLoader)应用类加载器(ApplicationClassLoader)启动类加... 查看详情

jvm的类加载机制(代码片段)

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。 类加载的规则:全盘负责,当一个类加载器负责加载某个Class... 查看详情

虚拟机类加载机制(代码片段)

1.类的生命周期验证、准备、解析统称为连接。加载、验证、准备、初始化、卸载这5个阶段开始执行的顺序固定,但往往是交叉执行,并不会执行完一个再执行下一个。解析某些情况下会在初始化之后,这是为了支持Java的动态... 查看详情

类加载机制(代码片段)

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。Java类型的加载、连接和初始化过程都是在程序运行期间完成的。这种策略虽然会令类加载时稍... 查看详情

深入理解java虚拟机——类加载机制(代码片段)

文章目录类加载机制类的生命周期类的加载过程1、加载2、验证3、准备4、解析5、初始化类的初始化时机类加载器类与类加载器类加载器分类双亲委派模型工作过程源码分析双亲委派机制的好处类加载机制类的生命周期一个类型... 查看详情

jvm类加载机制(代码片段)

文章目录一、类加载时机二、类加载过程2.1加载2.2验证2.3准备2.4解析2.5初始化三、类加载器3.1类与类加载器3.2双亲委派模型四、Tomcat类加载器架构原创不易,未经允许,请勿转载。博客主页:https://xiaojujiang.blog.csdn.net... 查看详情

jvm类加载机制(代码片段)

文章目录一、类加载时机二、类加载过程2.1加载2.2验证2.3准备2.4解析2.5初始化三、类加载器3.1类与类加载器3.2双亲委派模型四、Tomcat类加载器架构原创不易,未经允许,请勿转载。博客主页:https://xiaojujiang.blog.csdn.net... 查看详情

java序列化机制深度解析(代码片段)

概要序列化机制允许将实现序列化的Java对象转换为字节序列,这些字节序列可以被保存在磁盘上或通过网络传输,以备以后重新恢复原来的对象,序列化机制使得对象可以脱离程序的运行而独立存在可序列化的类包... 查看详情

jvm类加载机制详解(代码片段)

如下图所示,JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程。加载加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法... 查看详情