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