概述

Java内存模型(JMM规范)

java程序员的经历 java程序员的日常_操作数


为了方便理解这里画一个简图,这也是很多文章所提及的jvm规范,注意:这只是java虚拟机的规范,并非实际情况,可以看作是一个接口,各种虚拟机的是对这个接口的实现,例如jdk8 hotsport 元空间是对方法区的实现。

程序计数器

也叫做pc寄存器,在计算机中,“寄存器”属性稀缺资源离cpu近,造价贵,读写速度是内存的N倍。程序计数器可以看作是当前线程所执行的字节码指令的行号指示器。字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。分支,循环,跳转,异常处理,线程回复等都需要依赖这个计数器来完成。多线程切换也是通过程序计数器实现当前线程之前该继续执行拿一条指令。

java程序员的经历 java程序员的日常_java程序员的经历_02

本地方法栈

本地方法栈为虚拟机使用到的Native方法(比如C++方法)服务,一个Native Method就是一个java调用非java代码的接口。有时java应用需要与java外面的环境交互。这是本地方法存在的主要原因,你可以想想java需要与 一些底层系统如操作系统或某些硬件交换信息时的情况

虚拟机栈

虚拟机栈也是线程私有,而且生命周期与线程相同,每个Java方法在执行的时候都会创建一个栈帧(Stack Frame);栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构.栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用至执行完成的过程,都对应着一个栈帧在虚拟机栈里从入栈到出栈的过程

  1. 局部变量表
    是一组变量值存储空间,用于存放方法参数和方法内定义的局部变量,一个局部变量可以保存一个类型为boolean、byte、char、short、int、float、reference和 returnAddress类型的数据。reference类型表示对一个对象实例的引用。
    局部变量表的容量以变量槽(Variable Slot)为最小单位。Java虚拟机规范规定了一个槽应该可以存放一个32位以内的数据类型。long、double类型用2个连续变量槽存储。
  2. 操作数栈
    操作数栈即操作栈,出栈入栈流程世纪及流程:当一个方法刚刚开始执行时,其操作数栈是空的,随着方法执行和字节码指令的执行,会从局部变量表 或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者。相当于是对局部变量或参数进行计算操作得到可以使用的结果并返回。
    每个栈帧容量32位,64位数据占两个栈帧。
  3. 动态联接
    一个方法要调用其他方法,需要将这些方法的符号引用转化为其在内存地址中的直接引用,而符号引用存在于方法区中的运行时常量池;这些符号引用一部分会在类加载阶段或者第一次使用时就直接转化为直接引用,这类转化称为静态解 析。另一部分将在每次运行期间转化为直接引用,这类转化称为动态连接
  4. 方法返回
    - 正常返回
    如果当前方法正常完成,则根据当前方法返回的字节码指令,这时有可能会有返回值传递给方法调用者(调用它的方法),或者无返回值
    - 异常返回
    异常完成出口是指方法执行过程中遇到异常,并且这个异常在方法体内部没有得到处理,导致方法退出
    方法退出过程实际上就等同于把当前栈帧出栈,因此退出可以执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者的操作数栈中,调整PC计数器的值以指向方法调用指令后的下一条指令
    一般来说,方法正常退出时,调用者的PC计数值可以作为返回地址,栈帧中可能保存此计数值。而方法异常退出时,返回地址是通过异常处理器表确定的,栈帧中一般不会保存此部分信息

方法区

  • 方法存储内容
  1. 类型信息,比如Class
  2. 方法信息,比如Method(方法名称、方法参数列表、方法返回值信息)
  3. 字段信息,比如Field(字段类型,字段名称需要特殊设置才能保存的住)
  4. Code区,存储的是方法执行对应的字节码指令 方法表(方法调用的时候) 在A类的main方法中去调用B类的method1方法,是根据B类的方法表去查 找合适的方法,进行调用的。
  5. 静态变量(类变量) 运行时常量池(字符串常量池)—从class中的常量池加载而来
  6. 字面量 双引号引起来的字符串值,比如"abc" ----- 会进入字符串常量池
  7. final修饰的变量
  8. 非final修饰的变量,比如long、double、float
  9. 符号应用, 类的符号引用、方法、字段

java程序员的经历 java程序员的日常_操作数_03

  • 方法区是抽象出来的概念。 而永久代和元空间是方法区的具体实现。
  • 永久代所使用的内存区域是JVM进程所使用的区域,它的大小受整个JVM的大小所限制。元空间所使用的内存区域是物理内存区域。
  • 永久代存储的信息基本是上面方法区存储内容中的数据。JDK8以后元空间只存储类的元信息,而静态变量和运行时常量池都在堆中

Java堆被所有线程共享,在Java虚拟机启动时创建。是虚拟机管理最大的一块内存。Java堆是垃圾回收的主要区域,而且主要采用分代回收算法。堆进一步划分主要是为了更好的回收内存或更快的分配内存。堆的内存分配其实是通过垃圾收集器去实现。也就是说垃圾收集器不仅负责堆的垃圾回收,还负责堆中对象需要的内存分配

java程序员的经历 java程序员的日常_操作数_04


JVM 对象生成过程

java程序员的经历 java程序员的日常_局部变量_05


JVM 类加载过程

java程序员的经历 java程序员的日常_java_06