深入理解Java虚拟机:JVM高级特性与最佳实践-总结-11

news2024/12/27 13:42:38

深入理解Java虚拟机:JVM高级特性与最佳实践-总结-11

  • Java内存模型与线程
    • 概述
    • 硬件的效率与一致性
    • Java内存模型
      • 主内存与工作内存
      • 内存间交互操作

Java内存模型与线程

概述

多任务处理在现代计算机操作系统中几乎已是一项必备的功能了。在许多场景下,让计算机同时去做几件事情,不仅是因为计算机的运算能力强大了,还有一个很重要的原因是计算机的运算速度与它的存储和通信子系统的速度差距太大,大量的时间都花费在磁盘I/O、网络通信或者数据库访问上。 如果不希望处理器在大部分时间里都处于等待其他资源的空闲状态,就必须使用一些手段去把处理器的运算能力“压榨”出来,否则就会造成很大的性能浪费,而让计算机同时处理几项任务则是最容易想到,也被证明是非常有效的“压榨”手段。

除了充分利用计算机处理器的能力外,一个服务端要同时对多个客户端提供服务,则是另一个更具体的并发应用场景。衡量一个服务性能的高低好坏,每秒事务处理数(Transactions Per Second,TPS)是重要的指标之一,它代表着一秒内服务端平均能响应的请求总数,而TPS值与程序的并发能力又有非常密切的关系。 对于计算量相同的任务,程序线程并发协调得越有条不紊,效率自然就会越高;反之,线程之间频繁争用数据,互相阻塞甚至死锁,将会大大降低程序的并发能力。

硬件的效率与一致性

物理计算机中的并发问题:物理机遇到的并发问题与虚拟机中的情况有很多相似之处,物理机对并发的处理方案对虚拟机的实现也有相当大的参考意义。

“让计算机并发执行若干个运算任务”与“更充分地利用计算机处理器的效能”之间的因果关系,看起来理所当然,实际上它们之间的关系并没有想象中那么简单,其中一个重要的复杂性的来源是绝大多数的运算任务都不可能只靠处理器“计算”就能完成。处理器至少要与内存交互,如读取运算数据、存储运算结果等,这个I/O操作就是很难消除的(无法仅靠寄存器来完成所有运算任务)。由于计算机的存储设备与处理器的运算速度有着几个数量级的差距,所以现代计算机系统都不得不加入一层或多层读写速度尽可能接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间的缓冲:将运算需要使用的数据复制到缓存中,让运算能快速进行,当运算结束后再从缓存同步回内存之中,这样处理器就无须等待缓慢的内存读写了。

基于高速缓存的存储交互很好地解决了处理器与内存速度之间的矛盾,但是也为计算机系统带来
更高的复杂度,它引入了一个新的问题:缓存一致性(Cache Coherence)在多路处理器系统中,每个处理器都有自己的高速缓存,而它们又共享同一主内存(Main Memory),这种系统称为共享内存多核系统(Shared Memory Multiprocessors System),如下图所示。当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致。 如果真的发生这种情况,那同步回到主内存时该以谁的缓存数据为准呢?为了解决一致性的问题,需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议来进行操作,这类协议有MSI、MESI(Illinois Protocol)、MOSI、Synapse、Firefly及Dragon Protocol等。“内存模型”可以理解为在特定的操作协议下,对特定的内存或高速缓存进行读写访问的过程抽象。 不同架构的物理机器可以拥有不一样的内存模型,而Java虚拟机也有自己的内存模型,并且与这里介绍的内存访问操作及硬件的缓存访问操作具有高度的可类比性。
在这里插入图片描述除了增加高速缓存之外,为了使处理器内部的运算单元能尽量被充分利用,处理器可能会对输入代码进行乱序执行(Out-Of-Order Execution)优化,处理器会在计算之后将乱序执行的结果重组,保证该结果与顺序执行的结果是一致的,但并不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致,因此如果存在一个计算任务依赖另外一个计算任务的中间结果,那么其顺序性并不能靠代码的先后顺序来保证。 与处理器的乱序执行优化类似,Java虚拟机的即时编译器中也有指令重排序(Instruction Reorder)优化

