粗略笔记
1. 虚拟机介绍
虚拟机家族
Sun Classic
HostSPot VM
经典虚拟机
Mobile / Embedded VM
移动端/嵌入式使用
BEA JRockit / IBM J9 VM
BEA/IBM公司自有虚拟机
...
2.内存区域与OOM
内存区域
java内存区域遵循《JAVA虚拟机规范》并由具体虚拟机进行划分和实现
程序计数器
占用空间极小,用于指示执行时字节码行号 各线程程序计数器私有,不存在OOM异常
java虚拟机栈
描述的是JAVA方法执行的线程内存模型,方法执行时构造栈帧
栈帧包含:
- 局部变量表
- 操作数栈
- 动态连接
- 方法出口
hotSpot的虚拟机栈是不可以动态扩展的 当线程申请栈空间失败时,会出现OOM异常
可能出现的异常:
- 线程请求时的栈深度大于虚拟机所允许的最大深度, 抛出StackOverflowError
- 如栈容量动态扩展,当无法申请到组后内存时,排除OOM Error
本地方法栈
为虚拟机使用到的本地方法(Native)服务 抛出异常与虚拟机栈相同
JAVA堆
虚拟机锁管理的内存中最大的一块区域,被所用线程共享、虚拟机启动时创建,用于存放对象实例(“所有的对象实例以及数组都应当在堆上分配”) JAVA堆也是垃圾收集器管理的内存区域, 从回收内存角度,和垃圾分代收集理论,堆场被分配新生/老年/永久、eden/from/to等。HotSpot内部垃圾收集器基于经典分代设计。 从分配内存角度,所有线程共享的java堆可以划分出多个线程私有的分配缓冲区(TLAB)以提高对象分配的效率 - 经典分代 即新生代(包含一个eden区和两个survivor区)、老年代划分
JAVA方法区
各个线程共享的内存区域,主要用于存放被虚拟机加载的类型信息、常量、静态变量、即时编译后的代码缓存等 《JAVA虚拟机规范》中方法区的描述是堆的一块逻辑部分 jdk1.8以前,HotSpot虚拟机的方法区的实现称为永久代,部分数据占用堆内存 1.8及之后,HotSpot虚拟机方法区的实现称为元空间,使用本地内存
在方法区的垃圾回收主要目的是针对常量池的回收和对类型的卸载,类型的未完全回收有概率引发内存泄漏,从而造成OOM异常
对象的创建
- 方法区检查Class类是否被加载,如果没被加载,则进行类文件加载
- 读取Class类元数据,分配堆空间给新对象
- 堆空间规整:指针碰撞
- 堆空间杂乱:空间列表
- 多线程并发:TLAB OR CAS+失败重试
- 分配的内存空间归零处理,
- 根据类元空间设置对象头信息,
- 调用
<init>()
方法,完成对象资源的初始化
对象构成: [对象头] + [实例数据] + [对齐填充]
对象头
针对于HotSpot虚拟机,对象头由动态数据结构(Mark work)和类型指针组成 - 动态数据结构 一个有着动态定义的数据结构,根据对象的状态复用自己的存储空间 用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标识、偏向锁线程ID、偏向锁时间戳等等 数据长度:32比特(32位系统)、64比特(64位系统)
Mark work
- 对象哈希码(25bit)
- 对象分代年龄(4bit)
- 锁标志位(2bit)
-
固定0(1bit)
-
类型指针 对象指向它的类型元数据的指针,java通过这个指针来确定该对象是哪个类的实例
如果对象是一个java数组,那么在对象头中还必须有一块用于记录数组长度的数据。
对象的访问定位
- 使用句柄池间接访问堆中对象
- 使用直接指针指向堆中对象
调试参数
用于堆
设置堆最大值-Xms
设置堆最小值-Xmx
内存溢出时Dump出当前内存堆转储快照-XX:+HeapDumpOnOutOfMemoryError
虚拟机栈
设置栈容量-Xss
用于方法区
<=jdk6
设置永久代最小-XX:PermSize
>= jdk7
设置永久代最大-XX:MaxPermSize
>=jdk8
设置元空间最大值-XX:MaxMetaspaceSize
设置元空间初试大小-XX:MetaspaceSize
控制垃圾收集后元空间最小剩余容量百分比-XX:MinMetaspaceFreeRatio