介绍JAVA虚拟机的运行时数据区域
按照物理结构来划分:java虚拟机主要由以下几部分构成栈、堆和程序计数器,其中栈又可以分为虚拟机栈VM stack 和 本地方法栈 Native Method Statck,堆可以划分方法区和普通的堆内存。按照逻辑划分线程私有空间和线程共线空间。
虚拟机栈、本地方法栈、程序计数器都是属于线程私有的,生命周期随着线程的创建和销毁。
方法区、堆内存属于线程共线空间,后续主要是通过垃圾回收机制来管理内存的。
(1)Java虚拟机栈
线程私有的,用于存储局部变量表、操作数栈、动态链接、方法出口,其中局部变量表存放的是基本数据类型和引用数据类型的Reference。一个槽占32bit,基本类型占用一个槽位,long和double占用两个槽位。
注意:数组变量也是存储在虚拟机占里面的,如果开辟的数组变量内存过大,可能会引起*Error。
导致虚拟机栈溢出的情况如下:
- 检查递归方法,确保有终止条件并且在合理的时间内结束递归。
- 减少方法内部的局部变量数量,特别是大的数组或集合,可以考虑使用全局静态变量。
- 增加虚拟机栈的大小。可以通过JVM启动参数-Xss来调整,例如-Xsslm将栈大小设置为1M。
- 单个线程请求栈深度太深导致的,可以尝试将算法改为迭代形式,减少栈的使用。
(2)程序计数器
意义上就是一个程序控制流的指示器,主要用于在线程轮流切换的时候,能够恢复到正确的执行位置,每个线程都有自己独立的程序计数器,各条线程之间计数器互不影响,独立存储。
(3)本地方法栈
和虚拟机栈作用一样,区别虚拟机栈服务于Java方法,本地方法栈则是服务于本地(Native)方法。
(4)Java堆
虚拟机所管理的内存中最大的一块,也是GC主要回收的管理内存的部分。“GC”采用粉带收集理论设计的,把Java堆划分不同的年龄段“新生代”、“老年代”。“永久代”、其中新生代有划分为“Eden空间”、“From Survivor空间”、“To Survivor空间”。
(5)方法区
主要解析Class文件之后,存放类型信息、常量、静态变量、字段、方法、接口。其中运行时常量池(Runtime Constant Pool)也是方法区的一部分,主要用于存放编译期生成的各种字面量与符号引用。
(6)直接内存
NIO(New Input/Output)类,引入了一种基于通道(channel)于缓冲器(Buffer)的I/O方式,它可以使用Navtive函数库直接分配堆外内存。