Java内存模型

《Java虚拟机规范》中曾试图定义一种“Java内存模型”(Java Memory Model,JMM)来屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。在此之前,主流程序语言(如C和C++等)直接使用物理硬件和操作系统的内存模型。因此,由于不同平台上内存模型的差异,有可能导致程序在一套平台上并发完全正常,而在另外一套平台上并发访问却经常出错,所以在某些场景下必须针对不同的平台来编写程序。

定义Java内存模型并非一件容易的事情,这个模型必须定义得足够严谨,才能让Java的并发内存访问操作不会产生歧义;但是也必须定义得足够宽松,使得虚拟机的实现能有足够的自由空间去利用硬件的各种特性(寄存器、高速缓存和指令集中某些特有的指令)来获取更好的执行速度。

主内存与工作内存

Java内存模型的主要目的是定义程序中各种变量的访问规则,即关注在虚拟机中把变量值存储到内存和从内存中取出变量值这样的底层细节。此处的变量(Variables)与Java编程中所说的变量有所区别,它包括了实例字段、静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,因为后者是线程私有的,不会被共享,自然就不会存在竞争问题。 为了获得更好的执行效能,Java内存模型并没有限制执行引擎使用处理器的特定寄存器或缓存来和主内存进行交互,也没有限制即时编译器是否要进行调整代码执行顺序这类优化措施。

Java内存模型规定了所有的变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理硬件时提到的主内存名字一样,两者也可以类比,但物理上它仅是虚拟机内存的一部分)。每条线程还有自己的工作内存(Working Memory,可与前面讲的处理器高速缓存类比),线程的工作内存中保存了被该线程使用的变量的主内存副本[2],线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的数据[3]。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、主内存、工作内存三者的交互关系如下图所示,注意与上面的图片进行对比。
在这里插入图片描述
这里所讲的主内存、工作内存与第2章所讲的Java内存区域中的Java堆、栈、方法区等并不是同一
个层次的对内存的划分,这两者基本上是没有任何关系的。如果两者一定要勉强对应起来,那么从变
量、主内存、工作内存的定义来看,主内存主要对应于Java堆中的对象实例数据部分[4],而工作内存
则对应于虚拟机栈中的部分区域。从更基础的层次上说,主内存直接对应于物理硬件的内存,而为了
获取更好的运行速度,虚拟机(或者是硬件、操作系统本身的优化措施)可能会让工作内存优先存储
于寄存器和高速缓存中,因为程序运行时主要访问的是工作内存。

内存间交互操作

关于主内存与工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步回主内存这一类的实现细节,Java内存模型中定义了以下8种操作来完成。Java虚拟机实现时必须保证下面提及的每一种操作都是原子的、不可再分的(对于double和long类型的变量来说,
load、store、read和write操作在某些平台上允许有例外)。

  • lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
  • unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量 才可以被其他线程锁定。
  • read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以 便随后的load动作使用。
  • load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的 变量副本中。
  • use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。
  • write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

如果要把一个变量从主内存拷贝到工作内存,那就要按顺序执行read和load操作,如果要把变量从工作内存同步回主内存,就要按顺序执行store和write操作。注意,Java内存模型只要求上述两个操作必须按顺序执行,但不要求是连续执行。也就是说read与load之间、store与write之间是可插入其他指令的,如对主内存中的变量a、b进行访问时,一种可能出现的顺序是read a、read b、load b、load a。除此之外,Java内存模型还规定了在执行上述8种基本操作时必须满足如下规则:

  • 不允许read和load、store和write操作之一单独出现,即不允许一个变量从主内存读取了但工作内存不接受,或者工作内存发起回写了但主内存不接受的情况出现。
  • 不允许一个线程丢弃它最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回 主内存。
  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存中。
  • 一个新的变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量,换句话说就是对一个变量实施use、store操作之前,必须先执行assign和load操作。
  • 一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。
  • 如果对一个变量执行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作以初始化变量的值。
  • 如果一个变量事先没有被lock操作锁定,那就不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定的变量。
  • 对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作)。

