java架构之路-(JVM优化与原理)JVM类的加载机制

    话不多说,先上图。

 

 

 ***.class文件执行大概就是这样来走的。我们都知道我们的java文件经过编译以后会生成对应的class文件。先经过类装载子系统,然后塞进运行时内存模型的元空间,开始执行方法,对象放在堆,线程开辟栈空间,程序计数器控制执行顺序。字节码执行引擎整体调控程序计数器,走你。。。大概就是这样的。我们先来看一下类装载子系统是如何工作的。

  类装载子系统大概分为,验证->准备->解析->初始化。笼统的来说就这个4个步骤。

1,验证:验证我们的编译文件(字节码文件)是否正确。

2,准备:给予类的静态常量开辟堆空间。并且赋予默认值。对象也在这个时候放置在堆空间,并且给予空值。

3,解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用。就像是我们把main转化为001,将()转化为002,将这一系列的编码存在堆上。

4,初始化,将第3步的静态常量(或对象)赋值,执行静态代码块。

类的加载器大致分为,启动类加载器,扩展类加载器,应用类加载器和自定义加载器,后面我们会说如何实现自己的类加载器。

启动类加载器是用来加载java自身的lib包的。用C语言实现的,我们是看不到的。

扩展类加载器顾名思义,是加载java的扩展包的。加载ext包下的jar包

然后就是我们的应用加载器,来执行我们一行行代码的。

最后才是我们的自定义加载器。我来看一段代码。

import com.sun.crypto.provider.DESKeyFactory;

public class Main {

    public static void main(String[] args) {
        System.out.println(String.class.getClassLoader());
        System.out.println(DESKeyFactory.class.getClassLoader().getClass().getName());
        System.out.println(Main.class.getClassLoader().getClass().getName());
    }
}

输入如下:

null
sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$AppClassLoader

Process finished with exit code 0

三种加载器就是这样的。

 双亲委派机制:

  我们知道一个基础的知识,就是我们新建的java.lang.String是无法加载的,就是加载过程的双亲委派机制限制了我们自定义重写java本来的代码。

双亲委派是为了阻止我们重写java内部的类,做到了沙箱安全的目的。

  大概就是这样来实行的。上图:

自定义加载器会优先拿到要加载的文件,但是他不会去马上加载。而是直接交给应用类加载器,应用类加载器也不会管,继续向上走,交给扩展类加载器,他也不管,继续向上走,交给启动类加载器,没办法了,启动类加载器没法继续向上交付了。自己先试试可以加载吗?可以加载就加载,加载不了退返给扩展类加载器,扩展类看到是推回来的,试试吧。可以加载吗?可以加载就加载,加载不了退返给应用类加载器,应用类加载器可以加载就加载,加载不了退返给自定义加载器。这样一个由下到上,谁也不管。逐个去尝试往下推的方法去加载。好久就是为了防止你重写java内部的类。

  这里简单说一下自定义加载器。

我们只要重写com.sun.org.apache.bcel.internal.util;包下的ClassLoader类的findClass方法,最后调用defineClass方法。就可以实现我们的自定义加载器。

  这回我们再回过头来看上一篇博客的tomcat打破双亲委派机制也就懂得是怎么回事了吧。不懂的评论留言吧。就说到这里,我们下一次说一下jvm运行时内存模型那一块。

 写的这么不好的文章能坚持读到最下面确实挺不易的,贡献一个小技巧,来看类是否真的被加载了。

最进弄了一个公众号,小菜技术,欢迎大家的加入

 

posted @ 2019-09-10 20:47  小菜技术  阅读(604)  评论(0编辑  收藏  举报