JVM调优系列课程--java类加载机制

本文围绕Java类加载展开,介绍了类加载过程,包括加载、验证、准备、解析和初始化;阐述了类加载器的分类,如引导、扩展、应用程序和自定义类加载器;讲解了双亲委派和全盘负责委托机制及其好处;还说明了自定义类加载器实现、打破双亲委派机制的方法,以及Tomcat加载不同版本jar包的原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 类加载图解

                 

2 . 类的加载过程(加载,验证,准备,解析,初始化)

    1 .  加载,将磁盘上的字节码文件load到内存,生成该类的class对象,作为访问该类方法区的入口,触发类的加载常见指令包括getStatic,putStatic,new对象

    2 . 验证,验证字节码文件的正确性(比如java类的字节码文件都是cafe babe开头)

    3 . 准备,为静态变量赋默认值,基本数据类型默认值,对象类型默认值为null

    4 . 解析, 又称静态链接,将静态方法的符号应用替换为直接引用,以为此时静态方法已经加载了,普通方法并没有加载,静态方法的符号引用替换为静态方法所在的内存地址,普通方法的符号引用是在程序运行时动态替换的,此过程成为动态链接

    5 . 初始化,对类的静态变量赋值,执行静态代码块,执行构造方法

 

3 . 类加载器

   1 .  类加载器一般分为引导类加载器,扩展类加载器,引用程序类加载器,自定义类加载器

   2 . 引导类加载器(BootstrapClassloader)

     底层由c++实现的,所有类加载器的父类加载器,负责加载jre核心类库的jar,jre/lib目录下的jar包

   3 . 扩展类加载器(ExtClassloader)

    java语言实现,继承自URLClassloader,AppClassloader的父类加载器,负责加载jre/lib/ext下的jar包

   4 . 应用程序类加载器(AppClassloader)

   java语言实现,继承自URLClassloader,自定义类加载器的父类加载器,加载classPath下的jar包 

   5 . 自定义类加载器

   用户自定义实现,继承Classloader类,重写loadclass方法,加载用户自定义类路径下的jar

    6 . 类加载器是什么,它是由谁创建的?

     BootstrapClassloader,ExtClassloader,AppClassloader统称为系统类加载器,是由sum.misc.Launcher创建的,在Launcher的构造方法中会创建ExtClassloader和AppClassloader,而BootstrapClassloader是c++实现,在java代码中并没有其创建过程

    自定义类加载器是由用户创建

    Launcher是单例的,保证程序中只有会创建一次并只有一个实例

类加载器关系图

      

4 . 双亲委派机制

    1 .  每种类加载器都有自己的职责加载自己该加载的类

    2 . 当程序需要用到某个类时,先在自己负责加载的路径下是否有该类,如果有,去内存中找该类,如果没有,向上委托。父类加载器过程相同,一直到BootstrapClassloader,如果没有,BootstrapClassloader查找是否是自己加载的,如果是,加载该类并返回,如果不是,下放

    3 . 双亲委派机制的好处

     沙箱安全,保证jdk内部的类不会被修改,即使被修改,也加载不进来

    避免重复加载,保证类的唯一性

5 . 全盘负责委托机制

    当一个Classloader加载一个类时,该类所引用的类也由该Classloader加载,除非指定用其他类加载器加载

6 . 自定义类加载器实现

    继承ClassLoader类,重写findClass方法,自定义classPath路径,加载指定的类

package com.tuling.jvm;

import java.io.FileInputStream;
import java.lang.reflect.Method;

public class MyClassLoaderTest {

    static class MyClassLoader extends ClassLoader{

        private String classPath;

        MyClassLoader(String classPath){
            this.classPath = classPath;
        }

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

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

    public static void main(String[] args) throws Exception {
        MyClassLoader classLoader = new MyClassLoader("D:/test");
        Class clazz = classLoader.loadClass("com.tuling.jvm.User1");
        Object obj = clazz.newInstance();
        Method method = clazz.getDeclaredMethod("sout", null);
        method.invoke(obj, null);
        System.out.println(clazz.getClassLoader().getClass().getName());
    }
}

7 . 打破双亲委派机制(加载不同路径下相同全限定名的类)

     再创建一个classLoader,再加载一次

public static void main(String[] args) throws Exception {
        MyClassLoader classLoader = new MyClassLoader("D:test");
        final Class<?> clazz = classLoader.loadClass("com.tuling.jvm.User1");
        System.out.println(clazz.getClassLoader().getClass().getName());
        final Method sout = clazz.getDeclaredMethod("sout");
        final Object obj = clazz.newInstance();
        sout.invoke(obj, null);

        System.out.println("-------------------");
        MyClassLoader classLoader1 = new MyClassLoader("D:test1");
        final Class<?> clazz1 = classLoader1.loadClass("com.tuling.jvm.User1");
        System.out.println(clazz1.getClassLoader().getClass().getName());
        final Method sout1 = clazz1.getDeclaredMethod("sout");
        final Object obj1 = clazz1.newInstance();
        sout1.invoke(obj1, null);
    }

8 . tomcat是怎么实现多个项目依赖不同版本jar包

  在tomcat中,可以同时跑两个war包,两个war包可能分别依赖于apring4和spring5,而spring4和spring5中存在很多同名的包,用上面的方法就可以实现加载同名的包

附tomcat的类加载器图例

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值