一文深度分析Java内存模型

0x01 内存模型产生的历史背景

曾经,计算机的世界远没有现在复杂,那时候的cpu只有单核,我们写的程序也只会在单核上按代码顺序依次执行,根本不用考虑太多。

后来,随着技术的发展,cpu的执行速度和内存的读写速度差异越来越大,人们很快发现,如果还是按照代码顺序依次执行的话,cpu会花费大量时间来等待内存操作的完成,这造成了cpu的巨大浪费。

为了弥补cpu和内存之间的速度差异,计算机世界的工程师们在cpu和内存之间引入了缓存,虽然该方法极大的缓解了这一问题,但追求极致的工程师们觉得这还不够,他们又想到了一个点子,就是通过合理调整内存的读写顺序来进一步缓解这个问题。

比如,在编译时,我们可以把不必要的内存读写去掉,把相关连的内存读写尽量放到一起,充分利用缓存。

比如,在运行时,我们可以对内存提前读,或延迟写,这样使cpu不用总等待内存操作的完成,充分利用cpu资源,避免计算能力的浪费。

这一想法的实施带来了性能的巨大提升,但同时,它也带来了一个问题,就是内存读写的乱序,比如原本代码中是先写后读,但在实际执行时却是先读后写,怎么办呢?

为了避免内存乱序给上层开发带来困扰,这些工程师们又想到了可以通过分析代码中的语义,把有依赖关系,有顺序要求的代码保持原有顺序,把剩余的没有依赖关系的代码再进行性能优化,乱序执行,通过这样的方式,就可以屏蔽底层的乱序行为,使代码的执行看起来还是和其编写顺序一样,完美。

之后,计算机的性能就是在硬件的飞速发展及软件执行方式的深入优化下,野蛮生长了很长一段时间,同样一段代码,即使什么都不改,每隔一段时间还是会有执行速度上的成倍提升,这就是著名的摩尔定律时代。

但万事皆有终结,随着硬件上遇到的物理瓶颈越来越多,软件上对执行方式的优化已接近极限,单纯通过单核来提升性能的方式显示是已经走不通了,此时,计算机世界已经来到了一个十字路口,前方的路如何走,已经成为困扰所有人的一个问题。

既然单核遇到了瓶颈,为什么不发展多核呢?

不知道谁想到了这个点子,一下引爆了整个计算机行业,是啊,单核不行了我们可以发展多核嘛,自此,整个计算机行业都开始向多核转变,也由此进入到了多核的大航海时代。

2005年,时任intel主席的Paul Otellini宣布,将所有的未来产品都转向多核,并预测这将是历史性的拐点。

事实也确实如此,到现在,我们用的所有的cpu基本都是多核。

多核时代的到来虽然重启了计算机世界新一轮的发展,但也带来了一个非常严峻的问题,那就是多核时代如何承接单核时代的历史馈赠。

上面我们提到,在单核时代,为了追求代码的极致性能,我们在编译时和运行时都对内存操作做了各种乱序处理,虽然已经通过一定的方式,让这种乱序不影响到上层开发,对上层逻辑透明,但这种方式只对单核有效,进入到多核时代,单核运行不可见的乱序,在多核情况下都可见了,且此种乱序已经严重影响到了多核代码的正确编写。

怎么办呢?移除乱序优化,让代码还是按照原来的顺序方式执行?

那这样会造成巨大的性能损失,此种情况下由单核进化到多核的意义也就不大了,那怎么办呢?

谁说代码就一定要顺序执行的?谁说乱序就一定不能对上层可见的?我们可以好好看下我们写的代码,其实很多时候,我们并不介意乱序执行,我们介意的只是某些关键位置上代码的乱序执行。

比如,一个线程先对n个普通变量进行写操作,再对一个特殊的,用于标志写完成的变量进行写操作,此种情况下,我们并不关心前n次写操作是否乱序,我们只要能确保前n次写和最后一次写之间是有序的就可以了,这样,当其他线程要读那前n次写时,只要先读最后那次写,检查其值是否表示前n次已经写完成,如果是,我们就可以放心的读那前n次写了。

默认乱序执行,在关键节点保证有序,这种方式不仅使单核时代的各种乱序优化依然有效,也使多核情况下的乱序行为有了一定的规范。

基于此,各种硬件平台提供了自己的方式给上层开发,约定好只要按我给出的方式编写代码,即使是在多核情况下,该保证有序的地方也一定会保证有序。

这套在多核情况下,依然可以让开发者指定哪些代码保证有序执行的规则,就叫做内存模型。

0x02 内存模型的定义

内存模型的英文是memory model,或者更精确的来说是memory consistency model,它其实就是一套方法或规则,用于描述如何在多核乱序的情况下,通过一定的方式,来保证指定代码的有序执行。

它是介于硬件和软件之间,以一种协议的形式存在的。

对硬件来说,它描述的是硬件对外的行为规范,对软件来说,它描述的是编写多线程代码的一套规则。

总之,内存模型就相当于是在多核时代下,硬件的使用说明书,只要按照说明书来编写多线程程序,那不管底层如何乱序,如何优化,都不会影响你代码的正确性。

0x03 Java的雄心壮志

上文我们提到,内存模型描述的是在多核情况下,硬件的一套行为规范,也就是说,只要硬件支持多核,就必须提供一套自己的内存模型。

这就衍生出了一个问题,就是不同硬件上的内存模型差异很大,完全不兼容。

比如应用于桌面和服务器领域的x86平台用的是x86 tso内存模型。

比如应用于手机和平板等移动设备领域的arm平台用的是weakly-ordered内存模型。

比如最近几年大火的riscv平台用的是risc-v weak memory ordering内存模型。

不同硬件平台的内存模型,描述的指令乱序情况,及禁止乱序的方式都完全不一样,它们只适用于自己平台的指令集,或者说只适用于自己平台的汇编语言。

不过由于汇编语言本身就不具有跨平台性,所以这样做也无可厚非,甚至某种程度上来说,只能这样做,基于这样的事实,大家在各自的硬件平台,按照对应的内存模型规范,编写着自己的汇编代码,相安无事又其乐融融的享受着多核带来的红利。

如果只是写汇编代码的话,因为大家对其没有什么跨平台的预期,所以也沒啥太大的意见,但对于追求跨平台的c/c++等高级语言来说࿰

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值