JVM的组成
运行时数据区-总览
Java虚拟机在运行Java程序过程中管理的内存区域,称之为运行时数据区。
《Java虚拟机规范》中规定了每一部分的作用
运行时数据区-应用场景
Java的内存分成哪几部分?
Java内存中哪些部分会内存溢出?
JDK7 和JDK8中在内存结构上的区别是什么?
内存调优
一、运行时数据区-程序计数器
程序计数器(Program Counter Register)也叫PC寄存器,每个线程会通过程序计数器记录当前要执行的字节码指令的地址。
作用
1、可以控制程序指令的进行,实现分支、跳转、异常等逻辑。
2、在多线程执行情况下,Java虚拟机需要通过程序计数器记录CPU切换前解释执行到哪一句指令并继续解释运行。
程序计数器在运行时会出现内存溢出吗?
内存溢 出指的是程序在使用某一块内存区域时,存放的数据需要占用的内存大小超过了虚拟机能提供的内存上限。
因为每个线程只存储一个固定长度的内存地址,程序计数器是不会发生内存溢出的。
程序员无需对程序计数器做任何处理。
二、栈
Java虚拟机栈和本地方法栈。:Java虚拟机栈存储了Java方法调用时的栈帧,而本地方法栈存储的是native本地方法的栈帧。
Java虚拟机栈用来保存java中实现的方法,每次请求方法都会往栈里进行保存。而本地方法栈存储的是native本地方法的栈帧。
采用栈的数据结构来管理方法调用中的基本数据,先进后出(First In Last Out),每一个方法的调用使用一个栈帧(Stack Frame)来保存。
Java虚拟机栈随着线程的创建而创建,而回收则会在线程的销毁时进行。由于方法可能会在不同线程中执行,每个线程都会包含一个自己的虚拟机栈。
局部变量表的作用 是在方法执行过程中存放所有的局部变量。编译成字节码文件时就可以确定局部变量表的内容。
操作数栈 是栈帧中虚拟机在执行指令过程中用来存放中间数据的一块区域。他是一种栈式的数据结构,如果一条指令将一个值压入操作数栈,则后面的指令可以弹出并使用该值。
在编译期就可以确定操作数栈的最大深度,从而在执行时正确的分配内存大小。
帧数据 当前类的字节码指令引用了其他类的属性或者方法时,需要将符号引用(编号)转换成对应的运行时常量池中的内存地址。动态链接就保存了编号到运行时常量池的内存地址的映射关系。
方法出口指的是方法在正确或异常结束时,当前栈帧会被弹出,同时程序计数器应该指向上一个栈帧中的下一条指令的地址。所以在当前栈帧中,需要存储此方法出口的地址。
异常表存放的是代码中异常的处理信息,包含了异常捕获的生效范围以及异常发生后跳转到的字节码指令位置。
Java虚拟机栈-栈内存溢出
Java虚拟机栈如果栈帧过多,占用内存超过栈内存可以分配的最大大小就会出现内存溢出。
Java虚拟机栈内存溢出时会出现StackOverFlowError的错误。
Java虚拟机栈-默认大小
如果我们不指定栈的大小,JVM将创建一个具有默认大小的栈。大小取决于操作系统和计算机的体系结构。
要修改Java虚拟机栈的大小,可以使用虚拟机参数 -Xss
语法: -Xss栈大小
单位: 字节(默认,必须是1024的倍数)、k或者K(KB)、m或者M(MB)、g或者G(GB)。
-Xss1024k,
Java虚拟机栈-注意事项
1、HotSpot JVM对栈大小的最大值和最小值有要求:
比如测试如下两个参数:
-Xss1k
-Xss1025m
Windows(64位)下的JDK8测试最小值为180k,最大值为1024m。
2、局部变量过多、操作数栈深度过大也会影响栈内存的大小。
一般情况下,工作中即便使用了递归进行操作,栈的深度最多也只能到几百,不会出现栈的溢出。所以此参数可以手动指定为-Xss256k节省内存。
本地方法栈
Java虚拟机栈存储了Java方法调用时的栈帧,而本地方法栈存储的时native本地方法的栈帧。
在HotSpot虚拟机中,Java虚拟机栈和本地方法栈实现上使用了同一个栈空间。本地方法栈会在栈内存上生成一个栈帧,临时保存方法的参数同时方便出现异常时也把本地方法的栈信息打印出来。