首页 > 系统相关 >java 内存模型

java 内存模型

时间:2023-06-05 11:34:22浏览次数:48  
标签:java 变量 模型 Cache 线程 内存 操作 CPU

java内存模型(memory model)定义了java虚拟机如何与计算机内存交互。JVM将内存主要分为栈(stack)内存和堆(heap)内存。每当我们声明新的变量和对象、调用新的方法、声明String或执行类似的操作时,JVM都会从堆栈内存或堆空间为这些操作指定内存。

The Java Memory Model From a Logic Perspective

每个线程有自己的栈内存,是线程独有的。堆是线程共享数据区域。每个线程操作自己的线程栈,存储当前正在执行方法的局部变量(local variable)。一个线程创建的局部变量对创建它的线程以外的所有其他线程是不可见的。即使两个线程正在执行完全相同的代码,这两个线程仍然会在各自的线程堆栈中创建该代码的局部变量。因此,每个线程都有各自版本的局部变量。

所有基本类型的局部变量(boolean, byte, short, char, int, long, float, double)都完全存储在线程堆栈中,对其他线程不可见。局部变量可以是基元类型也可以是对象的引用。在这种情况下,引用(局部变量)存储在线程堆栈上,但对象本身存储在堆上。

The Java Memory Model showing references from local variables to objects, and from object to other objects.

共享变量(shared variable)

所有线程可共同访问的内存称为共享内存或堆内存。

对象的成员变量与对象本身一起存储在堆中。无论成员变量是基元类型,还是引用对象。静态类变量也与类定义一起存储在堆中。堆上的对象可以由所有引用该对象的线程访问。当线程有权访问某个对象时,它也可以访问该对象的成员变量。如果两个线程同时在同一对象上调用一个方法,它们都可以访问对象的成员变量,但每个线程都有自己的局部变量副本

线程对共享变量操作

线程间操作是由一个线程执行的操作,该操作可以被另一个线程检测到或直接影响。程序可以执行几种线程间操作:

Read:读取共享变量

Write:对共享变量赋值

硬件内存架构

Modern hardware memory architecture.

CPU寄存器(CPU Register)是CPU内部一块存储空间,CPU可以直接访问,拥有非常高的访问速度。

Cache :即高速缓冲存储器,是位于CPU与主内存间的一种容量较小但速度很高的存储器。由于CPU的速度远高于主内存,CPU直接从内存中存取数据要等待一定时间周期,Cache中保存着CPU刚用过或循环使用的一部分数据,当CPU再次使用该部分数据时可从Cache中直接调用,这样就减少了CPU的等待时间,提高了系统的效率。Cache又分为一级Cache(L1 Cache)和二级Cache(L2 Cache),L1 Cache集成在CPU内部,L2 Cache早期一般是焊在主板上,现在也都集成在CPU内部,常见的容量有256KB或512KB L2 Cache.

现在计算机一般会有多个CPU多核。可以同时运行多个线程。多线程在访问(读写)共享变量(主内存)会存在缓存一致性问题。

java内存模型和硬件内存架构间关系

The division of thread stack and heap among CPU internal registers, CPU cache and main memory.

Java内存模型和硬件内存体系结构是不同的。硬件内存体系结构不区分线程堆栈和堆。在硬件上,线程堆栈和堆都位于主存中。线程堆栈和堆的一部分有时可能存在于CPU缓存和内部CPU寄存器中。由于对象和共享变量可能存储在不同的内存区域,多线程就会带来一些问题。

  • 可见性(Visibility)

如果两个线程同时访问一个共享变量,可能会引发一个线程修改了变量另一个线程无法感知到。可以使用volatile关键字定义共享变量解决可见性问题。volatile修饰的变量读操作会从主内存中读取变量,写操作会将结果立马刷新到主内存。

  • 原子性(Atomicity)

原子性意味着操作是不可中断的。即使多个线程一起执行,操作一旦启动也不会受到其他线程的干扰。也就是说,原子操作是最小操作单元。原子操作包括。。。

例:a =1 是原子操作,只有一个赋值操作。

i++不是原子操作。包含read,+1,write三个操作。在运算过程中i的值可能会变。

  • 有序性(Ordering)

有序性就是程序按照一定顺序执行。在并发编程中也是如此。顺序是指按照编写代码的顺序执行代码的能力。然而,为了提高程序的执行性能和编译性能,计算机和编译器有时会修改程序的执行顺序。但是,在多线程的情况下,编译器对执行顺序的修改可能会导致错误。

编译器优化:编译器在不改变单线程程序语义前提下,可以重新安排语句的执行顺序。

指令重排:如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。

计算机执行时内存数据区
image

Happen-before原则

顺序执行原则:在一个线程内保证语义串行化,按代码顺序执行

锁原则:对同一个monitor,unlock必须发生在lock之前。

volitale规则 对volatile的write操作早于接下来的read操作。保证可见性。

线程启动规则 线程的start()方法先于线程内的每一个动作

线程终止原则 线程的所有动作先于线程的终止操作

