|
|
@ -88,19 +88,16 @@ JVM把描述类的数据加载到内存里面,并对数据进行校验、解
|
|
|
|
|
|
|
|
|
|
|
|
# JVM
|
|
|
|
# JVM
|
|
|
|
|
|
|
|
|
|
|
|
## JAVA内存模型(JMM)
|
|
|
|
## JAVA内存模型
|
|
|
|
|
|
|
|
|
|
|
|
JAVA内存模型(Java Memory Model,JMM)描述线程之间如何通过内存(Memory)来进行交互。具体说来:
|
|
|
|
Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来**屏蔽掉各种硬件和操作系统的内存访问差异**,以实现让Java程序在各种平台下都能达到一致的内存访问效果。
|
|
|
|
|
|
|
|
|
|
|
|
- **主存区(Main Memory或Java Heap Memory)**:对于所有线程进行共享,而每个线程又有自己的工作内存(Working Memory,实际上是一个虚拟的概念)
|
|
|
|
|
|
|
|
- **工作内存**:工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作并非发生在主存区,而是发生在工作内存中,而线程之间是不能直接相互访问的,变量在程序中的传递,是依赖主存来完成的。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
JMM描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存中读取出变量这样的底层细节。所有的变量都存储在主内存中,每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中变量的一份拷贝)。JMM的两条规定:
|
|
|
|
JMM描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存中读取出变量这样的底层细节。所有的变量都存储在主内存中,每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中变量的一份拷贝)。JMM的两条规定:
|
|
|
|
|
|
|
|
|
|
|
|
- 线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写
|
|
|
|
- **线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写**
|
|
|
|
- 不同的线程之间无法直接访问其他线程工作内存中的变量,线程变量值的传递需要通过主内存来完成
|
|
|
|
- **不同的线程之间无法直接访问其他线程工作内存中的变量,线程变量值的传递需要通过主内存来完成**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -148,14 +145,14 @@ JVM试图定义一种统一的内存模型,能将各种底层硬件以及操
|
|
|
|
|
|
|
|
|
|
|
|
**线程隔离数据区:**
|
|
|
|
**线程隔离数据区:**
|
|
|
|
|
|
|
|
|
|
|
|
- **程序计数器:** 一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器
|
|
|
|
- **程序计数器:** 一块较小的内存空间,存储当前线程所执行的字节码行号指示器
|
|
|
|
- **虚拟机栈:** 里面的元素叫栈帧,**存储局部变量表、操作栈、动态链接、方法返回地址**等,方法被调用到执行完成的过程对应一个栈帧在虚拟机栈中入栈到出栈的过程
|
|
|
|
- **虚拟机栈:** 里面的元素叫栈帧,存储 **局部变量表、操作栈、动态链接、方法返回地址** 等
|
|
|
|
- **本地方法栈:** 和虚拟机栈的区别在于虚拟机栈为虚拟机执行Java方法,本地方法栈为虚拟机使用到的本地Native方法服务
|
|
|
|
- **本地方法栈:** 为虚拟机使用到的本地Native方法服务时的栈帧,和虚拟机栈类似
|
|
|
|
|
|
|
|
|
|
|
|
**线程共享数据区:**
|
|
|
|
**线程共享数据区:**
|
|
|
|
|
|
|
|
|
|
|
|
- **方法区:** 可以描述为堆的一个逻辑部分,或者说使用永久代来实现方法区。存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
|
|
|
|
- **方法区:** 存储已被虚拟机加载的**类信息、常量、静态变量、即时编译器编译后的代码**等数据
|
|
|
|
- **堆:** 唯一目的就是存放对象的实例,是垃圾回收管理器的主要区域,分为Eden、From/To Survivor空间
|
|
|
|
- **堆:** 唯一目的就是存放**对象的实例**,是垃圾回收管理器的主要区域
|
|
|
|
- **元数据区**:常量池、方法元信息、
|
|
|
|
- **元数据区**:常量池、方法元信息、
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -164,11 +161,11 @@ JVM试图定义一种统一的内存模型,能将各种底层硬件以及操
|
|
|
|
|
|
|
|
|
|
|
|
程序计数器(Program Counter Register)。一块较小的内存空间, 是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。正在执行 java 方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果还是 Native 方法,则为空。这个内存区域是唯一一个在虚拟机中没有规定任何 OutOfMemoryError 情况的区域。
|
|
|
|
程序计数器(Program Counter Register)。一块较小的内存空间, 是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。正在执行 java 方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果还是 Native 方法,则为空。这个内存区域是唯一一个在虚拟机中没有规定任何 OutOfMemoryError 情况的区域。
|
|
|
|
|
|
|
|
|
|
|
|
- 线程私有
|
|
|
|
- **线程私有**
|
|
|
|
- 是一块很小的独立内存空间
|
|
|
|
- **是一块很小的独立内存空间**
|
|
|
|
- 主要存储当前线程所执行的字节码行号指示器
|
|
|
|
- **主要存储当前线程所执行的字节码行号指示器**
|
|
|
|
- 以一种数据结构的形式放置于内存中
|
|
|
|
- **以一种数据结构的形式放置于内存中**
|
|
|
|
- 分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成
|
|
|
|
- **分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成**
|
|
|
|
|
|
|
|
|
|
|
|
**注意**:此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
|
|
|
|
**注意**:此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
|
|
|
|
|
|
|
|
|
|
|
@ -178,9 +175,9 @@ JVM试图定义一种统一的内存模型,能将各种底层硬件以及操
|
|
|
|
|
|
|
|
|
|
|
|
JAVA虚拟机栈(Java Virtual Machine Stacks)。是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、 方法返回值和异常分派( Dispatch Exception)。栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。
|
|
|
|
JAVA虚拟机栈(Java Virtual Machine Stacks)。是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、 方法返回值和异常分派( Dispatch Exception)。栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。
|
|
|
|
|
|
|
|
|
|
|
|
- 线程私有
|
|
|
|
- **线程私有**
|
|
|
|
- JAVA线程创建同时,会自动创建对应的JAVA栈
|
|
|
|
- **JAVA线程创建同时,会自动创建对应的JAVA栈**
|
|
|
|
- JAVA栈包含多个栈帧(运行每个方法,就会自动创建一个栈帧,用于存储局部变量、操作栈和返回值等)
|
|
|
|
- **JAVA栈包含多个栈帧(运行每个方法,就会自动创建一个栈帧,用于存储局部变量、操作栈和返回值等)**
|
|
|
|
|
|
|
|
|
|
|
|
> **相关参数:**
|
|
|
|
> **相关参数:**
|
|
|
|
>
|
|
|
|
>
|
|
|
@ -192,10 +189,10 @@ JAVA虚拟机栈(Java Virtual Machine Stacks)。是描述java方法执行的
|
|
|
|
|
|
|
|
|
|
|
|
本地方法栈(Native Method Stacks)。本地方法区和 Java Stack 作用类似, 区别是虚拟机栈为执行 Java 方法服务, 而本地方法栈则为Native 方法服务, 如果一个 VM 实现使用 C-linkage 模型来支持 Native 调用, 那么该栈将会是一个C 栈,但 HotSpot VM 直接就把本地方法栈和虚拟机栈合二为一。
|
|
|
|
本地方法栈(Native Method Stacks)。本地方法区和 Java Stack 作用类似, 区别是虚拟机栈为执行 Java 方法服务, 而本地方法栈则为Native 方法服务, 如果一个 VM 实现使用 C-linkage 模型来支持 Native 调用, 那么该栈将会是一个C 栈,但 HotSpot VM 直接就把本地方法栈和虚拟机栈合二为一。
|
|
|
|
|
|
|
|
|
|
|
|
- 线程私有
|
|
|
|
- **线程私有**
|
|
|
|
- 与JAVA栈的作用相似
|
|
|
|
- **与JAVA栈的作用相似**
|
|
|
|
- 主要为JVM使用本地方法(native)提供支持
|
|
|
|
- **主要为JVM使用本地方法(native)提供支持**
|
|
|
|
- 不是由Java实现的,而是由C实现的
|
|
|
|
- **不是由Java实现的,而是由C实现的**
|
|
|
|
|
|
|
|
|
|
|
|
与虚拟机栈一样,本地方法栈区域也会抛出**StackOverflowError**和**OutOfMemoryError**异常:
|
|
|
|
与虚拟机栈一样,本地方法栈区域也会抛出**StackOverflowError**和**OutOfMemoryError**异常:
|
|
|
|
|
|
|
|
|
|
|
@ -218,10 +215,10 @@ Exception in thread "main": java.lang.OutOfMemoryError: <reason> <stack trace> (
|
|
|
|
|
|
|
|
|
|
|
|
方法区(Method Area)。即我们常说的**永久代(Permanent Generation)**, 用于存储**被 JVM 加载的类信息、常量、静态变量、即时编译器编译后的代码**等数据,HotSpot VM把GC分代收集扩展至方法区, 即使用Java堆的永久代来实现方法区, 这样 HotSpot 的垃圾收集器就可以像管理 Java 堆一样管理这部分内存, 而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型的卸载, 因此收益一般很小)。运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。 Java 虚拟机对 Class 文件的每一部分(自然也包括常量池)的格式都有严格的规定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、装载和执行。
|
|
|
|
方法区(Method Area)。即我们常说的**永久代(Permanent Generation)**, 用于存储**被 JVM 加载的类信息、常量、静态变量、即时编译器编译后的代码**等数据,HotSpot VM把GC分代收集扩展至方法区, 即使用Java堆的永久代来实现方法区, 这样 HotSpot 的垃圾收集器就可以像管理 Java 堆一样管理这部分内存, 而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型的卸载, 因此收益一般很小)。运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。 Java 虚拟机对 Class 文件的每一部分(自然也包括常量池)的格式都有严格的规定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、装载和执行。
|
|
|
|
|
|
|
|
|
|
|
|
- 又称之为:**非堆(Non-Heap)**或 **永久区**
|
|
|
|
- **又称之为:非堆(Non-Heap)或 永久区**
|
|
|
|
- 线程共享
|
|
|
|
- **线程共享**
|
|
|
|
- 主要存储:类的类型信息、**常量池(Runtime Constant Pool)**、字段信息、方法信息、类变量和Class类的引用等
|
|
|
|
- **主要存储:类的类型信息、常量池(Runtime Constant Pool)、字段信息、方法信息、类变量和Class类的引用等**
|
|
|
|
- Java虚拟机规范规定:当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常
|
|
|
|
- **Java虚拟机规范规定:当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常**
|
|
|
|
|
|
|
|
|
|
|
|
> **相关参数:**
|
|
|
|
> **相关参数:**
|
|
|
|
>
|
|
|
|
>
|
|
|
@ -235,11 +232,11 @@ Exception in thread "main": java.lang.OutOfMemoryError: <reason> <stack trace> (
|
|
|
|
|
|
|
|
|
|
|
|
堆内存(JAVA Heap)。是被线程共享的一块内存区域,创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行垃圾收集的最重要的内存区域。由于现代 VM 采用**分代收集算法**, 因此 Java 堆从 GC 的角度还可以细分为: **新生代**(Eden区、From Survivor 区和 To Survivor 区)和老年代。
|
|
|
|
堆内存(JAVA Heap)。是被线程共享的一块内存区域,创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行垃圾收集的最重要的内存区域。由于现代 VM 采用**分代收集算法**, 因此 Java 堆从 GC 的角度还可以细分为: **新生代**(Eden区、From Survivor 区和 To Survivor 区)和老年代。
|
|
|
|
|
|
|
|
|
|
|
|
- 线程共享
|
|
|
|
- **线程共享**
|
|
|
|
- 主要用于存储JAVA实例或对象
|
|
|
|
- **主要用于存储JAVA实例或对象**
|
|
|
|
- GC发生的主要区域
|
|
|
|
- **GC发生的主要区域**
|
|
|
|
- 是Java虚拟机所管理的内存中最大的一块
|
|
|
|
- **是Java虚拟机所管理的内存中最大的一块**
|
|
|
|
- 当堆中没有内存能完成实例分配,且堆也无法再扩展,则会抛出OutOfMemoryError异常
|
|
|
|
- **当堆中没有内存能完成实例分配,且堆也无法再扩展,则会抛出OutOfMemoryError异常**
|
|
|
|
|
|
|
|
|
|
|
|
> **相关参数:**
|
|
|
|
> **相关参数:**
|
|
|
|
>
|
|
|
|
>
|
|
|
|