这8种内存访问操作以及上述规则限定,再加上专门针对volatile的一些特殊规定,就已经能准确地描述出Java程序中哪些内存访问操作在并发下才是安全的。这种定义相当严谨,但也是极为烦琐,实践起来更是无比麻烦。Java设计团队大概也意识到了这个问题,将Java内存模型的操作简化为read、write、lock和unlock四种,但这只是语言描述上的等价化简,Java内存模型的基础设计并未改变,但即使是这四操作种,对于普通用户来说阅读使用起来仍然并不方便。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/565716.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

22WPF----Prism框架

1.关于Prism框架 官网:Prism Library 文档可以参考 源码地址:https://github.com/PrismLibrary/Prism 版本8.1 Prism框架10历史、微软,最新版本使用 2、功能说明 Prism提供了一组设计模式的实现,有助于编写结构良好的且可维…

JavaScript for与forEach结束本轮循环/结束循环

文章目录 前言一、for1.终止当前轮次2.终止循环 二、forEach1.终止当前轮次2.终止循环 总结 前言 我以前一直想尝试一下这个for里嵌switch来着, 找不到合适的机会, 今天写node脚本刚好遇到, 必须狠狠的尝试一下. 一、for 1.终止当前轮次 我先把正确写法放在这里, 如果循环没…

chatgpt赋能python:Python:一个多才多艺的编程语言

Python: 一个多才多艺的编程语言 作为一名有10年 Python 编程经验的工程师,我必须坦言,Python 是我最喜欢的编程语言之一。Python 简单易学,语法简洁,非常适合开发 Web 应用程序、数据分析、人工智能、自然语言处理等方面。 Pyt…

抖音账号矩阵系统源码开发之——视频发布功能开发

视频发布权限在账号矩阵系统研发之初,都是一个备受争议的功能,最早之前我们使用的视频发布权限名字是Video.creat, video.delete权限,但是该权限于2022年10月份做了权限的收回,后又在上架了一个能力叫发布内容至抖音:…

chatgpt赋能Python-python_theano

简介 什么是Python? Python是一种高层次、动态、解释型编程语言。它是一个易于学习且功能强大的语言,拥有广泛的应用领域。Python是由Guido van Rossum于1989年发明和开发的。它的设计哲学强调代码的可读性和简洁性,在保持语言的清晰和简洁性时&#…

chatgpt赋能python:PythonUDF-知道这些你就能轻松实现自己的需求

Python UDF - 知道这些你就能轻松实现自己的需求 如果你是一名Python开发者,你肯定知道Python的强大和适用性。在数据分析、机器学习和Web应用程序等领域,Python的使用已经成为了常态。Python的一个重要特点是拥有大量的库和框架,这些库和框…

C Primer Plus第四章编程练习答案

学完C语言之后,我就去阅读《C Primer Plus》这本经典的C语言书籍,对每一章的编程练习题都做了相关的解答,仅仅代表着我个人的解答思路,如有错误,请各位大佬帮忙点出! 1.编写一个程序,提示用户输…

【unity插件】OpenFracture插件实现物体破裂和切割

插件地址 https://github.com/Mustenaka/OpenFracture 使用注意事项 1.如果要导入自定义网格,则必须在导入设置中将“启用读/写”设置为 true。否则,您将收到错误。 2.网格必须是非相交和封闭的。否则,重新三角测量将失败。 上面描绘的是凳子的线框模型。注意横杆如何与…

kotlin协程并发/并行与串行互相切换,CoroutineScope与await

