JVM内存模型

JVM内存结构

  • (线程)共享内存
    • 方法区(JDK1.7 永久代(位于虚拟机数据区) -> JDK1.8 元数据区(位于本地内存))
  • (线程)私有内存
    • JVM虚拟机栈
    • 本地方法栈
    • 程序计数器PC

程序计数器(PC)

  • 定义: 当前线程正在执行的字节码指令的地址。若线程正在执行的是本地方法,则为Undefined

  • 作用:

    • 字节码解释器通过改变程序计数器依次读取指令,实现代码的流程控制
    • 记录当前线程执行位置,方便线程的上下文切换
  • 特点:

    • 内存空间占用小
    • 不会出现内存溢出错误
    • 线程私有,随线程的创建而创建、销毁而销毁

虚拟机栈(java栈)

  • 定义:

    • 记录java方法运行过程的内存模型
    • 栈帧为单位为每个方法创建一个内存区域:
      • 局部变量表
      • 操作数栈
      • 动态链接
      • 方法出口信息
    • 由于Java 虚拟机栈是与线程对应的,数据不是线程共享的,因此不用关心数据一致性问题,也不会存在同步锁的问题。
  • 压栈出栈过程

    • 虚拟机栈栈顶的栈帧是正在执行的活动栈,表示当前正在执行的方法,PC寄存器会指向这个地址
    • 当前方法内部创建局部变量时,会将局部变量的值存入栈帧的局部变量表中
    • 当前方法调用新的方法时,会新建栈帧压入栈顶,同上述步骤
    • 当前方法退出时,若存在返回值则会进入新栈帧的操作数栈中;若无返回值则无变化。
  • 特点:

    • 局部变量表的大小在编译时确定并分配内存,运行过程中不会变化
    • 会出现两种异常:
      • StackOverFlowError 请求栈的深度超过当前 Java 虚拟机栈的最大深度时
      • OutOfMemoryError 线程请求栈时内存用完了

本地方法栈(C栈)

  • 定义:
    • 本地方法栈是描述本地方法运行过程的内存模型, 很多 Native 方法都是用 C 语言实现的。

  • 定义: 堆是用来存放对象的内存空间

  • 特点:

    • 线程共享,整个JVM只有一个堆
    • 虚拟机创建时启动,是垃圾回收的主要场所
    • 可分为:
      • 新生代(Eden)——From Survior、To Survivor
      • 老年代
    • Java 堆所使用的内存不需要保证是连续的。而由于堆是被所有线程共享的,所以对它的访问需要注意同步问题,方法和对应的属性都需要保证一致性。

方法区

  • 定义: Java 虚拟机规范中定义方法区是堆的一个逻辑部分。方法区存放以下信息

    • 已加载类信息
    • 常量
    • 静态变量
    • 即时编译器编译后的代码
  • 特点:

    • 线程共享(同堆一样)
    • 存储一些长期存在不会发生频繁变动的信息,也被称为老年代/元数据区
    • 该区域垃圾回收效率低,可以通过设置不允许垃圾回收。主要的回收目标是: 常量池、类的卸载
  • 运行时常量池:

    • 常量
    • 静态变量
    • 加载的类信息
    • 即时编译代码

直接内存(堆外内存)

  • 定义: 独立于java虚拟机数据区外的内存空间

  • 操作直接内存:
    在 NIO 中引入了一种基于通道和缓冲的 IO 方式。它可以通过调用本地方法直接分配 Java 虚拟机之外的内存,然后通过一个存储在堆中的DirectByteBuffer对象直接操作该内存,而无须先将外部内存中的数据复制到堆中再进行操作,从而提高了数据操作的效率

  • 与堆内存比较:

    • 直接内存申请空间耗费更高的性能
    • 直接内存读取 IO 的性能要优于普通的堆内存。
    • 直接内存作用链: 本地 IO -> 直接内存 -> 本地 IO
    • 堆内存作用链:本地 IO -> 直接内存 -> 非直接内存 -> 直接内存 -> 本地 IO
原文地址:https://www.cnblogs.com/CodeSPA/p/13561738.html