美文网首页
android 多线程 — java 内存模型

android 多线程 — java 内存模型

作者: 前行的乌龟 | 来源:发表于2018-05-31 03:42 被阅读309次

JAVA 的内存模型是很复杂的,其中不仅包括操作系统底层的设计,更是和硬件设备(CPU 结构)密切相关,了解清楚 JAVA 的内存模型对于我们后面的学习,尤其是同步有非常大的帮助,要不有些地方会理解不通的。

JVM,JMM 概念


在开头我们先来了解下2个概念:

  • JVM
    JVM (Java Virtual Machine) Java虚拟机模型。主要描述的是Java虚拟机内部的结构以及各个结构之间的关系,负责将 java 文件编译成 class 文件并运行 class 文件
  • JMM
    JMM (Java Memory Model) Java内存模型。定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式,线程之间内存刷新的关系,是隶属于JVM的

简单来说,JVM可以理解为 java 执行的一个操作系统,而操作系统的内存模型就是 JVM

JAVA 内存模型


历史:

最初的 JAVA 内存模型不够好,存在很多的不足,所以在 JAVA 1.5 中,JAVA 内存模型的版本的进行了一次重大的更新与改进,并且在 JAVA 8 中仍然被使用。

JMM 的内存模型图很复杂,咱来个简单点的,便于理解,对于多线程来说,咱们了解这么多也就差不多了,要是研究 GC 的话,还需要更详细的研究,比如老年区啥的


JMM 内存模型图

JAVA 的内存模型中主要分3块:

  • 堆内存(Java Heap)
    内存中最大的一块,是被所有线程共享的一块内存区域。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

  • 栈(Native Method Stacks)
    它是线程私有的,它的生命周期与线程相同。存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型),它不等同于对象本身,根据不同的虚拟机实现,它可能是一个指向对象起始地址的引用指针,也可能指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。

  • 方法区(Method Area)
    被所有的线程共享,所有的class(class是指类的原始代码,要创建一个类的对象,首先要把该类的代码加载到方法区中,并且初始化)和static变量都存在这 ,存储的都是在整个程序中永远唯一的元素

  • 常量池(方法区的一部分)
    JVM为每个已加载的类型维护一个常量池,常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)和对其他类型、方法、字段的符号引用(1)。池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在Java的动态链接中起了核心作用。常量池存在于堆中

或者看下面的文章也可以,描述的更加清楚

数据存储必须要明确的点:

  • 对象本身存储在 堆内存,但是对象引用存在 栈内存中
  • JAVA 的8种基本数据类型是存在 栈内存里面的,线程间传递基本数据类型传递的都不是数据本身,而是基本数据类型的副本,另一个线程对同一个基本数据类型的修改不会影响其他线程中的值,但是对象不同,对象传递的引用,因为堆内存是共享的,一个线程修改了堆内存中对象的数据,那么必然会影响其他线程中的值
  • 方法中生成的局部变量全部存储在 栈内存中,即便是方法中 new 了一个对象出来,这个对象本身也是存在栈内存中的,不会跑到堆内存中,方法结束,既会销毁相关数据
  • 基本数据类型的包装对象不被视为基本数据类型了,而是对象了,会存在堆内存中。

一切如下面这2张图


数据存储示意图_1 数据存储示意图_2

这样看着应该好理解一些

JAVA 内部的数据传递


再来说下栈,栈是个统称,栈的内部是还可以再去细分的,看这个简单的图大家都能发现里面分好几块,要是看更详细的资料,大家会发现分的块还要多,不过这无助于我们理解多线程,我们只要记住栈是单个线程私有的,有多个线程在工作,那么内存中就会存在多个栈内存区域

如下图:


1234352-0a8474641ef704d5.png

线程处理数据的顺序:

  • 这里指引用对象
  • 习惯上我们把 堆内存 称为主内存
  1. Thread 对象把要处理的对象从主内存中 copy 一份到当前线程所在的栈内存中
  2. 假如这个处理耗时2秒,那么2秒后更新栈内存中这个对象的数据副本
  3. 最后把栈内存中对象的数据副本再写回到主内存中,我们称之为:刷新到主内存

看图逻辑会清晰一些


线程处理数据

线程处理数据的过程可能跟我们之前的想法有些出入,并不是直接操作堆内存中所在的数据,然后把数据自己 copy 了一份去处理,拿到结果再刷新堆内存中的数据。

这就造成一个问题,有个时间差,单线程没什么,一切按顺序执行,那么多线程呢,多线程怎么办呢,要是 Thread1 在处理数据时,Thread2 也在同时处理数据,2者同时往主内存中刷新数据,那么主线程会刷新成哪个线程的数据啊。再者就算 Thread1 和 Thread2 前后往主内存中刷新数据,那么结果也是不多啊,Thread1 先执行,那么 Thread2 在操作同一个数据时应该是在 Thread1 的结果基础上的,现在变成了 2个线程都在同一个数上操作,即便是前后写入结果到主内存中,结果也不是我们预期的。