kotlin协程并发/并行与串行互相切换,CoroutineScope与await import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.time.LocalTimefun main(args: Arra…

斑梨电子香橙派Orange Pi 5/5B金属N505外壳支持安装风扇双重散热可选带天线和电源

spotpear.cn/index/product/detail/id/1327/no/2574.html detail.tmall.com/item.htm?id719963414095&spma211lz.success.0.0.101d2b902yPJCT 【产品简介】 这是一款专为Orange Pi 5/5B设计的高品质金属外壳,我们将它命名为N505,机箱保留了主板的全部接口&…

简谈变电站综合自动化系统在变电站的应用分析

摘 要:变电站综合自动化系统在变电站的应用主要表现在将电气设备侧的模拟信号转化为数字信号,经转化后的数字信号系统实时处理保存为有用的信息数据,保障数据的完整性和一致性。相较于传统的常规变电站二次系统,变电站综合自动化系…

chatgpt赋能Python-python_pyv8

介绍 Python和JavaScript是web开发中最流行的两种编程语言之一。Python以其简洁、高效、灵活和易读的特性而备受欢迎。而JavaScript则是为了网页和web应用程序而设计的。两种语言各有优点,但在某些情况下,结合使用Python和JavaScript可以实现非常强大的…

操作系统第三章——存储系统(下)

锦衣雪华玉颜色,回眸一笑天下倾 文章目录 3.2.1 虚拟内存的基本概念知识总览传统存储方式的特征,缺点局部性原理虚拟内存的定义如何实现虚拟内存技术知识总结 3.2.2 请求分页管理方式知识总览页表机制缺页中断机制地址变换机制知识回顾 3.2.3 页面置换算…

Fourier分析入门——第8章——Fourier系数的统计描述

目录 第 8章 Fourier系数的统计描述 8.1 引言 8.2 统计假设 8.3 Fourier系数对噪声的均值和方差 8.4 Fourier系数对噪声信号的概率分布 8.5 随机信号的Fourier系数分布 8.6 信号平均 第 8章 Fourier系数的统计描述 8.1 引言 上一章通过假设离散函数是通过对连续函数定期…

chatgpt赋能python:Pythonre.compile:用于优化正则表达式匹配的工具

Python re.compile: 用于优化正则表达式匹配的工具 正则表达式在Python编程中扮演着重要的角色,但在处理复杂的文本数据时,由于其匹配速度较慢,可能影响程序的性能。Python re.compile是一种用于优化正则表达式匹配的强大工具。 什么是Pyth…

chatgpt赋能Python-python_peewee

介绍 Python是一种高效、简洁的编程语言,正因如此,它在世界范围内拥有着广泛的应用。作为一种优秀的Python ORM库,Peewee在实现Python数据库操作的同时,也成为了Python编程人员不可或缺的工具。 Peewee是一个轻量级Python ORM库…

C4D R26 渲染学习笔记(3):物体基本操作+快捷键 (更新中)

往期视频 C4D R26 渲染学习笔记(1):C4D版本选择和初始UI框介绍 C4D R26 渲染学习笔记(2):渲染流程介绍 常用快捷键大全 快捷键名称E移动工具R旋转工具T缩放工具X/Y/ZX/Y/Z轴锁定V选择转盘UU组合操作KK组…

chatgpt赋能python:PythonRewind:重新审视Python的历史和未来

Python Rewind: 重新审视Python的历史和未来 Python Rewind是一个术语,用于描述重新审视Python语言历史的过程,以及挖掘Python语言未来的潜能。Python Rewind旨在提供Python编程语言的全面历史回顾和前景分析,同时包括Python应用程序和工具的…

python+vue智能小区物业水电费管理系统6becq

这次开发的智能小区管理系统有管理员,业主,物业员工三个角色。 管理员功能有个人中心,业主管理,物业员工管理,家庭成员管理,车位信息管理,业主报修管理,报修处理管理,业主…

java泛型初阶和包装类

文章目录 1 包装类6 泛型如何编译的6.1 擦除机制6.2 为什么不能实例化泛型类型数组 7 泛型的上界7.1 语法7.2 示例7.3 复杂示例 8 泛型方法8.1 定义语法8.2 示例8.3 使用示例-可以类型推导8.4 使用示例-不使用类型推导 1 包装类 在Java中,由于基本类型不是继承自Ob…