Java内存模型学习

简介: 1,内存模型 左侧绿色区域为共享内存区,右侧黄色区域为线程私有内存区①方法区:存放被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码,方法区有一个运行时常量池存放编译器生成的字面量和符号引用。

1,内存模型
image

左侧绿色区域为共享内存区,右侧黄色区域为线程私有内存区
①方法区:存放被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码,方法区有一个运行时常量池存放编译器生成的字面量和符号引用。
②堆:虚拟机启动时创建,Java虚拟机管理内存中最大的一块,用于存放对象实例(注意:因为堆是垃圾收集器管理的主要区域,因此也叫GC堆)
③程序计数器:记录当前线程执行的字节码行号指示器,控制分支,循环,跳转,异常,线程恢复等基础功能
④虚拟机栈:虚拟机私有,线程创建时创建,代表java方法执行的内存模型。每个方法执行都会创建一个栈帧来存储方法的变量,操作数栈,动态链接方法,返回值,返回地址等信息。每个方法从调用到结束就对应于虚拟机栈中入栈和出栈的过程。
⑤本地方法栈:与虚拟机用到的Native方法相关。

2,内存执行模型
java程序的运行实体是线程,每个线程都会有一个工作内存(即栈空间)由线程私有,而内存模型归档所有的变量都存储在所有线程共享的堆内存,所以线程对变量的操作要:
①从堆内存中拷贝变量的副本到本线程的工作内存中
②在本线程的工作内存中操作变量副本
③操作完成后将变量写会回主内存
注意:如果方法中有一个局部变量是基本数据类型(如int, long, boolean, byte, short, char, float, double)则该变量直接存储在线程私有的栈内存中,如果本地变量是引用类型,则该引用存储在栈内存中,引用指向的实例存放在堆内存中。

3,CPU与内存的交互模型
image

读写速度CPU寄存器>CPU缓存>RAM

4,java线程与内核线程
内核线程是操作系统内核支持的线程,由操作系统内核完成线程管理,对于应用程序而言,一般不会直接调用内核线程,而是使用一种轻量级的进程(即通称意义上的线程),每个轻量级的进程都会映射到一个内核线程上,然后由操作系统将内核线程分发给各个处理器处理。
一个轻量级进程(线程)会映射到一个内核线程

image

5,线程安全问题
1)多个线程同时操作堆内存中的数据(线程间共享)时,可能引发线程安全问题,原因是:每个线程都会拷贝一份数据副本到线程私有内存区,如果线程A,B同一时间段内读取数据,修改,并回写,可能发生①A回写前,B就复制副本;②A回写后,B才复制副本等不确定问题。
2)程序运算的时候并不是所有操作在操作系统层都是原子性的,例如程序执行c = a+b,在指令层面可能会有:①load a to register1②load b to register2③add register1 register2 to register3④load register3 to c。这样的情况如果在多线程同时处理c变量的时候就有可能出现线程安全问题。另外:编译器语句重排;执行并行重排;内存系统重排等。
3)线程A修改了共享变量a的值,还未回写到共享存储区之前线程B是不知道修改后的值的,但在单线程下,修改后的值都是可见的,这叫可见性
4)java程序中,在本线程内所有的操作都视为有序的,这叫有序性;多线程环境下,一个线程中观察另外一个线程,所有操作都是无序的(由于指令重排/主内存和工作内存的延迟同步现象)

6,如何解决上面的原子性,可见性,有序性
①synchronized或ReentrantLock保证操作的原子性(volatile不能保证原子性)
②synchronized或volatile关键字保证可见性
③volatile保证有序性,因为它禁止重排优化
④happens-before原则

7,volatile的作用
①保证被volatile修饰的共享变量对所有的线程都是可见的,即:某个线程修改后的新值总是被其他线程立即得到
②禁止指令重排
volatile不能保证原子性,例如下面的程序可能会有线程安全问题
public class VolatileVisibility{
public volatile static int I = 0;
public static void increase(){

i++;//改操作不是原子性的,所以任然可能会有线程安全问题

}
}
修改方法是在increase方法上用synchronized修饰,属性定义上可去掉volatile修饰。
volatile保证可见性的方式:当修改一个volatile变量时,会强制将工作内存中改变后的值刷新到主内存;读取volatile变量时,会先将本线程工作内存中的值置为无效,重新从主内存里读取新副本。
④典型单例模式中禁止重排例子
Public class DoubleCheckLock {
private static DoubleCheckLock instance;
private DoubleCheckLock (){};
public DoubleCheckLock getInstance(){

if(null == instance){
    synchronized (DoubleCheckLock.class){
if(null == instance){
instance = new DoubleCheckLock();

}
}
}
return instance;
}
}
上面的程序有线程安全问题:当线程B读取到instance不为空的时候,其他线程初始化的动作可能还没有完成,因为new DoubleCheckLock();大致可以分为如下3步:
①memory = allocate();//分配内存空间
②instance(memory);//初始化内存区域
③instance=memory;//将初始化好的内存区域赋值给instance
上面三步中的②③可能会出现重排,即先赋值,再初始化。因此此时应该在私有变量instance上用volatile修饰:
public volatile static DoubleCheckLock instance;

目录
相关文章
|
1天前
|
监控 Java
Java中的内存泄漏分析与排查技巧
Java中的内存泄漏分析与排查技巧
|
1天前
|
安全 Java 编译器
深入解析Java内存模型
本文旨在探讨Java内存模型的设计理念、核心原理及其在并发编程中的应用。通过分析内存模型的结构组成,阐述其在确保多线程环境下数据一致性和可见性方面的关键作用。进一步讨论同步机制、happens-before原则以及内存屏障等概念,并结合实例代码演示如何正确利用Java内存模型进行高效并发编程。
|
1天前
|
存储 缓存 NoSQL
Java中的内存数据库与缓存技术
Java中的内存数据库与缓存技术
|
2天前
|
监控 Java 数据库连接
如何避免Java中的内存泄漏?
如何避免Java中的内存泄漏?
|
3天前
|
安全 Java
Java中的内存模型详解
Java中的内存模型详解
|
3天前
|
监控 算法 Java
如何优化Java应用程序的内存管理
如何优化Java应用程序的内存管理
|
1天前
|
存储 Java 程序员
Java内存模式以及volatile关键字的使用
Java内存模式以及volatile关键字的使用
8 0
|
1天前
|
安全 算法 Java
深入理解Java内存模型
本文将深入探讨Java内存模型,包括其定义、作用以及如何影响Java程序的并发和同步。我们将通过具体实例来说明内存模型如何确保线程安全,并讨论在多线程编程中常见的问题及其解决方案。文章旨在为读者提供对Java内存模型的清晰理解,帮助编写更高效、更可靠的并发程序。
|
1天前
|
存储 算法 Java
Java内存管理深度解析
在Java的世界中,内存管理是一块基石,它支撑着整个应用程序的运行。本文将深入探讨Java的内存管理机制,包括堆、栈、方法区的概念及其在内存中的角色和作用。我们将通过实际案例和数据,分析Java如何自动进行内存分配和垃圾回收,以及这些操作对程序性能的影响。文章还将介绍一些常见的内存泄漏场景和避免策略,帮助开发者更好地理解并优化他们的Java应用。
8 0
|
2天前
|
存储 监控 算法
Java堆栈内存管理与优化技巧的实践指南
Java堆栈内存管理与优化技巧的实践指南