【架构师面试-JVM原理-2】-JVM加载类过程

1:Java程序运行时整体架构

【架构师面试-JVM原理-2】-JVM加载类过程_第1张图片

 

Java通过latcher程序启动JVM,通过类加载器加载类,先加载类的元数据信息,包括类编译好的方法指令,这些信息先放在Method Area。

Method area中的数据就是运行时的数据(Runtime Data),而这些数据有一部分是指令,这些指令需要运行必须依靠线程。线程执行就需要stack和ProgramCounter,这两部分配合起来执行程序就需要使用heap(对象初始化需要heap),如果调用了JNI(java native interface)还需要Native memory

执行java的bytecode需要执行引擎,它包括JIT编译器(just in time)是一边编译一边执行,也就是一边将指令编译成机器码,一边执行机器码。编译过的程序不再编译第二次,也就是一次编译,到处运行。

GC是垃圾会收器和java内存分配的工具,即java堆的实际管理者。

本地库接口:任何语言都不可避免的要调用本地接口,比如Java的线程只是一个Object,它是没有办法执行程序的。只有操作系统可以执行,可以调用CPU,包括GC的内存分配也需要向操作系统要内存空间。本地库接口会调用本地方法来实现。

2:JVM加载类过程

【架构师面试-JVM原理-2】-JVM加载类过程_第2张图片

 

在Java中,一个java类将会编译成一个class文件。在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如org.simple.People类引用了org.simple.Language类,在编译时People类并不知道Language类的实际内存地址,因此只能使用符号org.simple.Language表示Language类的地址。

loadClass与forName区别

Classloader.loadClass得到的class是还没有链接的。

Class.forName得到的class是已经初始化完成的

3:ClassLoader的双亲委派机制

双亲名起的不准确,其实是父亲,父亲的父亲。

ClassLoader类型:

BootStrapClassLoader: 加载核心库java.*,由c++编写

ExtClassLoader:加载扩展库javax.*,由Java编写(jre/lib/ext)

AppClassLoader:加载程序所在目录,由Java编写(classpath)

自定义ClassLoader:定制化加载,由Java编写

【架构师面试-JVM原理-2】-JVM加载类过程_第3张图片

优点:避免多个同样字节码对象(Class对象)的加载,比如以前A已经加载过一个类,会有一个该类的Class对象,B在使用时就不需要再new一个该类的Class对象,而是从当前的ClassLoader逐层向父类ClassLoader去查找该Class对象,如果有ClassLoader已经加载过该Class对象则不需要再创建该类的Class对象。

4:什么是ClassLoader

ClassLoader主要工作在Class装载的加载阶段,主要作用是从系统外部获得Class二进制数据流。

所有的Class都是由ClassLoader加载的,它负责将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。

从ClassLoader源代码可知道,该类是一个抽象类。

Parent也是ClassLoader类型,说明ClassLoader是有多种类型的。

【架构师面试-JVM原理-2】-JVM加载类过程_第4张图片

有一个重要方法是loadClass(String name),name是类名,这个方法返回一个Class类型的对象,如果没有找到则抛出ClassNotFoundException异常。

【架构师面试-JVM原理-2】-JVM加载类过程_第5张图片

自定义类加载器

openclass.classloader/Tom.java

package openclass.classloader;
public class Tom {
    static {
        //static在初始化时执行
        System.out.println("hello tom");
    }
}

openclass.classloader /MyClassLoader.java

package openclass.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
 
public class MyClassLoader extends ClassLoader {
    private String path;
    private String classLoaderName;
 
    public MyClassLoader(String path, String classLoaderName) {
        this.path = path;
        this.classLoaderName = classLoaderName;
    }
    //用于寻找类文件
    @Override
    public Class findClass(String name) {
        byte[] b = loadClassData(name);
        return defineClass(name, b, 0, b.length);
    }
    //用于加载类文件
    private byte[] loadClassData(String name) {
        name = path + name + ".class";
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            //读取文件(tom.class)
            in = new FileInputStream(new File(name));
            out = new ByteArrayOutputStream();
            int i = 0;
            while ((i = in.read()) != -1) {
                out.write(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return out.toByteArray();
    }
}

openclass.classloader/ClassLoaderChecker.java

package openclass.classloader;
 
public class ClassLoaderChecker {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        //第一个参数就class文件的路径,第二个是自定义类加载器的名字,可以随意取。
        MyClassLoader m = new MyClassLoader("D:/workspace/jvmquestion/openclass/classloader/", "myClassLoader");
        Class c = m.loadClass("openclass.classloader.Tom");
        c.newInstance();
    }
}

5:如何打破双亲委派模型

打破双亲委派机制的场景有很多:JDBC、JNDI、Tomcat等

因为双亲委派模型是JDK1.2之后才被引入,而类加载器和抽象类java.lang.ClassLoader则在JDK1.0时就已经存在,所以在面对已存在的用户自定义类加载器的实现代码时,Java设计者引入双亲委派模型时不得不做出一些妥协。在此之前,用户去继承java.lang.ClassLoader的唯一目的就是为了重写loadClass()方法,这是源于虚拟机进行类加载的时候会调用加载器的私有方法loadClassInternal(),而这个方法的唯一逻辑就是去调用自己的loadClass()。

如果想自定义类加载器,就需要继承ClassLoader,并重写findClass,如果想不遵循双亲委派的类加载顺序,还需要重写loadClass。

你可能感兴趣的:(架构师面试,java,开发语言,后端,架构师)