举个例子,主内存有个对象,有个参数 money =1 ,Thread1 ++,Thread2 +2,那么 Thread1 和 Thread2 同时执行,那么 Thread1 和 Thread2 中 这个对象的 copy 副本中的 money 都是1 ,然后 Thread1 先写入数据到主内存,Thread2 紧随其后,此时 money 是 3,那么这个结果我们想要的吗,不是啊,我们想要的结果是 4 啊,我们想让 Thread1 先把 money 变成 2 ,然后Thread2 在 money =2 的基础上再加2 ,变成4

多线程同时刷新数据

这个例子搞懂了,多线程中我们面临的问题也就是很清晰了,怎么保持主内存数据和栈内存中数据副本的相同,那么这个就得靠 JAVA 多线程的同步机制了

硬件层面内存模型


上文书说到 java 的内存模型不光收到 JAVA 底层影响,更是受到硬件影响

下图是一个简化的现代计算机硬件结构图:


计算机硬件结构图

现在的 CPU 都有多个核心,每个核心都有自己的寄存器和高速缓存器

  • 寄存器 register
    每个cpu都会有一系列的寄存器registers在cpu的内存中,而且这些寄存器是很重要的。cpu在寄存器上进行计算操作比在主内存中进行计算要快的多。这是因为cpu访问寄存器的速度比访问内存要快得多。

  • 高速缓存器 cache
    每个cpu也会有一个cpu的cache内存。这是因为cpu访问cache比访问内存的速度要快得多,但是却比访问的寄存器要慢一些,所以cache的速度是介于寄存器和内存的。一些cpu还有多级cache,比如(Level 1 and Level 2),但是这对于我们理解java内存模型关系不大,我们只需要cpu有三层内存结构,寄存器-cache-内存(RAM)

CPU 在处理对象时,和线程一样会把对象 Copy 一份放到自己的 cache 中,然后先修改自己 cache 中的对象数据副本,然后再刷新回主内存中。

我们可以把 CPU 的寄存器,高速缓存看成另一个线程中的栈内存,都是会产生数据同步问题的,这样就好理解了。

Java内存模型和硬件内存模型的联系

最后


好了内存模型了解到这里就差不多了,我们知道了不管是线程中的栈内存空间还是 CPU 的寄存器和高速缓存都会操作从主内存的copy 的数据副本,因时间差和多线程,多 CPU 核心同时操作同一个对象数据,会产生数据错乱的数据同步问题。大家了解了多线程数据不同步产生的根本原因,那么本篇的内容就算是达到目的了,知道了为什么我们才好有的放矢不是。

吐槽下本篇文章真是不好写,反复找了不好资料,尤其是找图很麻烦啊,反复看了很多资料,书也看了3本:《开发艺术探索》《进阶之光》《从小工到专家》,就怕说错了误导大家。这个知识点我也是了解的不是非常深入,有错误请大家指正,我第一时间修改。

写的不好......想砸电脑......

参考资料:


相关文章

  • 深入理解Java并发内存模型

    Java内存模型是什么 Java 内存模型翻译自Java Memory Model,也称Java多线程内存模型,简...

  • Android下多线程的实现

    Android下多线程相关 线程安全相关问题参考:java内存模型与线程 android下与多线程有关的主要有以下...

  • android高阶基本技术清单

    待完成清单 多线程并发编程 Java内存模型 android源码 页面启动过程具体源码过程... java数据类...

  • java内存模型

    前言 在学习java多线程并发编程前,必须要了解java内存模型,只有了解java内存模型,才能知道为什么多线程并...

  • (五) volatile关键字

    Java多线程目录 1 背景 理解Java多线程的内存抽象逻辑请阅读java多线程内存模型,当代操作系统,处理器为...

  • java并发编程(四)

    java多线程编程(四) 引言: 内存模型的基础----内存模型相关的基本概念 java内存模型中的顺序一致性--...

  • android 内存泄漏

    内存泄漏 内存管理 内存模型 Android原生开发以java为主。 在java中,Java内存模型,往往是指J...

  • Android 系统内存管理机制

    系列文章Java 内存模型Android 系统内存管理机制Android 性能优化(三)之内存管理Android ...

  • Java 内存模型- JMM

    系列文章Java 内存模型Android 系统内存管理机制Android 性能优化(三)之内存管理Android ...

  • java线程安全总结

    浅谈java内存模型 不同的平台,内存模型是不一样的,但是jvm的内存模型规范是统一的。其实java的多线程并发问...

网友评论

      本文标题:android 多线程 — java 内存模型

      本文链接:https://www.haomeiwen.com/subject/vtefsftx.html