美文网首页
从类加载开始的JVM学习

从类加载开始的JVM学习

作者: 蓝调_4f2b | 来源:发表于2022-10-30 01:35 被阅读0次

目录

  • 引言
  • java类加载流程
  • java类加载机制
    - 类加载原理
    - 双亲委派机制
  • Tomcat中双亲委派机制的应用

引言

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

相关文章

  • JVM类加载机制

    这节主要从覆盖JDK的类开始学习JVM的类加载机制。Java和JVM的类加载机制类似,但JVM的类加过程稍有些复杂...

  • 从类加载开始的JVM学习

    目录 引言 java类加载流程 java类加载机制- 类加载原理- 双亲委派机制 Tomcat中双亲委派机制的应用...

  • java类加载机制、类加载器、自定义类加载器

    java类加载机制、类加载器、自定义类加载器 类加载机制 java类从被加载到JVM到卸载出JVM,整个生命周期包...

  • 类加载机制

    JVM类加载机制 类加载子系统 类加载过程 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:...

  • JVM——类加载机制

    前言 今天就来介绍类的加载机制以及双亲委派机制。 JVM 类加载机制 JVM 类加载的五个阶段 JVM 类加载机制...

  • JVM类加载学习二-类加载机制学习

    JVM类加载机制 @(Java)[JVM|类文件结构] [TOC] 基本介绍 JVM的类加载机制:JVM把描述类的...

  • 每天一个知识点(3)--JVM-类加载子系统-类加载器

    JVM的类加载子系统负责加载相关的类,加载类是JVM中类加载器的功能和职责。首先明确下JVM中的类加载器,分为以下...

  • 面试官:java类的加载过程

    Java 类加载机制 类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用...

  • (夜食)Java类的加载过程

    Java类的加载过程 类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用...

  • JVM类加载学习三-类加载器

    JVM类加载器 @(Java)[JVM|类加载器] 类加载过程中的加载阶段在JVM的外部实现。这样做可以让应用程序...

网友评论

      本文标题:从类加载开始的JVM学习

      本文链接:https://www.haomeiwen.com/subject/yxwftdtx.html