话很少说,先上图。java
***.class文件执行大概就是这样来走的。咱们都知道咱们的java文件通过编译之后会生成对应的class文件。先通过类装载子系统,而后塞进运行时内存模型的元空间,开始执行方法,对象放在堆,线程开辟栈空间,程序计数器控制执行顺序。字节码执行引擎总体调控程序计数器,走你。。。大概就是这样的。咱们先来看一下类装载子系统是如何工做的。apache
类装载子系统大概分为,验证->准备->解析->初始化。笼统的来讲就这个4个步骤。tomcat
1,验证:验证咱们的编译文件(字节码文件)是否正确。安全
2,准备:给予类的静态常量开辟堆空间。而且赋予默认值。对象也在这个时候放置在堆空间,而且给予空值。jvm
3,解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,好比main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态连接过程(类加载期间完成),动态连接是在程序运行期间完成的将符号引用替换为直接引用。就像是咱们把main转化为001,将()转化为002,将这一系列的编码存在堆上。ide
4,初始化,将第3步的静态常量(或对象)赋值,执行静态代码块。编码
类的加载器大体分为,启动类加载器,扩展类加载器,应用类加载器和自定义加载器,后面咱们会说如何实现本身的类加载器。spa
启动类加载器是用来加载java自身的lib包的。用C语言实现的,咱们是看不到的。线程
扩展类加载器顾名思义,是加载java的扩展包的。加载ext包下的jar包3d
而后就是咱们的应用加载器,来执行咱们一行行代码的。
最后才是咱们的自定义加载器。我来看一段代码。
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运行时内存模型那一块。
写的这么很差的文章能坚持读到最下面确实挺不易的,贡献一个小技巧,来看类是否真的被加载了。
最进弄了一个公众号,小菜技术,欢迎你们的加入