JVM学习笔记(内存分区篇)

概述

YsPiX6.jpg

线程私有区

这三个区是随着线程生命周期创建销毁的。

程序计数器

  • 与CPU中程序计数器作用相同 ,用于记录当前线程程序现在运行到哪里。
  • 基于CPU的时间片轮转运行机制,CPU并不会把一个线程中的任务做完之后才切换线程,而是在不同线程中不停切换以达到并发的效果,所以我们需要记录下当切程序运行到的指令地址,在下一次轮到该线程执行时可以继续运行。
  • Java虚拟机中的程序计数器仅仅是虚拟机中的,存在于内存之上的“虚拟”计数器。
  • 要注意的是在运行native方法, 使用的是cpu的程序计数器 JVM中的程序计数器会被定义为undefined。

可能产生的异常

  • 因为程序计数器只是存储一个定长的指令地址,所以不会有OutOfMemoryError出现

虚拟机栈 /本地方法栈

  • 把这两个区的作用大致是一样的,只是Java虚拟机栈运行的是Java方法,本地方法栈运行的是native方法。而且在很多虚拟机(比如HotSpot VM)中把这两个栈融合成所谓的“mixed stack”两种栈帧都可以储存。

  • 栈区是由栈帧组成的。当调用方法时,Java虚拟机将新建一个帧;方法退出时,帧将自动消除。

  • 线程运行过程中,只有一个栈帧是处于活跃状态,称为“当前活跃栈帧”,当前活动栈帧始终是虚拟机栈的栈顶元素。

栈帧

YsDwzF.jpg

局部变量表
  • 在编译程序代码的时候就可以确定栈帧中需要多大的局部变量表,具体大小可在编译后的 Class 文件中看到。
  • 局部变量表的容量以Variable Slot(变量槽)为最小单位,每个变量槽都可以存储 32 位长度的内存空间。
  • 在方法执行时,虚拟机使用局部变量表完成参数值到参数变量列表的传递过程的,如果执行的是实例方法,那局部变量表中第 0 位索引的 Slot 默认是用于传递方法所属对象实例的引用(在方法中可以通过关键字 this 来访问到这个隐含的参数)。
  • 其余参数则按照参数表顺序排列,占用从 1 开始的局部变量 Slot。
  • 基本类型数据以及引用和 returnAddress(返回地址)占用一个变量槽,long 和 double 需要两个。
操作数栈
  • 同样也可以在编译期确定大小。
  • Frame 被创建时,操作栈是空的。操作栈的每个项可以存放 JVM 的各种类型数据,其中 long 和 double 类型(64位数据)占用两个栈深。
  • 方法执行的过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈操作(与 Java 栈中栈帧操作类似)。
  • 操作栈调用其它有返回结果的方法时,会把结果 push 到栈上(通过操作数栈来进行参数传递)。
动态链接
  • 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接。
  • 在类加载阶段中的解析阶段会将符号引用转为直接引用,这种转化也称为静态解析。另外的一部分将在运行时转化为直接引用,这部分称为动态链接。
返回地址
  • 方法开始执行后,只有 2 种方式可以退出 :方法返回指令,异常退出。

可能产生的异常

  • 线程请求分配的栈容量>Java虚拟机最大栈容量,则JVM会抛出StackOverFlowError异常。

  • 如果Java虚拟机可动态拓展,则如果在拓展的过程中无法申请到足够的内存,就会抛出OutOfMemoryError异常

线程共享区

堆区

  • Java Heap 大多数情况下都是JVM所管理的内存中最大的一块
  • 几乎所有的对象实例都在Java Heap中分配内存(比如TLAB 不用加🔒也能保证绝对的线程安全 不过限制也很大 或者 站上分配之类的)
  • 是GC管理的主要区域(GC会另开一篇学习 具体的堆分区会放在那里)
  • Java Heap 需要处于逻辑伤的连续 物理上的是否连续不作要求

可能产生的异常

  • 如果在拓展的过程中无法申请到足够的内存,就会抛出OutOfMemoryError异常

方法区

  • 储存了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。

常量区

  • 避免频繁的创建和销毁对象而影响系统性能
  • 包含在class文件信息中 被final修饰的成员变量,包括静态变量实例变量局部变量
  • 基本类型的包装类的大部分都实现了常量池技术 除了Float和Double都包含了[-128,127]相应类型的缓存数据
  • 可设置禁止GC (其实本身就很少进行GC)
  • 持有的只是变量的指针 实际的对象还是放在堆中

运行时常量区

  • 不包含在class文件信息中
  • 这种特性被开发人员利用比较多的就是String类的**intern()**方法。(intern()方法会查找在常量池中是否存在一份equal相等的字符串,如果有则返回该字符串的引用,如果没有则添加自己的字符串进入常量池。)

可能产生的异常

  • 如果在拓展的过程中无法申请到足够的内存,就会抛出OutOfMemoryError异常