• 基础 | JVM - [内存模型 & JMM]


    JVM 内存模型简介

    在这里插入图片描述

    上面内存组成中,堆和方法区(JDK1.8后称为元空间)为线程共享,其余均为线程独占。
    另一说 JVM 内存由7部分组成,这是因为单独计算了 运行时常量池(它是方法区的一部分) 并额外计算了直接内存(但其实它不是JVM内部的一部分)

    程序计数器(Program Counter Register)
    程序计数器是线程独立的
    用于记录当前线程下一条需要被执行的指令

    虚拟机栈(Java Stack)
    也称java 栈、线程栈,
    线程独立,生命周期同线程
    包含多个栈帧:方法执行时为其分配的栈空间
    默认大小约 1M,与操作系统主机配置有关
    用于存储下面几种数据:

    • 局部变量表
    • 操作数栈
    • 动态链接
    • 方法出入口

    示例代码

    public static int a(){
    	int a = 1;
    	return a;
    }
    
    public static void main(){
    	System.Out.Println(a());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    main 方法调用 a 方法时,是通过动态链接找到的 a 方法
    a 方法中对局部变量 a 进行赋值时,会先将 1 压入操作数栈,然后在局部变量表中开辟空间存放变量 a ,再从操作数栈弹栈赋值给变量 a
    执行结束后,通过方法出入口记录的地址找到 main 方法调用 a 方法的位置进行返回并是不接下来要执行的程序
    本地方法栈(Native Method Stack)
    Native 方法使用的内存空间

    堆(Heap)
    所有线程共享
    java中所有对象实例以及数组都存放在堆中
    是垃圾收集器主要管理的区域
    默认大小约 1/64(初始) 到 1/4(最大) 物理内存
    组成

    • 新生代
      • 伊甸
      • 幸存区 0(FROM)
      • 幸存区 1 (TO)
    • 老年代

    方法区
    JDK 1.8 以后叫 元空间,1.8 以前叫 永久代。
    所有线程共享
    用于保存

    • 类信息
    • 常量
    • 静态变量
    • 即时编译器编译后的代码缓存


    常量、静态变量 的对象 在堆里
    从下面案例可以看到静态变量与常量的对象时在堆里的,但其引用应该是在方法区(否则没法解释)

    public class MetaspaceBoomDemo {
        static byte[] a = new byte[10 * 1024*1024];//静态变量 10M
       // public static final byte[] a = new byte[10 * 1024*1024];//常量 10M
    
        public static void main(String[] args) {
            System.out.println(new MetaspaceBoomDemo());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    堆 3M ,元空间 30M:-Xms3m -Xmx3m -XX:MetaspaceSize=30m,爆炸了
    在这里插入图片描述
    堆 30M ,元空间 3M:-Xms30m -Xmx30m -XX:MetaspaceSize=3m,没事
    在这里插入图片描述

    JVM 的直接内存

    JVM 的直接内存,是 JVM 直接使用的 JVM 之外的内存
    默认大小约 1/4(最大) 物理内存(与 JVM 堆一致)
    直接内存的作用主要包括

    • JDK 8 及之后版本的 Metaspace 是直接保存在直接内存的
      但要注意 JDK 6 及以前的 字符串常量池 是在永久代(JDK 7 及以前的方法区)中的,从 7 开始放到 里面了
    • NIO 在使用过程中可以通过 ByteBuffer.allocteDirect(capability) 在直接内存中划分区域使用
      此方式因为避免了在 Java 堆Native 堆 之间来回复制数据,所以相对较快
      但是若不断分配直接内存,JVM 堆内存使用较少,JVM 就不会触发 GC ,当直接内存先分配满还没 GC 时,会出现 OOM:Direct buffer memory

    JMM 内存模型

    JMM 是 java 内存模型:java Memory Model ≠ JVM 内存模型
    JMM 本身是一个抽象概念,并不真实存在
    JMM 描述了一组规则规范,通过它定义了程序中各个变量的访问方式
    上述变量包括成员变量、静态变量和集合中的元素

    JMM 内存模型
    示意图
    在这里插入图片描述
    概念
    主内存
    也可以理解为 共享内存
    JMM 将大部分变量都视为存储在 主内存 中,这些变量都可能被多线程同步访问操作

    本地内存
    也可以理解为 工作内存,对线程而言是私有的
    工作内存是相互隔离的,其互相访问需要通过主内存

    线程操作共享内存中变量的流程

    • 从主内存复制变量进线程的工作内存中
    • 操作变量副本
    • 操作结果同步回主内存
      • 线程解锁前,必须将共享变量的值刷新会主内存
      • 线程加锁前,必须读取主内存的值到自己的工作内存
      • 加锁和解锁是对于同一把锁

    JMM 工作流程

    • read
      作用于主内存
      从主内存将值传输到工作内存
    • load
      作用于工作内存
      将 read 到的值存入工作内存变量副本中
    • use
      作用于工作内存
      将工作内存变量副本的值传递给 CPU
      每次使用到变量都会执行此操作
    • assign
      作用于工作内存
      将 CPU 处理后的结果赋值给工作内存变量副本
      每次给变量赋值时,都会执行此操作
    • store
      作用于工作内存,将工作内存变量副本的值写回主内存(值传递给主内存)
    • write
      作用于主内存,将 store 传输的值写回主内存变量
    • lock
      作用于主内存
      为防止线程安全问题,将变量标记为线程独占
      只锁了写变量的过程
    • unlock
      作用于主内存
      释放锁定状态的变量
  • 相关阅读:
    JAVA计算机毕业设计毕业生信息管理系统Mybatis+源码+数据库+lw文档+系统+调试部署
    Linux(CentOS7)搭建LAMP服务环境
    tiup cluster reload
    齿轮振动信号的数字滤波处理-含Matlab代码
    bootz启动 Linux内核涉及 bootm_os_get_boot_func 函数
    【智能优化算法】基于象鼻虫优化算法求解单目标优化问题附matlab代码
    React 状态管理 - Context API 前世今生(下)
    Creo 9.0 中几何对象的选取方法
    Java基础 - 9 - 集合进阶(二)
    C/C++教程 从入门到精通《第二章》—— 快速入门C/C++
  • 原文地址:https://blog.csdn.net/ZEUS00456/article/details/126481627