专栏首页洁癖是一只狗面试java内存模型

面试java内存模型

JAVA内存模型

今天我们讲一下java内存模型(JMM),JMM的问题在面试中经常被问到,我们今天就讲一下这道题.

在正式Java内存模型之前,先讲一下机器硬件CPU.

计算机的并发问题和虚拟机的情况有不少相似之处,我们知道所有的运算操作都是由CPU完成的.CPU指令就牵扯到了数据的操作,难免就会和主内存打交道,虽然CPU发展的性能越来越好,但还是受限于内存的访问速度,通常这种差异是千差万别的,极端情况甚至上万倍.

基于上面原因,便有了加入缓存(CPU Cache)的机制,CPU Cache 充当CPU 和主内存的缓冲,就有了下面的操作

  1. 将数据先放到缓存中
  2. CPU就可以直接拿缓存的数据计算处理
  3. 处理完之后就再放回主内存中

下面就是CPU的架构图

虽然这样解决速度的问题,但是也带来了另外一个问题,缓存一致性,举个例子如i++。

  1. 读取主内存的 i到CPU Cache中
  2. 对进行加一操作
  3. 将结果写到CPU Cache中
  4. 将数据刷新到主内存中。

i++在单线程不会有问题,但是多线程下就会有问题,假设i的初始值是0,两个线程本地都把i=0缓存到的CPU Cache中,然后都对i进行加一操作后,然后把他们放到主内存,就有可能存储的i还是1,这就是典型的缓存不一致.

这问题也有解决的办法,有两种:

  1. 通过总线加锁的方式
  2. 通过缓存一致性协议。

第一种是早期的解决办法, 他的原理就是一种悲观的实现,有多个CPU要和主内存交互的之前,都要和总线(数据总线,控制总线,地址总线)进行通信,总线只会允许一个CPU访问变量的主内存,这种效率太低,第二种,就是缓存一致性方式解决一致性问题,看一下的图,如下

缓存一致性最为出名的是Intel的MESI协议,保证了缓存使用中共享变量副本都是一致的,他的原理是:

  1. CPU 操作Cache中的数据
  2. 如果发现变量是共享变量,也就是说其他CPU中也有一个副本
  3. 那么当读取数据时,不做任何处理
  4. 如果是写操作,就是会让其他CPU的缓存失效,其他CPU就会从主内存再次获取。

理解了上面的内存,那对Java内存模型学习就很容易了,但是他们不是一个东西,我们先看一下Java内存模型是啥样子

java的内存模型决定了一个线程对共享变量的写入何时对其他线程可见,Java内存模型定义线程和主内存之间的抽象关系,具体如下

  1. 共享变量存储于主内存,每个线程都可以访问
  2. 每个线程都有私有的工作内存,或者成为本地内存
  3. 工作内存只存储线程对变量的副本
  4. 线程不能直接操作主内存,只有先操作工作内存之后,才能写入主内存
  5. 工作内存和java内存模型都是抽象的概念,他并不真实存在,他涵盖了缓存,集萃器,编译器优化以及硬件等等

我们再来看一个例子

如图,本地内存A,和本地内存B有主内存中共享变量的X的副本,初始时,这3个内存中的x都是0,线程A执行后,把更新的值,临时放到自己的本地内存A中,当线程A和线程B通讯时候,线程A首先会把自己本地内存修改的值,放到主内存中,此时,B的本地内存已经失效,线程B到主内存拿到的值就是线程A更新后的值,线程B的本地内存就会变成1。这一点和CPU和CPU Cache 关系非常相似。

切记,他们并不完全一样,比如计算机物理内存不会存在栈内存,堆内存的划分,无论是堆内存还是虚拟机栈内存都会对应到物理的主内存,当然也有一部分的数据有可能存入CPU Cache寄存器中。如下图

JMM是一种规范,目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题,java中的许多关键字是使用java内存模型封装了底层给我们使用,如synchronized ,volatile,final.这里我们不展开讲解,有兴趣的可以自行学习。

本文分享自微信公众号 - 洁癖是一只狗(rookie-dog),作者:洁癖

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-12-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Mysql如何选择唯一索引和普通索引

    相信大家对唯一索引和普通索引是有一定的了解的,那么在不同的业务场景,使用唯一索引还是普通索引呢,比如下面的场景

    小土豆Yuki
  • Linux 集群总结 + LVS(负载均衡器)原理及配置

    相信你已经对集群分类有了大致了解了,那么我们现在详细说说使用LVS来实现负载均衡集群。

    小土豆Yuki
  • Mysql数据--死锁解密

    Mysql行锁是在引擎中实现的,并不是所有的存储引擎都支持行锁,比如myisam就不支持行锁,而innodb支持行锁,myisam在并发度高的系统中就会影响系统...

    小土豆Yuki
  • 吊打Java面试官-Java内存模型深入详解(JMM)

    为了提高程序运行的性能,现代CPU在很多方面对程序进行了优化。: 例如: CPU高速缓存。 尽可能地避免处理器访问主内存的时间开销,处理器大多会利用缓 存...

    JavaEdge
  • Android内存泄漏终极解决篇(上)

    一、概述 Android内存的文章详见:http://blog.csdn.net/linghu_java/article/details/39480761 在...

    非著名程序员
  • JVM调优,程序员必须掌握的知识

    在开发的过程中,可以通过oracle的jdk,bin目录下的jvisualvm.exe查看是否死应用锁,且会发现有线程一直在休眠状态

    黎明大大
  • 深入理解java虚拟机-第二章:java内存区域与内存泄露异常

    java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解...

    用户1134788
  • Redis学习五(Redis 阻塞的原因及其排查方向).

    因为 Redis 是单线程的,大量的慢查询可能会导致 redis-server 阻塞,可以通过 slowlog get n 获取慢日志,查看详情情况。

    JMCui
  • 放下王者农药这锅,玩一把Tensorflow吧

    暑期开始了!对于Lady姐来说,如何安排儿子的暑期生活是一件大事,显然是不能沉迷于王者农药, ? 于是Lady姐随手扔了一个教程给他:按照这份教程,在家里Win...

    GPUS Lady
  • 宋宝华:那些年你误会的Linux DMA(关于Linux DMA ZONE和API最透彻的一篇)

    互联网、Linux内核书籍上充满了各种关于Linux DMA ZONE和dma_alloc_coherent、dma_map_single等的各种讲解,由于很多...

    Linux阅码场

扫码关注云+社区

领取腾讯云代金券