在接下来的几天想总结下, JVM 相关的一些内容, 比如下面的这三个内容算是比较核心知识点了
1. 运行时数据区域: 在运行时数据区里存储类 Class 文件元数据 (方法区), 对象和数组(堆), 方法参数局部变量(栈) 等.
2. 垃圾回收机制: java 语言的优势之一就是它的自动内存管理, 主要回收运行时数据区域的堆内存里的数据
3. 类加载机制: 虚拟机首先需要把编译完成的字节码文件通过类加载器来加载到运行时数据区域
一个段 Java 代码的生命周期都会少不了上图这几个步骤, 也就是 Java 代码首先会被编译成字节码文件, 之后被类加载器加载到运行时数据区域, 以及运行, 垃圾收集器回收对象等等.
但今天我想介绍第一个知识点《运行时数据区域》
1 运行时数据区
Java 虚拟机定义了一系列逻辑数据区域, 有些是随着虚拟机的启动而创建, 虚拟机的关闭而销毁. 还有一部分是随着线程生命周期创建销毁的.
我们有必要深入了解这块的内容, 因为它将决定服务器性能, 首先我们需要对整个运行时区域由整体的认识并且了解了每个区域的生命周期以及作用之后才能通过相应的调参来提升系统性能. 除此之外还有助于快速定位虚拟机的相关 Error.
逻辑上可以划出一下 6 个区域分别是
1.1 PC 寄存器
全名叫做 Program Counter Register 既然是叫做寄存器了那么肯定是需要存东西, 那到底存的是什呢?
由于 JVM 同时可以处理多个线程所以就涉及到一些线程调度, 当 CPU 暂停运行线程 A 把时间片让给线程 B 的时候我们需要保存线程 A 被暂停执行前的一些现场状态, 需要记录当前执行到那一行字节码了, 所以具备保存现场的功能.
每条线程都有自己的 pc 寄存器, 在任意时刻虚拟机只会执行一个方法
如果执行的是方法不是 native 方法 pc 寄存器则保存指向当前执行字节码的指令地址
如果执行的是 native 方法 pc 寄存器会保存 undefined
1.2 java 虚拟机栈
虚拟机栈也是每条线程私有的区域, 里头存储栈帧(Frame), 后面会重点介绍栈帧算是重点内容. 方法的调用与返回基于栈帧来实现的.
1.3 虚拟机堆
在 Java 虚拟机中堆是所有线程都可以共享的内存区域, 是存放所有类实例和数组对象的地方. 在虚拟机启动就根据相关堆参数, 创建堆, 他也是垃圾收集器工作的主要区域.
堆内存里的对象不会被显式的回收, 而是由垃圾回收器回收
为了配合垃圾收集器的特性我们可以把堆分为年轻代和老年代
年轻代又分了 Eden 和 survivor 区, 主要是为了配合垃圾回收算法而这么搞得.
1.4 方法区和运行时常量池
在 Java 虚拟机中 方法区是可提供各个线程共享的运行时内存区域, 它存储了每一个类的结构信息, 例如运行时常量池, 字段和方法数据, 构造函数和普通函数的字节码内容, 一句话总结就是存储元数据地方
运行时常量池是 class 文件中每个类或接口常量池表的表示形式. 它包括了若干不同的常量, 比如 从编译期可知的数值字面量到运行时才能解析获得的方法或字段引用等等.
创建时机
每个运行时常量池都在 Java 虚拟机的方法区中分配, 在加载类和接口到虚拟机之后创建对应的运行时常量池
1.5 本地方法栈
如果我们想再 Java 底层里调用别的语言代码的话就需要用到别的方法栈了, 比如 Java 虚拟机的实现会用到传统的栈 (C stack) 来调用 native 方法, 这个就是本地方法栈的应用, 当然这个不是必须实现的, 完全取决于虚拟机的实现.
2 栈帧:
首先看下栈帧在虚拟机内存中在什么位置,
栈帧是用来存储数据和部分过程结果的数据结构, 同时也用来处理动态链接, 方法返回, 异常分派等工作. 栈帧的生命周期是跟方法一致的, 随着方法的调用而创建, 方法的结束或者异常而销毁.
每个栈帧都由局部变量表, 操作数栈, 动态链接组成的
2.1 局部变量表 (Local variable)
每个栈帧内部都包含一组称为局部变量表的列表, 变量表的长度在编译期决定.
一个局部变量可以存储一个基本数据类型或一个对象引用(referance),returnAddress 的数据. 存储 long 或 double 需要两个局部变量才能存储.
当虚拟机要使用局部变量表里的数据时通过索引来定位, 默认从 0 开始, 由于 long 和 double 占用两个局部变量所以它的索引较特殊, 取决于最小的那个值, 比如某个 long 类型数据在索引 n 和 n+1 里存储了, 那么它对应的索引值就是 n.
虚拟机通过局部变量表来完成方法调用时的参数传递. 如果是类方法, 它的参数依次从 0 开始的位置传递到局部变量表, 如果是实例方法则第 0 位置存储所在对象的引用(this), 从 1 开始传递参数.
2.2 操作数栈 (Operating Stack)
操作数栈是属于栈帧中的栈, 其实它的全名叫做当前栈帧的初操作数栈. 栈, 栈帧, 操作数栈的关系需要梳理清楚:
栈: 是虚拟机运行时数据区的一个逻辑区域, 里面存储了一个个栈帧.
栈帧: 栈帧代表一个方法的整个生命周期, 里头存储了局部变量表, 操作数栈, 动态链接
操作数栈: 刚刚创建时操作数栈是空的. 虚拟机提供一些指令从局部变量表把一些常量或者变量值加载到操作数栈, 也提供了从操作数栈取走数据的指令.
调用方法时操作数栈用来准备调用方法参数以及接受方法的返回结果.
2.3 动态链接 (Dynamic Linking).
动态链接是用来完成运行时绑定操作的. 在栈帧中有一个指向常量池的当前类的一个引用. 在 class 文件里一个方法要是调用其他方法或者方法其他成员变量, 则需要通过符号引用来表示.
动态链接的作用就是将符号引用转换为直接引用.
类加载的过程中将要解析尚未被解析的符号引用, 并且把对变量的访问转换为正确的偏移量.
来源: https://www.cnblogs.com/wyc1994666/p/11795781.html