在正式讲解类加载子系统之前我们首先讲解JVM的启动过程。
当你启动java虚拟机时,你通常会使用这样的命令:
java Main.class
当你敲下这行命令时,电脑就开始运行{JRE_HOME}/bin/java.exe程序,java.exe 程序将完成以下步骤:
- 根据JVM内存配置要求,为JVM申请特定大小的内存空间;
- 创建一个引导类加载器实例BootstrapClassLoader,初步加载系统类到内存方法区区域中;
- 创建JVM 启动器实例 Launcher,并取得类加载器ClassLoader,是AppClassLoader实例;
- 使用上述获取的ClassLoader实例加载我们定义的Main类;
- 加载完成时候JVM会执行Main类的main方法入口,执行Main类的main方法;
- 结束,java程序运行结束,JVM销毁。
STEP1.根据JVM内存配置要求,为JVM申请特定大小的内存空间
JVM内存按照功能上的划分,可以粗略地划分为方法区(Method Area) 和堆(Heap),那么JVM就需要未方法区和堆申请内存空间。
STEP2.创建一个引导类加载器实例BootstrapClassLoader,初步加载系统类到内存方法区区域中;
JVM申请好内存空间后,JVM会创建一个引导类加载器(Bootstrap Classloader)实例,引导类加载器(Bootstrap Classloader)是使用C++语言实现的,负责加载JVM虚拟机运行时所需的基本系统级别的类,如java.lang.Object、java.lang.String等等。
因为是使用C++实现,所以如何加载该类并不在我们的讨论范围之内。
引导类加载器(Bootstrap Classloader)会读取 {JRE_HOME}/lib下的jar包和配置,然后将这些系统类加载到方法区内。(具体如何加载一个类就是下一章需要讲解的内容,详情参看:3 类装载子系统_tinpo_123的博客-CSDN博客_类装载子系统)
STEP3.创建JVM 启动器实例 Launcher,并取得类加载器AppClassLoader
上述步骤完成,JVM基本运行环境就准备就绪了。接着,要让JVM工作起来了。JVM虚拟机调用已经加载在方法区的类sun.misc.Launcher 的静态方法getLauncher(), 获取sun.misc.Launcher 实例。
sun.misc.Launcher launcher = sun.misc.Launcher.getLauncher(); //获取Java启动器
ClassLoader classLoader = launcher.getClassLoader(); //获取类加载器ClassLoader用来加载class到内存来
在Launcher的内部,其定义了两个类加载器(ClassLoader),分别是sun.misc.Launcher.ExtClassLoader(扩展类加载器)
和sun.misc.Launcher.AppClassLoader(应用类加载器)
launcher.getClassLoader()
方法将会返回 AppClassLoader 实例,AppClassLoader将ExtClassLoader作为自己的父加载器。
注意:这里的父加载器不是一种继承关系,而是我们之后要讲的双亲委派机制。
STEP4. 使用类加载器ClassLoader加载Main类
ClassLoader classloader = launcher.getClassLoader();//取得AppClassLoader类
classLoader.loadClass("Main");//加载自定义类
上述定义的Main类被编译成Main class二进制文件,这个class文件中有一个叫常量池(Constant Pool)的结构体来存储该class的常亮信息。常量池中有CONSTANT_CLASS_INFO类型的常量,表示该class中声明了要用到那些类。详情参看2 java class文件解析_tinpo_123的博客-CSDN博客
Main类要想正常工作,首先要能够保证这些其内部声明的类加载成功。所以AppClassLoader会再次利用双亲委派机制,加载这些内部声明的类。
STEP5. 使用Main类的main方法作为程序入口运行程序
STEP6. 方法执行完毕,JVM销毁,释放内存
当所有的非守护线程结束,那么JVM的声明就走到了尽头,JVM销毁,释放内存。