JVM的类加载原理机制

参考:
https://www.cnblogs.com/Qian123/p/5707562.html
https://blog.csdn.net/xiao__jia__jia/article/details/81044621

JVM简介

JVM 全称是Java Virtual Machine ,Java 虚拟机。JVM 的命令集则是可以到处运行的,因为JVM 做了翻译,根据不同的CPU ,翻译成不同的机器语言。
JVM是一个在内存中生成的虚拟机,它的存储全部都在内存中,也就是我们写的所有类、常量、变量、方法都在内存中,这决定着我们程序运行的是否健壮、是否高效。

JVM组成部分

JVM结构
从图中可以看出,JVM是运行在操作系统之上的,没有与硬件的直接互通。下图显示的是JVM的构成图。
JVM构成
可以看出JVM主要由以下四部分构成

class loader 类加载器

类加载器的作用是将class文件加载到内存中。

execution engine 执行引擎

执行引擎也叫做解释器,负责将命令解释给操作系统执行。

native interface 本地接口

本地接口是为了融合不同的编程语言为Java所用,目前使用的比较少。

runtime data area 运行数据区

运行数据区是整个JVM 的重点。我们所有写的程序都被加载到这里,之后才开始运行。

JVM类加载器的原理机制

Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显式的加载所需要的类。
类装载方式有两种:

  • 隐式装载:程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中;
  • 显示装载:通过class.forname()等方法,显式加载需要的类
    Java的类加载是动态的,它并不会一次性地将所有类全部加载后在运行,而是保证程序运行的基础类(像基类)完全加载到jvm中,置于其他类,则在需要的时候才加载。这就节省了内存开销。
    Java的类加载器有三个,对应Java的三种类
    1
    2
    3
    4
    5
    Bootstrap Loader //负责加载系统类。指内置类如String
    |
    -- ExtClassLoader //负责加载扩展类。指继承类和实现类
    |
    -- AppClassLoader //负责加载应用类,程序员自定义的类
    类加载器的步骤如下:
    (1)装载:查找和导入class文件。
    (2)连接:检查载入的class文件数据的正确性;为类的静态变量分配存储空间;将符合引用转换为直接引用。(也就是验证准备解析三个步骤)
    (3)初始化:初始化静态变量和静态代码块。
    (4)使用
    (5)卸载
    这样的过程会在程序调用类的静态成员的时候开始执行,所以静态方法main()才会成为一般程序的入口方法。类的构造器也会引发该动作。

关注连接步骤的准备阶段

  1. 准备阶段的目的:正式为类变量分配内存并设置类变量初始值的阶段。这些变量所使用的内存将在方法区中分配。注意这里是类变量(static)而不是实例变量。
  2. 这里的类变量初始值通常是指数据类型的零值。比如int=0, boolean=false。真正的初始化赋值是在初始化阶段进行的。当然如果你将类变量设置为final,那么准备阶段变量的初始值就会直接变为你设定的那个初始值。

关注连接步骤的解析阶段

  1. 解析的目的:虚拟机将常量池内的符号引用替换为直接引用。因为在准备阶段为类变量分配了内存,将直接引用分配的内存地址。
  2. 解析阶段的特点:发生时间不可预料,有可能和初始化阶段相互交换位置。
  3. 什么是常量池?常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。他包括了关于类、方法、接口等中的常量static,也包括字符串常量。解析阶段的常量池就是存在于.class文件中的常量池,结果在运行期被JVM装载,并且可以扩充的存在于方法区中的运行时常量池。
  4. 解析中的动态链接:这是解析阶段发生时间不确定的直接原因:大部分JVM的实现都是延迟加载或者叫动态链接。他的意思就是JVM装载某个类A时,如果类A中有引用其他类B,虚拟机并不会将这个类B也同时装载进JVM内存,而是等到执行的时候采取装载。而这个被引用的B类在引用他的类A中的表现形式主要被登记在了符号表中,而解析的过程就是当需要用到被引用类B的时候,将引用类B在引用类A的符号尹永明改为内存中的直接引用。这就是解析发生时间不确定的原因,而且这个阶段是发生在方法区的。