从类加载开始的JVM学习

2022-10-30  本文已影响0人  蓝调_4f2b

目录

引言

JVM(java virtual machine)是决定java语言可以跨平台执行的重要因素。对于不同的操作系统,java语言均实现了一套可供执行程序的JVM环境。在我们的程序开始执行时,我们将Java类文件读取到JVM提供的类加载器中;加载后,在内存中将保存我们程序中使用的java类。


企业微信截图_2bdf7229-2d5e-4460-b0ff-9d218e4237fb.png

java类加载流程

  1. java类加载逻辑流程


    企业微信截图_0e5d6fb0-7d95-42cd-91cc-d2236a4b3021.png
  2. java类加载流程详解
    企业微信截图_c5666a79-253a-4beb-9063-b972fd716546.png
    类加载:类文件从磁盘中读入到内存中
    (1)验证阶段:验证字节码文件中格式是否合法.
    (2)准备阶段:为程序中静态变量(static)赋默认值;一般int类型为0,bool类型为false
    (3)解析阶段:将(静态变量)符号引用替换为直接引用;
    该阶段将静态方法替换为指向数据所存内存的指针或句柄,被称为静态链接过程;
    注:动态链接过程:在运行期间完成将符号引用变为直接引用。
    (4)初始化:最后对类静态变量赋指定值(即赋值语句中的值)
  3. JVM懒加载特性
    思考以下场景
class A {
  static {
    system.out.println("load A");
 }
  public A()  {
  system.out.println("initual A");
}
 public static void main(String[] args) {
 # 思考以下两种初始化方法有何不同
  A a = new A();
  A a = null;
} 
}

new A:
打印"load A"与"initual A"字段
A a = null:
懒加载将不加载A()的方法,仅会在类加载阶段加载类的静态方法

java的双亲委派机制

  1. 双亲委派机制流程:


    企业微信截图_2f8b5ce3-dc44-4294-8357-7ddd16029af5.png

    其中:
    (1)引导类加载器:负责加载支撑了JVM运行的位于JRE目录下的核心类库,例如: rt.jar, charset.jar等
    (2)扩展类加载器:负责加载支持JVM运行的位于JRE lib目录下的ext jar包
    (3)应用类加载器:加载ClassPath下的类包,主要加载开发者自己创建的类

  2. 双亲委派/类加载原理:
    (1)Launcher类:rt.jar下的核心类,负责类文件的加载
    详细:待补充
    (2)类加载入口方法:getClassLoader()
    代码:

public classLoader getClassLoader() {
  return this.loader;(loader通过Launcher()方式获取,该方法默认从AppClassLoader中进行加载,故开发者的类加载过程一般从App类加载器开始)
}

思考:从AppClassLoader开始加载的优点?
通过第一次加载将引导类与扩展类中相关类文件代入AppClass类加载中,后续仅访问AppclassLoader即可实现对所有资源的加载
(3)AppClass类中核心方法loadClass()解析:

// 注:以下为根据理解形成的伪代码
public LoaderClass loadClass() {
  Class<?> c = findLoadClass(name);   // 从Appclass目录下加载类文件
  if (parent != null) {
    c = parent.loadClass(name);      // 在AppClass目录下未找到对应的类文件,尝试从它的父类(Ext)目录下加载类文件
 } else {
    c = findBootstrapClassOrNull(name);  // 尝试通过BootstrapClass目录下获取类文件 
}
 
  if (c == null) {
  c = findClass(name);    // 都找不到,最后回到AppClass中的findClass方法
}
 return c;
}

(4)双亲委派机制设计思想:
(4.1)沙箱安全机制:防止开发者用自己写的核心类(或其他)篡改核心库Api,例如:


企业微信截图_55fec45b-7a4e-499c-bb81-18021c221326.png

(4.2)避免类的重复加载

  1. 尝试自定义类加载器:
    思路:实现自定类加载器需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是 loadClass(String, boolean),实现了双亲委派机制,还有一个方法是findClass,默认实现是空方法,所以我们自定义类加载器主要是重写这个方法。
import java.io.FileInputStream;
import java.lang.reflect.Method;

public class MyClassLoaderTest {
    static class MyClassLoader extends ClassLoader {
        private String classPath;

        public 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;
        }

        protected 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("User");
            Object obj = clazz.newInstance();
            Method method = clazz.getDeclaredMethod("sout", null);
            method.invoke(obj, null);
            System.out.println(clazz.getClassLoader().getClass().getName());
        }
}
  1. 尝试打破双亲委派机制
    思路:重写 java.lang.ClassLoader 类中loadClass方法,实现以下功能:当类加载机制从AppClassLoader开始时,不向上层的Loader类进行委托。
protected Class<?> loadClass(String name, boolean resolve)
 throws ClassNotFoundException {
   synchronized (getClassLoadingLock(name)) {
   Class<?> c = findLoadedClass(name);

   // 当c=null时,不调用parent.loadClass(name)方法
   if(c==null){
   long t1 = System.nanoTime();
   c = findClass(name);
   sun.misc.PerfCounter.getFindClassTime().
    addElapsedTimeFrom(t1);
   sun.misc.PerfCounter.getFindClasses().increment();
}
    if (resolve) {
    resolveClass(c);
    }
    return c;
    }
  }
}

Tomcat中双亲委派机制的应用

  1. 双亲委派机制给Tomcat带来的问题:
    待补充
  2. Tomcat8中的实现:


    企业微信截图_71e31b91-8772-495e-ac2b-3a0fac506ca4.png
上一篇 下一篇

猜你喜欢

热点阅读