目录
[1.1 JVM内存与本地内存](#1.1 JVM内存与本地内存)
[1.2 JVM与JDK的关系](#1.2 JVM与JDK的关系)
[2.1 JVM(Java Virtual Machine)](#2.1 JVM(Java Virtual Machine))
[2.2 Java与JVM的关系](#2.2 Java与JVM的关系)
[2.3 JVM的内存结构](#2.3 JVM的内存结构)
[2.3.1 堆内存](#2.3.1 堆内存)
[2.3.2 栈内存](#2.3.2 栈内存)
[2.3.3 方法区](#2.3.3 方法区)
[2.3.4 本地方法栈](#2.3.4 本地方法栈)
[2.3.5 程序计数器(PC寄存器)](#2.3.5 程序计数器(PC寄存器))
[2.4 JVM的位置](#2.4 JVM的位置)
[2.5 JVM的体系结构](#2.5 JVM的体系结构)
[2.6 JVM调优](#2.6 JVM调优)
[3.1 类加载器的作用](#3.1 类加载器的作用)
[3.2 常见的类加载器](#3.2 常见的类加载器)
[3.3 双亲委派机制](#3.3 双亲委派机制)
[3.4 沙箱安全机制](#3.4 沙箱安全机制)
[4.1 Native关键字](#4.1 Native关键字)
[4.2 JNI的作用](#4.2 JNI的作用)
[5.1 定义](#5.1 定义)
[5.2 作用](#5.2 作用)
[6.1 定义](#6.1 定义)
[6.2 特点](#6.2 特点)
[7.1 栈](#7.1 栈)
[7.1.2 线程独立](#7.1.2 线程独立)
[7.1.3 栈溢出](#7.1.3 栈溢出)
[7.2 堆内存](#7.2 堆内存)
[7.2.1 堆的结构](#7.2.1 堆的结构)
[7.2.2 垃圾回收](#7.2.2 垃圾回收)
[7.2.3 堆内存溢出(OOM)](#7.2.3 堆内存溢出(OOM))
[7.2.4 堆内存的调优](#7.2.4 堆内存的调优)
[7.2.5 内存分配策略](#7.2.5 内存分配策略)
[8、 默认垃圾回收算法](#8、 默认垃圾回收算法)
[8.1 分代垃圾回收](#8.1 分代垃圾回收)
[8.2 垃圾回收器的选择](#8.2 垃圾回收器的选择)
[8.3 总结](#8.3 总结)
[9.1 OOM(OutOfMemoryError)分析](#9.1 OOM(OutOfMemoryError)分析)
[9.2 工具介绍](#9.2 工具介绍)
[9.3 在IDEA中使用JProfiler](#9.3 在IDEA中使用JProfiler)
[10.1 标记整理(标记压缩)](#10.1 标记整理(标记压缩))
[10.2 标记清除法](#10.2 标记清除法)
[10.3 复制算法](#10.3 复制算法)
[10.4 引用计数器](#10.4 引用计数器)
[10.5 GC算法总结](#10.5 GC算法总结)
[10.6 GC算法与分代收集](#10.6 GC算法与分代收集)
1、引言
1.1 JVM内存与本地内存
-
JVM内存 vs. 本地内存:
在JVM中操作时,主要是使用JVM虚拟机的内存,这包括了堆内存(Heap)、方法区(Method Area)、栈内存(Stack)、本地方法栈(Native Method Stack)等。JVM负责管理和分配这部分内存。
- 堆内存:用于存储对象实例,这是JVM主要使用的内存区域。
- 栈内存:用于存储每个线程的局部变量、方法调用链等。
- 方法区:用于存储类信息、常量、静态变量等。
- 本地方法栈:用于调用本地(native)方法时使用的内存。
JVM操作的这些内存都在其管理之下,虽然它运行在操作系统的本地环境中,但开发者一般不直接与操作系统的内存打交道。JVM虚拟内存是在操作系统的本地内存之上管理的。简单来说,JVM虚拟机的内存是操作系统为它分配的内存的一部分,它在此基础上进一步划分和管理。
1.2 JVM与JDK的关系
-
JVM和JDK的关系:
当下载并安装JDK(Java Development Kit)时,JVM(Java Virtual Machine)就会自动包含在内。JVM是JDK中的一个核心组件,它负责执行Java字节码。JDK不仅包含JVM,还包括编译器(javac)、标准类库等开发工具。所以只要你安装了JDK,JVM就已经包含在你的环境中了,不需要单独下载。
2、JVM基础
2.1 JVM(Java Virtual Machine)
JVM是Java虚拟机,是运行Java程序的基础环境。它负责加载、执行Java字节码,并提供内存管理、垃圾回收等功能。Java的跨平台性正是由于JVM的存在,因为Java程序在不同操作系统上的JVM上运行,从而实现了"写一次,处处运行"的特性。
2.2 Java与JVM的关系
- Java编译过程:Java源代码首先会通过编译器(如
javac
)编译成字节码(.class
文件)。这个字节码并不直接运行在物理机器上,而是由JVM来解释和执行。 - JVM的作用:JVM是Java程序运行时的核心,它接收和解释Java字节码,并将其转换为操作系统可执行的机器指令。通过JVM,Java程序可以跨平台运行,即同一份Java代码可以在不同操作系统上运行。
2.3 JVM的内存结构
官方JVM体系结构图
简易结构图
JVM内存结构分为多个区域,每个区域负责管理不同类型的数据和任务:
- JVM垃圾介绍:
- 堆内存中的垃圾:堆中存储所有对象实例,由于Java的自动内存管理机制,垃圾回收器(GC)会负责回收不再使用的对象,这些未使用的对象即为"垃圾"。
- 方法区中的垃圾:虽然方法区存储类信息和常量,但在某些情况下,未被使用的类或静态数据也会被回收,因此方法区本质上也可能产生垃圾。
- 栈内存中的数据:Java栈、本地方法栈和程序计数器中存储的是临时数据,比如局部变量、方法调用信息等。由于这些区域的生命周期与方法的执行周期相关,方法执行完毕后栈帧会自动释放,因此不会有垃圾存在。
2.3.1 堆内存
- 堆(Heap):用于存储所有Java对象及数组。堆是垃圾回收器(GC)的主要工作区域,因为在这个区域中会产生垃圾对象。
2.3.2 栈内存
- Java栈(Java Stack):存储局部变量、方法调用栈帧等。每个线程都有自己独立的Java栈,栈中不会存在垃圾对象,因为栈帧随方法执行结束自动释放。
2.3.3 方法区
- 方法区(Method Area):存储已加载的类信息、常量、静态变量、即将编译的代码等。方法区本质上是堆内存的一部分,但用于特定用途。
2.3.4 本地方法栈
- 本地方法栈(Native Method Stack):专门用于调用本地方法(使用非Java语言编写的方法,如C或C++),类似于Java栈。
2.3.5 程序计数器(PC寄存器)
- 程序计数器(PC寄存器):存储当前线程执行的字节码的地址(程序指令),用于跟踪线程的执行进度。
2.4 JVM的位置
JVM运行在操作系统之上,充当了Java程序和底层操作系统之间的中间层。JVM实际上是一个软件,其主要实现通常是使用C语言编写的,部分实现还可能使用其他低级语言(如汇编)来优化性能。JVM负责屏蔽不同操作系统的细节,使Java程序能够在多种平台上运行。
2.5 JVM的体系结构
JVM的体系结构包括多个组成部分,主要分为以下几个模块:
- 类加载器系统(Class Loader Subsystem):负责加载.class文件,验证、准备、解析和初始化类。
- 运行时数据区(Runtime Data Area):包括堆、栈、方法区等,用于管理程序执行时的各种数据。
- 执行引擎(Execution Engine):负责解释执行字节码或将字节码编译为本地机器码。
- 本地方法接口(Native Interface):用于调用非Java编写的本地方法。
- 垃圾回收系统(Garbage Collection System):自动管理堆中的对象回收。
2.6 JVM调优
JVM的性能优化通常主要针对堆和方法区进行。因为这两个区域负责管理Java对象、类信息和静态数据,并且会产生垃圾。通过调整堆的大小、垃圾回收策略、类加载和卸载等,可以提高应用的性能和内存使用效率。
- JVM调优的几个常见方面:
- 堆大小设置:通过
-Xms
和-Xmx
参数设置堆的初始大小和最大大小。 - 垃圾回收器选择:不同的GC算法适合不同的场景,如
G1
适用于大内存应用,CMS
适用于低延迟应用。 - 方法区调优:通过调整方法区的大小和类卸载策略,防止内存溢出或类加载过多导致的性能问题。
- 堆大小设置:通过
3、类加载器及双亲委派机制
- 类加载器及双亲委派机制:确保类加载的安全性和有效性,通过双亲委派避免核心类被恶意替换。
3.1 类加载器的作用
类加载器(ClassLoader)负责将.class
文件加载到JVM中并转化为内存中的Class对象。具体来说,当你创建一个对象(如new Student()
)时,类加载器会加载该类的字节码文件,而引用对象存放在栈中,实际的对象数据存储在堆内存中。
3.2 常见的类加载器
JVM中存在多种类加载器,它们按层次结构组织,负责加载不同范围的类:
- 虚拟机自带的加载器:由JVM内部实现,用于加载JDK核心类。
- 启动类加载器(Bootstrap ClassLoader):又称为根类加载器,负责加载JVM核心库,通常是
$JAVA_HOME/lib
中的类,如rt.jar
。 - 扩展类加载器(Extension ClassLoader):用于加载扩展库,通常是
$JAVA_HOME/lib/ext
目录下的类。 - 应用程序类加载器(Application ClassLoader):负责加载用户类路径中的类,通常加载
classpath
中的类,是默认的类加载器。
3.3 双亲委派机制
双亲委派机制是一种类加载器的工作方式,用来避免类重复加载并保证Java核心类的安全性。
双亲委派机制的执行过程如下:
- 类加载请求:当某个类加载器接收到一个类的加载请求时,它不会立即尝试加载,而是将请求委派给它的父类加载器去处理。
- 逐级向上委派:每个类加载器都会将请求向上委托给父类加载器,直到最顶层的启动类加载器(Bootstrap ClassLoader)。
- 启动类加载器检查:启动类加载器检查是否能够加载该类,如果能,则加载并返回;否则将请求传递给子类加载器。
- 子类加载器处理:如果启动类加载器无法加载,才由子类加载器尝试加载。
注意:双亲委派机制的一个常见问题是如果我们定义了一个与JDK核心类(如
java.lang.String
)同名的类,它将永远不会被加载。因为启动类加载器会优先加载java.lang.String
类,导致我们的自定义类无法加载,进而报错,如找不到main
方法。
通过obj.getClass().getClassLoader()
方法可以获取对象的类加载器,利用反射可以深入了解类加载器的工作机制。
3.4 沙箱安全机制
沙箱机制是一种保护机制,限制程序运行时的系统资源访问,防止恶意代码执行或访问敏感资源。类加载器在沙箱机制中扮演了重要角色,特别是采用了双亲委派机制,防止用户定义的恶意类替代核心类。
沙箱安全机制的组成:
- 字节码校验器:确保字节码的正确性和安全性。
- 类加载器:采用双亲委派机制防止类加载冲突。
- 存取控制器:控制程序对文件、网络等系统资源的访问。
- 安全管理器:提供更细粒度的安全策略控制。
- 安全软件包:实现Java平台的安全功能,如加密、认证等。
4、Native方法与JNI(Java本地接口)
- Native方法与JNI:允许Java调用底层代码,通过JNI扩展Java的使用范围。
4.1 Native关键字
native关键字用于声明本地方法,即用非Java语言(如C/C++)实现的方法。这些方法是因为Java无法直接实现某些底层操作,必须调用操作系统或底层库来完成。native方法调用时,会进入本地方法栈,并通过JNI(Java Native Interface,Java本地接口)与底层代码交互。
4.2 JNI的作用
JNI是Java与其他语言(如C/C++)进行交互的桥梁,主要作用是扩展Java的能力,让Java可以调用非Java的底层代码,尤其是在需要高性能或者与系统底层紧密相关的场景下(如硬件交互、系统调用等)。
5、程序计数器(PC寄存器)
- PC寄存器:用于线程的字节码指令跟踪,是实现多线程的关键。
5.1 定义
程序计数器(Program Counter Register)是一个非常小的内存区域,每个线程都有一个独立的PC寄存器。它用来存储当前线程正在执行的字节码指令地址,指向下一条即将执行的指令。由于Java是多线程的,PC寄存器是线程私有的。
5.2 作用
程序计数器用于跟踪线程的执行进度,线程切换时通过它恢复到正确的执行位置,因此它是实现Java多线程的关键组件之一。
6、方法区
- 方法区:用于存储类信息、静态变量、常量等,是JVM内存结构的重要组成部分。
6.1 定义
方法区(Method Area)是JVM内存的一个逻辑区域,它由所有线程共享,主要用于存储:
- 已加载的类信息(类名、方法、字段等)
- 运行时常量池(存储编译器生成的常量)
- 静态变量
- 类的字节码和方法(包括构造方法、接口定义等)
6.2 特点
- 共享区域:方法区是线程共享的内存空间,不像栈和PC寄存器那样是线程私有的。
- 存储内容:主要存储类的元数据、静态变量、常量池等,而对象实例是存储在堆中的。
- 垃圾回收:尽管方法区存储静态信息,但在某些情况下(如类卸载)也会触发垃圾回收。
7、Java栈与堆内存
7.1 栈
7.1.1数据结构
- 数据结构:栈是一种后进先出(LIFO,Last In First Out)的数据结构,只有栈顶的元素可以被访问和修改。
7.1.2 线程独立
- 线程独立:每个线程都有自己的栈,栈的生命周期与线程相同。
- 栈内存内容:
- 八大基本数据类型(如
int
,char
等) - 对象引用:指向堆中实际对象的引用
- 方法调用:每当一个方法被调用时,相关信息(如局部变量、参数等)会存放在栈中。
- 八大基本数据类型(如
7.1.3 栈溢出
-
栈溢出
栈溢出(Stack Overflow)通常发生在递归调用中,如果方法循环调用而没有终止条件,最终会耗尽栈空间,导致溢出错误。
7.2 堆内存
-
堆内存
- 单一堆:一个JVM实例只有一个堆内存,所有线程共享这个堆。
- 堆内存的调节:可以通过JVM参数进行配置,通常会调整堆的初始大小和最大大小。
-
JVM实现
三种常见的JVM实现:
- Sun公司的HotSpot
- BEA的JRockit
- IBM的J9 VM
注意:从JDK 1.8开始,永久代(PermGen)被移除,改为使用元空间(Metaspace)来存储类的元数据。
7.2.1 堆的结构
-
堆的结构
堆内存通常被细分为几个区域:
- 新生区(Eden Space):
- 对象在此区域被创建(使用
new
关键字)。 - 如果新生区满了,会触发轻量级垃圾回收(轻GC)。
- 对象在此区域被创建(使用
- 幸存者区(Survivor Space,分为S0和S1):
- 新生区经过一次GC后存活的对象会被转移到这里。经过几次GC后仍然存活的对象可能被转移到老年代。
- 老年区(Old Generation):
- 存放长时间存活的对象,通常是经历过多次GC的对象。
- 元空间(Metaspace):
- 存放类的元数据,从JDK 1.8开始取代永久代。元空间使用本地内存,物理上不存在限制。
- 图
- 新生区(Eden Space):
7.2.2 垃圾回收
- 垃圾回收
- 轻量级垃圾回收(轻GC):主要针对新生区的对象。
- 重量级垃圾回收(重GC):通常针对老年区的对象,处理复杂和耗时。
7.2.3 堆内存溢出(OOM)
-
堆内存溢出(OOM)
-
当堆内存不足以满足对象创建需求时,会抛出OutOfMemoryError(OOM)。
-
常见原因包括创建过多的对象,尤其是字符串等长生命周期的对象。
-
图:
-
-
字符串处理
- 在Java中,字符串的长度是动态的,但受限于可用内存。大量字符串或过长字符串会导致堆内存溢出。
7.2.4 堆内存的调优
- 堆内存的调优
- 新生区调优:设置合适的大小以优化轻GC的频率和效率。
- 老年区调优:通过调整老年区大小来减少重GC的次数
7.2.5 内存分配策略
-
JVM默认情况下分配的总内存通常是计算机内存的1/4,而初始化内存是1/64。可以通过命令行参数调整这些值。
-
默认分配值(idea代码)
javapublic static void main(String[] args) { long max = Runtime.getRuntime().maxMemory(); long total = Runtime.getRuntime().totalMemory(); System.out.println("max" + max + "字节\t" + (max / (double) 1024 / 1024) + "MB"); System.out.println("total" + total + "字节\t" + (total / (double) 1024 / 1024) + "MB"); }
-
IDEA中进行JVM调参
-
参数调优:JVM调优就是在这边地方调优。(调优区别:原本241MB内存大小,调成了981MB)
-
参考:原始参数,下图是调优后的参数。(避免堆内存OOM错误)
调优参数: -Xms1024m -Xmx1024m -XX:+PrintGCDetails
-
控制台中:新生区(伊甸园)、老年区、元空间
- 总结
- 栈用于存储方法调用和局部变量,每个线程都有独立的栈。
- 堆用于存储对象,所有线程共享一个堆,内存分为新生区、幸存者区、老年区和元空间。
- 垃圾回收的策略影响对象的生存周期与内存使用效率。
- 堆内存的调优对于大型应用性能至关重要,合理配置内存可以有效防止OOM错误。
在 Java 中,垃圾回收器(GC,Garbage Collector)主要负责自动管理内存的分配和释放。Java 虚拟机(JVM)中的垃圾回收机制是基于 分代回收 理念的,这种机制认为对象的生命周期是不一样的,因此可以根据对象存活的时间长短采用不同的回收策略。
8、 默认垃圾回收算法
Java 的垃圾回收器默认使用 HotSpot VM,它采用的垃圾回收算法是 分代垃圾回收算法(Generational Garbage Collection),结合了多种不同的垃圾回收算法,具体包括:
- 新生代回收算法:复制算法(Copying Algorithm)
- 老年代回收算法:标记-清除算法(Mark-Sweep Algorithm)和 标记-整理算法(Mark-Compact Algorithm)
8.1 分代垃圾回收
-
新生代:大多数新创建的对象会被分配在新生代。新生代中的对象大部分都是"朝生夕死"的,因此新生代的垃圾回收主要使用 复制算法,将存活对象从一个区域复制到另一个区域,非存活对象则直接被清理。
- Eden 区:新对象首先分配在 Eden 区。
- Survivor 区:对象在 Eden 区中经过一次垃圾回收后,如果没有被清理,会被移动到 Survivor 区。
算法: 新生代中主要使用 复制算法(Copying Algorithm),它将存活的对象复制到另一个区域,而非存活的对象则会被回收。这种方法回收速度很快,因为它不需要遍历所有对象,只需处理存活的对象。
-
老年代:新生代中经过多次垃圾回收后仍然存活的对象会被晋升到老年代。老年代中的对象通常存活时间较长,因此回收频率较低。
算法:
- 标记-清除算法(Mark-Sweep Algorithm):先标记出所有存活的对象,然后清理掉未标记的对象。缺点是容易产生内存碎片。
- 标记-整理算法(Mark-Compact Algorithm):在标记完存活对象后,不仅清理掉未存活的对象,还会将存活对象整理到一起,避免内存碎片问题。
8.2 垃圾回收器的选择
虽然 Java 默认使用的是分代回收机制,但具体使用的垃圾回收器可以根据 JVM 的配置不同而变化,以下是常见的几种垃圾回收器:
- Serial GC:一个简单的单线程垃圾回收器,适合单核 CPU 和小型应用。
- Parallel GC:多线程垃圾回收器,适合多核 CPU 和注重吞吐量的应用。
- G1 GC:一种面向服务端应用的垃圾回收器,旨在减少 GC 造成的停顿时间,替代了老年代的 CMS(Concurrent Mark-Sweep)回收器。
- ZGC 和 Shenandoah GC:专注于极低停顿时间的垃圾回收器,适用于超低延迟应用。
8.3 总结
Java 默认采用的是基于 分代垃圾回收算法 的策略,其中新生代主要使用 复制算法,而老年代结合 标记-清除 和 标记-整理 算法。这种分代回收机制优化了内存管理,使得不同生命周期的对象能被高效回收。
9、使用JProfiler工具分析OOM原因
9.1 OOM(OutOfMemoryError)分析
- OOM(OutOfMemoryError)分析
- 内存快照分析:使用内存快照工具(如JProfiler、MAT)可以方便地查看在OOM发生时的内存状态,定位具体出错的代码行。
- 代码行定位:通过内存快照分析,可以快速找到导致内存泄漏或堆溢出的代码位置,避免逐行调试的麻烦。
9.2 工具介绍
- 工具介绍
- MAT(Memory Analyzer Tool):用于分析Java堆转储文件,帮助定位内存泄漏、获取堆中对象的数据、找到占用内存大的对象等。
- JProfiler:强大的性能分析工具,能够监控应用的内存使用情况,生成内存快照并分析内存泄漏。(实时监控)
9.3 在IDEA中使用JProfiler
-
插件安装:在IDEA中安装JProfiler插件,可以集成性能分析和监控功能。
-
JProfiler官网上下载
-
编写OOM异常代码
-
在IDEA中编写可能导致OOM的代码,利用JProfiler监控具体行数:
javapublic class OOMExample { public static void main(String[] args) { List<String> list = new ArrayList<>(); while (true) { list.add("This is a very long string that will keep consuming memory..."); } } }
-
-
调参
- -Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
-
-Xms:设置JVM的初始内存分配大小,建议设置为总内存的1/64(例如:
-Xms512m
)。 -
-Xmx:设置JVM的最大内存分配大小,建议设置为总内存的1/4(例如:
-Xmx2048m
)。 -
-XX:+PrintGCDetails:打印垃圾回收的详细信息,帮助分析内存使用情况和垃圾回收的效果。
-
监控软件使用
-
配置
- -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\Sachsen\Desktop\testKZ -Xms512m -Xmx512m
-
代码
javapublic static void main(String[] args) { String str = "zhangSan"; while (true) { str += str + new Random().nextInt(1888888888) + new Random().nextInt(999999999); } }
-
java.lang.OutOfMemoryError
-
配置的文件目录下生成快照
-
查看当前对象集
-
查看栈溢出的对象(第一种方式)Current Object Set对象集传递引用查看
-
查看栈溢出的对象(第二种方式)Current Object Set对象集图像结构查看
-
查看栈溢出的对象(查看的第三种方式)Thread Dump线程转储查看
- 线程转储记录了快照时的所有线程,可以查看线程运行状态(运行、等待、阻塞等)和线程调用栈(展现线程执行的方法调用路径)。运行出现错误的线程会用一个特殊的图标标记。
-
- 总结
- 使用JProfiler和MAT等工具可以快速定位和分析OOM的原因,减少线下调试的时间。
- 合理配置JVM内存参数有助于防止内存溢出。
- 对于GC的了解能够帮助优化应用的内存使用和性能。
10、GC常见的算法
10.1 标记整理(标记压缩)
- 概念:首先标记活着的对象,然后将它们整理到内存的一端,释放出连续的空闲空间。
- 优点:减少了内存碎片,使内存使用更高效。
- 缺点:需要额外的时间来移动对象。
10.2 标记清除法
- 概念:标记所有活着的对象,然后直接清除未标记的对象。
- 优点:实现简单,不需要额外空间。
- 缺点:存在内存碎片,可能导致内存浪费。
10.3 复制算法
- 概念:将存活的对象从Eden区复制到两个幸存者区中的一个(To区),当进行垃圾回收时,清空Eden区。
- 工作过程:
- Eden区:每次GC时,将Eden区的存活对象复制到幸存者区的To区。
- 养老区:对象经历一定次数的GC(默认15次)后,若仍存活,则被转移到老年区。
- 优点:内存利用率高,适合存活时间较短的对象。
- 缺点:Eden和幸存者区可能会产生空闲区域,浪费内存。
10.4 引用计数器
- 概念:为每个对象维护一个计数器,记录引用该对象的数量。当计数器为0时,自动回收对象。
- 优点:简单易实现。
- 缺点:无法处理循环引用,效率低,不推荐使用。
10.5 GC算法总结
内存效率:复制算法>标记清除算法>标记压缩算法(时间复杂度)
内存整齐度:复制算法=标记压缩算法>标记清除算法
内存利用率:标记压缩算法=标记清除算法>复制算法
10.6 GC算法与分代收集
- GC算法:通过不同的策略管理内存,优化性能与资源利用。
- GC算法也称为分代收集算法,因为它利用了对象生命周期的特性,将对象分为新生代(Eden和幸存者区)和老年区。
11、Java内存模型(JMM)
- JMM:确保多线程编程中内存访问的一致性和安全性。
- 定义:Java内存模型规定了Java程序中线程如何访问共享内存,确保线程间的可见性与一致性。
- 主要内容:
- 主内存与工作内存:JMM将内存划分为主内存(共享内存)和工作内存(线程的私有内存)。
- 内存屏障:确保特定的操作顺序和内存可见性。
- 可见性:确保一个线程对共享变量的修改能被其他线程看到。
- 原子性:保证对共享变量的操作是不可分割的。