Jvm 内存管理
程序计数器
- 程序计数器是一块比较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器
- 程序计数器属于线程独占区
- 如果线程执行的是 java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址,如果执行的是native方法,这个计数器的值为undefined
- 程序计数器是 Java 虚拟机中唯一没有OutOfMemoryError( 内存溢出 )的区域
Java虚拟机栈
- 虚拟机栈描述的是Java方法执行的动态内存模型
- 栈帧
- 每个方法执行,都会创建一个栈帧,伴随着方法从创建到执行完成。方法执行完毕,栈帧销毁。
- 在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧,与这个栈帧相关联的方法称为当前方法,执行引擎运行的所有字节码指令都只针对当前栈帧进行操作。
- 线程私有、先进先出
- 用于存储局部变量表、操作数栈、动态链接、方法返回地址、附加信息
- 局部变量表
- 存放方法参数和方法内的局部变量
- 在java程序编译为字节码文件时,就在方法的Code属性的max_locals数据项中确定了需要分配的局部变量表的最大容量。
- 局部变量表的存储单位是变量槽 Variable Slot,每个槽存放的容量是32字节,(针对double、long这样的64字节的基本数据类型, 用两个槽来存放,因此在编译的时候是可以算出max_locals的值)
- 我们一般说的栈中对象引用就是存到这个栈帧中的局部变量表里的,这里的引用指的是局部变量的对象引用,而不是成员变量的引用。成员变量的对象引用是存储在堆(heap)中
- 操作数栈
- 方法的执行操作在操作数栈中完成,每一个字节码指令往操作数栈进行写入和提取的过程,就是入栈和出栈的过程。
- 我们所说的栈深度,其实就是指的这个操作数栈的深度,每个32位数据类型占用的栈深度为1,64位为2。
- 同局部变量表一样,栈的最大深度(max_stacks),在编译的时候就在方法的Code属性max_stacks中确定了要分配的最大深度。
- 当在方法执行时,操作数栈的深度超过了max_stacks,(比如一直while(true) int a = 1),这时会有两种情况:
如果max_stacks允许扩展,则扩大最大深度,当虚拟机在扩展栈时无法申请到足够的空间,会抛出OutOfMemoryError;如果不允许,则会抛出StackOverFlowException;
- 动态连接
- 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程的动态连接。
- 静态解析:常量池中的符号引用,一部分会在类加载阶段或第一是时间的时候就转化为直接引用。
- 动态连接:另一部分在每一个运行期间转化为直接引用,这个过程称作动态连接。
- 方法返回地址
- 方法退出的方式分两种:正常完成出口和异常完成出口
- 正常完成出口:执行引擎执行任意一个方法返回(如:return)的字节码指令,这时候会可能会有返回值返回给方法的调用者。一般来说,正常退出时,调用者的PC计数器的值可以作为返回地址。
- 异常完成出口:在方法执行的过程中遇到了异常,且没有在方法体中进行处理。异常完成出口退出时,不会给上层调用者任何返回值。
- 方法退出实际上等同于当前栈帧出栈,因此一般过程为:
- 恢复上层方法的局部变量表和操作数栈。
- 把返回值压入调用者栈帧的操作数栈中
- 调整PC计数器的值以指向后面一条指令。
- 附加信息
- 虚拟机实现的一些不在规范中的信息保存到栈帧中
一般把动态连接,方法返回地址和附加信息统称为栈帧信息。
本地方法栈
- 与虚拟机栈非常类似,区别在于虚拟机栈为Java方法服务,本地方法栈为native方法服务
- 线程私有、先进先出
- 虚拟机规范中对本地方法栈中的方法使用语言、使用方式与数据结构并没有做强制规定,有些虚拟机(hotspot)直接将本地方法栈和虚拟机栈合二为一
- 与虚拟机栈一样,会抛出StackOverFlowError 和 OutOfMemoryError
Java 堆
- 存储对象实例 (只有方法内部的、并且对象没有逃逸,作用域只在方法内部的对象存放在栈中)
- 垃圾收集的主要区域
- 新生代(eden、servivorTo、servivorFrom)、老年代(tenured space)
- 会抛出OutOfMemoryError
- jvm参数设置
- -Xms 为jvm启动时分配的内存,比如-Xms200m,表示分配200M
- -Xmx 为jvm运行过程中分配的最大内存,比如-Xms500m,表示jvm进程最多只能够占用500M内存
- -Xss 为jvm启动的每个线程分配的内存大小,默认JDK1.4中是256K,JDK1.5+中是1M
方法区
- 存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
- 类的版本
- 字段
- 方法
- 接口
- jdk8以后 永久代被元内存替代
- 垃圾回收在方法区的行为比较少
- 异常的定义 OutOfMemoryError
运行时常量池(属于方法区)
- Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table)
- 存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
- 参考文章地址:https://www.cnblogs.com/xiaotian15/p/6971353.html
未完待续…
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/83676.html