传递性 A先于B,B先于C,那么A先于C

所有对象的初始化操作先于对象的其它操作。

全是规范,实际代码用不到。

Java内存模型使用内存屏障保证有序性,volatile可以保证有序性,仅限于单线程内使用内存屏障防止重排序。多线程有序性还得靠锁。

内存屏障(Memory Barriers)

保证两个操作之间不进行重排序。内存屏障类型:

Load1; LoadLoad; Load2

Load1; LoadStore; Store2

Store1; StoreStore; Store2

Store1; StoreLoad; Load2

volatile内存屏障:

读:读后插入读屏障:volatile读,LoadLoad,LoadStore。

写:前后插入写屏障:StoreStore,volatile写,StoreLoad。

volatile使用内存屏障可以保证线程内有序性。

参考:

https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html

https://jenkov.com/tutorials/java-concurrency/java-memory-model.html

https://www.baeldung.com/java-stack-heap

https://gee.cs.oswego.edu/dl/jmm/cookbook.html

标签:java,变量,模型,Cache,线程,内存,操作,CPU
From: https://www.cnblogs.com/bird2048/p/17457348.html

相关文章

  • java 自己写分页
    longcurrent=1;longsize=100;StringcurrentOther=jsonObject.getStr("current");if(StrUtil.isNotBlank(currentOther)){//?:0或1个,*:0或多个,+:1或多个BooleanstrResult1=currentOther.matches("^[-\\+]?([0-9]+\\.?)?[0-9]+$");if......
  • OSI7层模型和TCP/IP模型
    前言在计算机网络领域中,OSI7层模型和TCP/IP模型是两个重要的概念。本文将对这两个模型进行介绍和比较,让大家了解它们的区别和联系。目录前言OSI7层模型TCP/IP模型OSI7层模型与TCP/IP模型的区别总结OSI7层模型OSI(OpenSystemInterconnection)层模型是国际标准化组织(ISO)制定......
  • Java中Double除保留后小数位的几种方法
     返回double型的1.能四舍五入doubled=114.145;d=(double)Math.round(d*100)/100;System.out.println(d); 2.BigDecimal.ROUND_HALF_UP表示四舍五入,BigDecimal.ROUND_HALF_DOWN也是五舍六入,BigDecimal.ROUND_UP表示进位处理(就是直接加1),BigDecimal.ROUND_DOWN表示直接......
  • Java体系面试题(2022)(一)
    全部试题及答案下载基础篇1、Java语言有哪些特点1、简单易学、有丰富的类库2、面向对象(Java最重要的特性,让程序耦合度更低,内聚性更高)3、与平台无关性(JVM是Java跨平台使用的根本)4、可靠安全5、支持多线程2、面向对象和面向过程的区别面向过程:是分析解决问题的步骤,然后用......
  • Java体系面试题(2022)(二)
    全部试题及答案下载JVM篇1、知识点汇总JVM是Java运行基础,面试时一定会遇到JVM的有关问题,内容相对集中,但对只是深度要求较高.其中内存模型,类加载机制,GC是重点方面.性能调优部分更偏向应用,重点突出实践能力.编译器优化和执行模式部分偏向于理论基础,重点掌握知识点.需了......
  • java 行转列
    行转列工具类publicclassRowConvertColUtil{privatestaticfinalStringNULL_VALUE="";privatestaticfinalStringHEADER_NULL_VALUE="工序";privatestaticSet<Object>headerSet;privatestaticSet<Object>f......
  • Java:从单线程计数器到多线程数据同步synchronized和原子类Atomic
    (目录)使用单线程单线程修改计数器的值,没有发生问题,每次运行结果都是10000,不过程序耗时较长packagecom.example;/***计数器*/classCounter{privatestaticlongcount;publicstaticlonggetCount(){returncount;}publicstaticv......
  • 基于JAVA操作系统在线网站SQL
    随着21世纪的到来,人们更深刻的感受到了计算机在生活和工作中作用的重要,越来越多的职业需要具有计算机的应用技能。掌握计算机是职业的需要,社会的需要,更是事业发展的需要。今天,计算机技术不但广泛地应用在办公自动化中,还全面参与到各行各业。所有与计算机相关的职业都要求工作者有......
  • Java学习笔记(十四)
    1.请描述你理解的IO流的作用 I/O流(输入/输出流)的作用是在程序与外部世界(例如文件、网络、控制台等)之间传输数据。2.请描述I/O流的体系结构(1)InputStream类和OutputStream类,其实现类:FileInputStream和FileOutputStream(2)Reader类和Writer类,其实现类:FileReader和FileWriter(3)缓......
  • 如何计算一个实例占用多少内存?
    我们都知道CPU和内存是程序最为重要的两类指标,那么有多少人真正想过这个问题:一个类型(值类型或者引用类型)的实例在内存中究竟占多少字节?我们很多人都回答不上来。其实C#提供了一些用于计算大小的操作符和API,但是它们都不能完全解决我刚才提出的问题。本文提供了一种计算值类型和引......