简单理解 JVM

in Java 进击 with 0 comment

JVM 架构图

概况来说,主要包含两个子系统和两个组件:Classloader(类加载器)子系统,Execution engine(执行引擎)子系统;Runtime data area(运行时数据区)组件,Native interface(本地方法接口)组件。

1.Classloader 子系统的作用:
根据给定的全限定名类名来装载 class 文件的内容到 runtime data area 中的 method area(运行时数据区)。开发人员可以通过继承 java.lang.Classloader 类来自定义类加载器。

2.Execution engine 子系统的作用:
执行引擎子系统,执行 classes 中的指令。任何 JVM specification 实现(JDK)的核心是 Execution engine。每一个运行中的线程都有一个 Execution engine 的实例。

3.Native Interface 组件:
本地方法接口组件与 native libraries 交互,是其他编程语言交互的接口。

4.Runtime data area 组件:
运行时数据区,就是 JVM 的内存。主要包括五个部分:
Heap(堆)、Method Area(方法区)、Java Stack(Java 栈)、Program Counter(程序计数器),Native method stack(本地方法接口)。
其中,Heap 和 Method area 是被所有线程共享使用;而 Java stack,Program counter,native method stack 是以线程为粒度的,每一个线程都有一份。
(1)Heap
Java 程序在运行时创建的所有类或数组都放在同一个堆中。而一个 java 虚拟实例中只存在一个堆空间,因此所有的线程都共享这个堆。每一个 java 程序独占一个 JVM 实例,因而每一个 java 程序都有自己的堆空间,它们不会彼此干扰。但是同一个 java 程序的多个线程都共享一个堆空间,就得考虑多线程同步访问的问题。(这里可能出现的 OOM 异常:java.lang.OutOfMemoryError:Java heap space)

(2)Method Area
在 JVM 中,被装载的 class 的信息存储在 Method Erea 方法区的内存中。当虚拟机装载某个类时,先用类加载定位相应的 class 文件,然后读入这些 class 文件并把这些文件的二进制流传输到 JVM 中。然后 JVM 提取其中的类型信息,并将这些信息存储到方法区。该类中的类(静态)变量同样也存储在方法区中。
和 Heap 一样,Method Erea 也是多线程共享的,因此要考虑多线程访问同步的问题。(该区可能会出现的 OOM 异常有:java.lang.OutOfMemoryError:PermGen full)

(3)Java Stack
Java 栈以帧为单位保持线程的运行状态。JVM 只会对 Java Stack 执行两种操作:以帧为单位的压栈或出栈。每当线程调用一个方法的时候,就对当前状态做一个帧保存到 java stack 中(压栈);当一个方法返回调用时,从 java.stack 弹出一个帧(出栈)。帧的大小有一定的限制,所以有可能出现 StackOverFlow 的问题。

(4)Program counter
在一个运行中的 Java 程序中,每一个线程都有自己的 PC 寄存器,也是该线程启动时创建的。PC 寄存器的内容总是指向下一条将被执行的 “地址”,这里的“地址” 可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。

(5)Native method stack
在一个运行的 Java 程序中,可能还会用到一些根本地方法相关的数据区。当某个线程调用一个本地方法时,它就进入了一个全新的不受 JVM 控制的世界。本地方法可以通过本地方法接口来访问 JVM 的运行时数据区,同时还可以做 n 多事情,本地方法具有和 JVM 相同的能力和权限。(这里可能出现 JVM 无法控制的内存溢出 OOM 问题:native heap OutOfMemory)