A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

      首先要了解一个概念,什么是java内存模型,即Java Memory Model,简称 JMM ,它是一种抽象的概念,或者是一种协议,用来解决在并发编程过程中内存访问的问题,同时又可以兼容不同的硬件和操作系统。实际上,java内存模型可以看做Java虚拟机的规范,用来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各个平台下都能达到一致的并发效果。       一、java内存模型概述
       在讲到java内存模型时,先来看一张图:


       内存是非常重要的系统资源,是硬盘和CPU的中间仓库及桥梁,承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程中内存申请、分配、管理的策略,保证了JVM的高效稳定运行不同的JVM对于内存的划分方式和管理机制存在着部分差异。

        上图是JDK1.8之前,java内存模型图。整个JVM运行时数据区分为堆、虚拟机栈、本地方法栈、方法区、程序计数器。接下来分析一下内存模型中各个组件的作用:
1.堆
      Java堆区是JVM内存中最胖的一块区域,因为这里存储的都是对象的实例和数组对象。这块区域是线程共享的,在JVM启动时就会创建。有时候
2.虚拟机栈
        虚拟机栈是线程私有的栈,随着线程的创建而创建,内部由一个个“栈帧”组成,“栈帧”存储的是局部变量,方法出口等信息,而每一个栈帧对应一次方法调用。虚拟机栈的调用是有一个限度的,当调用深度大于JVM锁允许的范围,就会出现栈溢出的错误。
3.本地方法栈
       也是线程私有的,本地方法栈主要是负责JVM用到的本地方法,所以这个部分是不需要我们去关心。
4.程序计数器
       也可以叫PC寄存器,JVM支持多个线程同时运行,每个线程都有自己的程序计数器。对于java普通方法(即没用native关键字修饰的方法),存储的是执行过程中当前指令的地址,而对于native方法,这里是空的(undefined),为啥呢?因为调用本地方法的时候可能已经超出了JVM虚拟机的内存地址了。同时,程序计数器是JVM中唯一不会报内存溢出(OutOfMemoryError)的区域。
5.方法区
         方法区是一个比较重要的区域,java虚拟机规范中把方法区描述为堆的一个逻辑部分。主要存储的是静态变量,常量(包括运行时常量),类的加载信息和java编译后的代码。这部分空间不需要连续,可以选择固定大小和可扩展,通常在这部分是没有GC的,因为GC回收的都是些静态变量,常量和类的加载信息,这些对象回收效果通常不尽人意,因此可以选择不实现垃圾回收。这块区域也称为持久代,当这块内存不足时,也会报OutOfMemoryError异常。
二、JDk1.8之后内存模型的变化
       在JDK1.8之后,java内存模型出现了一个变化,如下图:

      在JDK1.8之前内存模型存在永久代(PermGen) ,所谓永久代,是方法区的一种实现,并且只有HotSpot才有“PermGen space”,而对于其他类型的虚拟机并没有“PermGen space”。在JDK1.8中,HotSpot已经没有“PermGen space”这个区间了,取而代之是Metaspace(元空间)。
       元空间,存储的类信息、编译后的代码数据等已经移动到了MetaSpace(元空间)中,元空间并没有处于堆内存上,而是直接占用的本地内存(NativeMemory)。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。
        当然我们也可以通过配置来指定元空间的大小,如下:
  •        -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值
  •        - XX:MaxMetaspaceSize,最大空间,默认是没有限制的
  •        -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
  •        -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集


三、内存模型的目标
       通过前面的部分,我们知道内存模型其实是一种规则,它的目的是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出这样的底层细节。这些变量包括实例字段、静态字段和构成数组对象的元素,但是不包括局部变量和方法参数,因为这些是线程私有的,不会被共享,所以不存在竞争问题。
四、主内存与工作内存
       如下图,所有的变量都存储在主内存,每条线程还有自己的工作内存,保存了被该线程使用到的变量的主内存副本拷贝。线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,不能直接读写主内存的变量。不同的线程之间也无法直接访问对方工作内存的变量,线程间变量值的传递需要通过主内存。

那么,内存与内存之间是怎么操作呢?
五、内存之间的交互操作
一个变量如何从主内存拷贝到工作内存、如何从工作内存同步回主内存,Java内存模型定义了8种操作:
1)lock,作用于主内存变量,把一个变量标记为线程独占。
2)unlock,与lock正相反。
3)read,作用于主内存变量,它把一个变量从主内存传输到工作内存中。
4)load,作用于工作内存变量,把从read里面获取的变量放入工作内存的变量副本中。
5)use,作用于工作内存变量,把变量的值传递给执行引擎。
6)assign,作用于工作内存变量,把执行引擎的值 复制给工作内存变量。同use相反
7)store,作用于工作内存变量,把工作内存变量传输到主内存中。
8)write,作用于主内存变量,把store获取的值,写入到住内存中的变量。
read & load, store & write成对出现。
还有其他一些规则,目的就是保证内存变量的 操作合理,有序。



0 个回复

您需要登录后才可以回帖 登录 | 加入黑马