一、JVM
1. JVM的作用
Java
代码编译成java
字节码后,运行在JVM
中,只要针对不同的系统都开发JVM
后,java
就实现了跨平台。
2. JVM、JRE、JDK的关系
3. JVM的组成
- 类加载器(ClassLoader)
- 运行时数据区(Runtime Data Area)
- 执行引擎(Execution Engine)
- 本地库接口(Native Interface)
4. JVM工作流程
ClassLoader
负责加载字节码文件,至于是否可以运行,由Execution Engine
决定。Execution Engine
把指令和数据信息加载到内存中,并且负责把命令解释到操作系统,将JVM
指令集翻译成操作系统指令集。
当Execution Engine
执行指令时,可能会调用一些本地的接口,这时就需要用到Native Interface
,主要负责调用本地的接口给java
程序使用,会在本地方法栈中记录对应的本地方法。
5. 运行时方法区Runtime Data Area
是JVM最重要的部分,运行时数据区的组成主要包含以下
PC Register
(程序计数器)
程序计数器是程序控制流的指示器,循环,跳转,异常处理,线程的恢复等工作都需要依赖程序计数器去完成。程序计数器是线程私有的,它的生命周期是和线程保持一致的,我们知道,N
个核数的CPU
在同一时刻,最多有N
个线程同时运行,在我们真实的使用过程中可能会创建很多线程,JVM
的多线程其实是通过线程轮流切换,分配处理器执行时间来实现的。既然涉及的线程切换,所以每条线程必须有一个独立的程序计数器。Stack
(Java虚拟机栈)
虚拟机栈,其描述的就是线程内存模型,也可以称作线程栈,也是每个线程私有的,生命周期与线程保持一致。在每个方法执行的时候,jvm
都会同步创建一个栈帧去存储局部变量表,操作数栈,动态连接,方法出口等信息。一个方法的生命周期就贯彻了一个栈帧从入栈到出栈的全部过程。Native Method Stack
(本地方法栈)
本地方法栈的概念很好理解,我们知道,java
底层用了很多c
的代码去实现,而其调用c
端的方法上都会有native
,代表本地方法服务,而本地方法栈就是为其服务的。Heap
(堆)
可以说是JVM
中最大的一块儿内存区域了,它是所有线程共享的,不管你是初学者还是资深开发,多少都会听说过堆,毕竟几乎所有的对象都会在堆中分配。Method Area
(方法区)
方法区也是所有线程共享的区域,它存储了被jvm
加载的类型信息、常量、静态变量等数据。运行时常量池就是方法区的一部分,编译期生成的各种字面量与符号引用就存储在其中。
💡 随着Java 7及以后版本的发布,虽然字符串常量池被移至堆内存,运行时常量池仍然是方法区(或Java 8中的元空间)的一部分。
💡 在Java 8之前,方法区的实现被称为永久代(PermGen),并且是堆的一部分。所以,当我们说”方法区”时,从概念上讲,它是JVM的一个独立逻辑部分,但在HotSpot JVM的具体实现中,直到Java 7为止,它是作为堆内存结构的一个部分(即永久代)来实现的,永久代是堆的一个物理部分。
💡从Java 8开始,HotSpot JVM去除了永久代的概念,引入了元空间(Metaspace),并且元空间是在本地内存中,而不是在堆内存中。因此,在Java 8及以后的版本中,方法区的实现从永久代变为了元空间,方法区(现在通常指的是元空间)与堆内存是完全分开的。
💡 元空间在本地内存中,只要内存足够,就不会出现OOM(Out of Memory)。元空间的概念仍然在JVM内存模型中。
二、深入JVM内存模型(JMM)
。。。待续