面试必问,JVM内存模型扫盲( 二 )

  • 它是程序控制流的指示器 , 分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成
  • 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令
  • 它是唯一一个在 JVM 规范中没有规定任何 OutOfMemoryError 情况的区域
  • 2. 虚拟机栈与程序计数器一样 , Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的 , 生命周期与线程相同 。描述的是Java方法执行的内存模型 。
    在 JVM 中 , 每当一个新的线程被创建 , 都会创建一个与之关联的私有 JVM 栈 。这个栈会随着线程的运行而进行入栈(push)和出栈(pop)操作 。它主要用于存储局部变量、操作数堆栈以及方法调用的情况 。
    JVM 栈是由一系列栈帧(Stack Frame)组成的 。每当一个方法被调用 , 一个新的栈帧就会被压入栈中 , 每当一个方法调用结束 , 一个栈帧就会被弹出栈 。每个栈帧中都包含了局部变量表、操作数栈、动态链接和方法返回地址等信息 。
    局部变量表主要存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型 , 它不等同于指针 , 可能是一个指向对象起始地址的引用指针 , 也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和 returnAddress 类型(指向了一条字节码指令的地址) 。
    操作数栈则是在执行字节码指令时用到的临时存储区 , 比如在进行算数运算时 , 操作数栈就会用来存放操作数和接收结果 。
    Java虚拟机栈可能会抛出以下异常:
    1. 如果线程请求的栈深度大于 JVM 所允许的深度 , 将抛出 StackOverflowError 。
    2. 如果 JVM 栈可以动态扩展 , 当扩展时无法申请到足够的内存 , 会抛出 OutOfMemoryError 。

    面试必问,JVM内存模型扫盲

    文章插图
    3. 本地方法栈本地方法栈(Native Method Stack)也是线程私有 , 生命周期与线程相同 。作用是与虚拟机栈类似 , 虚拟机栈是为Java 方法服务的 , 而本地方法栈是为 Native 方法服务的 。
    和虚拟机栈一样 , 本地方法栈的大小可以是固定的也可以是动态的 。如果是固定的 , 当线程请求的栈深度超过最大深度时 , 会抛出 StackOverflowError 。如果是动态的 , 并且在尝试扩展时无法申请到足够的内存 , 会抛出 OutOfMemoryError 。
    4. 堆堆(Heap)是 JVM 所管理的最大一块内存空间 , 也是所有线程共享的一块内存区域 , 在虚拟机启动时创建 。堆主要用于存储对象实例和数组 , 这也是 Java 垃圾回收器主要活动的区域 。
    在物理上 , 堆区可以处于分散的内存空间中 , 但在逻辑上它被视为连续的 。堆区在 JVM 启动时创建 , 如果堆区的空间不足 , 将会抛出 OutOfMemoryError 。
    堆分为新生代(Young Generation)和老年代(Old Generation) 。新生代又分为 Eden 区、From Survivor 区(简称 S0)、 To Survivor 区(简称 S1) 。划分这么多区域的目的是为了更好地回收内存 , 或者更快地分配内存 。
    新生代中各个区域的内存占比分别是 , Eden : S0 : S1 = 8 : 1 : 1
    新创建的对象优先在  Eden 区进行分配 。当 Eden 区满时 , 会触发一次 Minor GC(新生代垃圾回收 , 也叫 Young GC) , 将仍然存活的对象从 Eden 区和 S0 区移动到 S1 区 , 下次 Minor GC 处理情况类似 , 把存活的对象从 Eden 区和 S1 区移动到 S0 区 。当 Survivor 区也满了 , 还存活的对象会被移动到老年代 。如果老年代也满了 , 将会触发 Major GC(老年代垃圾回收 , 也叫 Old GC) 。当老年代满了 , 也可能触发 Full GC , Full GC 会对整个堆内存进行垃圾回收 , 包含新生代、老年代和方法区 。Full GC 会导致较长的停顿时间 , 并且会消耗大量的系统资源 。
    【面试必问,JVM内存模型扫盲】
    面试必问,JVM内存模型扫盲

    文章插图


    推荐阅读