31 道 Java 核心面试题,统统打包给你( 二 )


10、什么是 JVM?
JVM(Java Virtual machine)俗称 Java 虚拟机 。之所以称为虚拟机 , 是因为它实际上并不存在 。它提供了一种运行环境 , 可供Java 字节码在上面运行 。
JVM 提供了以下操作:

  • 加载字节码
  • 验证字节码
  • 执行字节码
  • 提供运行时环境
JVM 定义了以下内容:
  • 存储区
  • 类文件格式
  • 寄存器组
  • 垃圾回收堆
  • 致命错误报告等
我们来尝试理解一下 JVM 的内部结构 , 它包含了类加载器(Class Loader)、运行时数据区(Runtime Data Areas)和执行引擎(Excution Engine) 。
31 道 Java 核心面试题,统统打包给你

文章插图
1)类加载器
类加载器是 JVM 的一个子系统 , 用于加载类文件 。每当我们运行一个 Java 程序 , 它都会由类加载器首先加载 。Java 中有三个内置的类加载器:
  • 启动类加载器(Bootstrap Class-Loader) , 加载 jre/lib 包下面的 jar 文件 , 比如说常见的 rt.jar(包含了 Java 标准库下的所有类文件 , 比如说 java.lang 包下的类 , java.net 包下的类 , java.util 包下的类 , java.io 包下的类 , java.sql 包下的类) 。
  • 扩展类加载器(Extension or Ext Class-Loader) , 加载 jre/lib/ext 包下面的 jar 文件 。
  • 应用类加载器(Application or App Clas-Loader) , 根据程序的类路径(classpath)来加载 Java 类 。
一般来说 , Java 程序员并不需要直接同类加载器进行交互 。JVM 默认的行为就已经足够满足大多数情况的需求了 。不过 , 如果遇到了需要和类加载器进行交互的情况 , 而对类加载器的机制又不是很了解的话 , 就不得不花大量的时间去调试
ClassNotFoundException 和 NoClassDefFoundError 等异常 。
对于任意一个类 , 都需要由它的类加载器和这个类本身一同确定其在 JVM 中的唯一性 。也就是说 , 如果两个类的加载器不同 , 即使两个类来源于同一个字节码文件 , 那这两个类就必定不相等(比如两个类的 Class 对象不 equals) 。
是不是有点晕 , 来来来 , 通过一段简单的代码了解下 。
 public class Test {
public static void main(String[] args) {
【31 道 Java 核心面试题,统统打包给你】ClassLoader loader = Test.class.getClassLoader;
while (loader != ) {
System.out.println(loader.toString);
loader = loader.getParent;
}
}
}
每个Java 类都维护着一个指向定义它的类加载器的引用 , 通过 类名.class.getClassLoader 可以获取到此引用;然后通过 loader.getParent 可以获取类加载器的上层类加载器 。
上面这段代码的输出结果如下:
 sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@4617c264
第一行输出为 Test 的类加载器 , 即应用类加载器 , 它是 sun.misc.Launcher$AppClassLoader 类的实例;第二行输出为扩展类加载器 , 是 sun.misc.Launcher$ExtClassLoader 类的实例 。那启动类加载器呢?
按理说 , 扩展类加载器的上层类加载器是启动类加载器 , 但在我这个版本的 JDK 中 ,  扩展类加载器的 getParent 返回。所以没有输出 。
2)运行时数据区
运行时数据区又包含以下内容:
31 道 Java 核心面试题,统统打包给你

文章插图