• 前言
    作为一位java开发人员,不懂java虚拟机规范是会被人鄙视的,特别是工作了多年的程序人员。为了不让他人有鄙视的机会,于是本人开始了JVM的学习之旅。
    学习Java虚拟机,从了解JVM内存模型开始。
  • JVM【java虚拟机规范】内存模型图


    1. Class files
    一般而言,我们都会将逻辑代码编写在以.java为后缀的文件中。class文件则是由java文件编译【使用javac命令编译】而成。

2. 类加载器子系统

class文件已经有了,那么class文件又是如何加载进虚拟机的呢?这时不得不提一下类加载器。类加载器,顾名思义,就是实现对类的加载-类加载器的主要任务就是将class文件加载进虚拟机内存。 从JAVA虚拟机的角度来看,类加载器可分为两种,一种是启动类加载器,用于加载虚拟机运行时所需类库。另一种是其他类加载器,用于加载非启动类库的其他所有类库。 从java开发人员的角度来看,类加载器还可以做更细致的划分,可分为:启用类加载器、扩展类加载器、应用程序类加载器及自动类加载器。如下图:

  • 启用类加载器(Bootstrap)C++
    负责将存在于\lib目录中的rt.jar的类库加载进虚拟机内存中
  • 扩展类加载器(Extension)Java
    负责将存在于\lib\ext目录中的所有类库加载进虚拟机内存中
  • 应用程序类加载器(AppClassLoader)java
    负责将用户类路径(ClassPath)上所指定的类库加载进虚拟机内存中
  • 自定义类加载器
    如开发人员对类的加载有特殊的要求,可自定义类加载器
    该类加载器子系统图展示了类加载器的种类,同时描述了类加载器之间的层次关系,我们称这种层次关系为双亲委派模型。
  • 双亲委派模型
    当类加载器收到类的加载请求时,首先自身不尝试去加载这个类,而是将这个类请求委派给父类加载器去完成。每个类加载 器都如此。因此所有的类加载请求都会传递到顶级类加载器【启动类加载器】,只有当父类加载器无法完成类加载请求时,子类加载器才会尝试自己去加载。
    如当类加载器加载java.lang.String类时。首先应用程序类加载器会接收到java.lang.String类的加载请求,因为应用序类加载器有父类加载器,所以应用程序类加载器不会尝试去加载java.lang.String类,而是将加载java.lang.String类的请求传递给父类加载器【扩展类加载器】。又因为扩展类加载器也有父类加载器【启用类加载器】,所以扩展类加载器也不去加载java.lang.String类,而是将加载java.lang.String类的请求传递给父类加载器【启动类加载器】。启动类加载器是顶级加载器,无父类加载器。它将尝试去加载java.lang.String类,刚好在rt.jar中找到了java.lang.String类,于是将该类加载进虚拟机。如果要加载的类并不在rt.jar里时。启动类加载器无法处理类请求,则会将类的加载请求下放子类加载器去处理。 如下图:

    类加载器内部是如何实现类加载的?这时候就要介绍一下类的加载过程。 类的加载时机分5步:加载、连接【验证、准备、解析】、初始化、使用、与卸载。其中加载、验证、准备、解析、初始化为类的加载全过程。如下图:

    • 加载
      • 通过一个类的全限定名获取定义该类的二进制字节流并在虚拟机中分配相应的内存
    • 连接【验证、准备、解析】
      • 验证:验证加载的类信息是否符合JVM标准,以确保虚拟机的安全
      • 准备:为类变量分配内存,并设置初始值
      • 解析:将虚拟机常量池中的符号引用转化为直接引用
    • 初始化
      • 执行类构造器