学习JVM需要一定的编程经验和计算机基础知识,适用于从事Java开发、系统架构设计、性能优化、研究学习等领域的专业人士和技术爱好者。
学习本专栏以及本章内容的前提和适用人群如下:
每位Java开发者都了解到Java字节码是在Java运行时环境(JRE)上执行的。JRE包含了最为关键的组成部分:Java虚拟机(JVM),它负责分析和执行Java字节码。通常情况下,大多数Java开发者无需深入了解虚拟机的内部运行原理。即使对虚拟机的运行机制不甚了解,也不会对开发工作产生太多影响。然而,对JVM有一定了解的话,将更有助于深入理解Java语言,并解决一些看似困难的问题。
本专栏全面系统地剖析了特定虚拟机产品(即HotSpot,Oracle官方虚拟机)的实现,本人不仅深刻地讲解了看似深奥的原理,还提供了大量易于上手的实践案例,下面是总体的JVM相关的知识拓扑架构。
tips:当然还有一些最新的JVM特性未在这张图并非展示本专栏的全部内容,另外还包含了最新的JVM特性。
Java虚拟机被称为“虚拟”的原因在于它是由一个规范定义的抽象计算机。因此,要运行Java程序,需要一个符合该规范的具体实现。本文主要描述了这个规范本身,同时也针对某些特性做了更细致的描述,包括它们可能的实现方式。
理解Java虚拟机时,首先需要认识到“Java虚拟机”可能代表以下两种不同的概念:
Java虚拟机能够执行符合规范的Class文件,并不仅限于执行Java程序,也支持其他编程语言,比如Scala、Clojure、Groovy、Fantom、Fortress、Nice、Jpython、JRuby、Rhino、Ioke、Jaskell,甚至还包括一些传统的编程语言如C、Fortran。
JVM实例对应着独立运行的Java程序,而JVM执行引擎实例则对应着用户运行程序的线程;换句话说,JVM实例对应着进程级别,而执行引擎对应着线程级别。
在Java虚拟机规范中,一个虚拟机实例的行为是按照子系统、内存区、数据类型以及指令等术语来描述的。这些组成部分共同展示了抽象的虚拟机内部抽象体系结构。然而,规范中对它们的定义并非是为了强制规定Java虚拟机实现内部的体系结构,而更多地是为了严格地定义这些实现的外部特征。规范本身通过定义这些抽象的组成部分以及它们之间的交互,来界定任何Java虚拟机实现都必须遵守的行为。
每个Java虚拟机都包括类加载器子系统,它根据给定的全限定名来加载类型(类或接口)。同样,每个Java虚拟机也包含执行引擎,它负责执行被加载类中的方法的指令。
类加载子系统的功能是根据给定的全限定名类名(比如:java.lang.Object)将class文件加载到Runtime data area中的method area。开发者可以通过继承java.lang.ClassLoader类来实现自定义的Class loader。
JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:
Bootstrap ClassLoader负责将$JAVA_HOME/jre/lib目录下的所有类库加载到内存中。Bootstrap类加载器属于JVM级别,由C++实现,不是ClassLoader的子类,开发者也无法直接获取其实例。
启动类加载器的引用,所以不允许直接通过引用进行操作。
Extension ClassLoader负责加载Java平台中的一些扩展功能的jar包,由sun.misc.Launcher$ExtClassLoader实现,它是一个Java类,继承自URLClassLoader超类。Extension ClassLoader负责将%JRE_HOME/lib/ext目录下的jar和class加载到内存中,开发者可以直接使用该加载器。
App ClassLoader负责将环境变量classpath中指定的jar包和目录中的class加载到内存中,开发者可以直接使用系统类加载器。
Custom ClassLoader是应用程序根据自身需求定制的ClassLoader(通常是java.lang.ClassLoader的子类)。它可以在程序运行时动态加载class文件,体现了Java的动态实时类加载特性。例如,像tomcat、jboss这样的应用会根据J2EE规范自行实现ClassLoader。
在某些应用场景中,自定义ClassLoader仍然是非常适用的,特别是需要灵活动态加载class的情况下。
JVM包括两个子系统和两个组件:class loader(类加载器)、Execution engine(执行引擎);以及两个组件:Runtime data area(运行时数据区)、Native interface(本地接口)。
执行引擎的功能是执行classes中的指令。任何JVM规范实现(比如JDK)的核心都包括Execution engine,不同的JDK,比如Sun的JDK和IBM的JDK,其性能优劣主要取决于它们各自实现的Execution engine的好坏。
native libraries交互是Java与其他编程语言进行交互的接口。当调用native方法时,程序进入一个全新且不再受虚拟机限制的领域,因此可能出现JVM无法控制的native heap OutOfMemory错误。
运行时数据区是我们常说的JVM的内存,主要分为五个部分:
Heap和Method Area是所有线程共享使用的,而Java Stack、Program Counter和Native Method Stack是以线程为粒度的,每个线程拥有独立的部分。
由于篇幅过长,因此暂时写到这里,后续内容会在后面的文章中继续体现和分析,下一篇文章会针对于JVM体系的细节进行深入分析和探索。