Java内存区域与内存溢出异常
一. 运行时数据区域
1.1 程序计数器
用于存储线程正在执行的指令的行数,执行普通方法时存储字节码文件地址,执行本地方法时为空。
1.2 Java虚拟机栈
有一个局部变量表,存储byte, short, int, long, boolean, char, float, double八种基本数据类型和引用指针。
1.3 本地方法栈
与Java虚拟机栈功能类似,区别在于Java虚拟机栈为虚拟机运行java方法时服务,而本地方法栈为虚拟机运行本地方法时服务。
1.4 Java堆
所有的对象实例以及数组都应当在堆上分配。
各个线程共享的内存区域。
1.5 方法区
各个线程共享的内存区域(与堆一样)
用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
方法区:
https://blog.csdn.net/duoyu779553/article/details/105878755/
有存放方法信息、类型信息和域信息等。
1.6 运行时常量池
运行时常量池是方法区的一部分。
String.intern():
当调用intern方法时,如果池已经包含与equals(Object)方法确定的相当于此String对象的字符串,则返回来自池的字符串。
否则,此String对象将添加到池中,并返回对此String对象的引用。
final类变量的值在编译期间就被确定了,因此保存在类的常量池里面,然后在加载类的时候,复制进方法区的运行时常量池里面,final类变量存储在运行时常量池里面,每一个使用它的类保存着一个对其的引用。
1.7 直接内存
不是虚拟机运行时数据区的一部分,也不是《java虚拟机规范》中定义的内存区域。
操作系统设备管理DMA相关
内存不需要经过CPU直接与外围设备交换数据
二. HotSpot虚拟机对象
2.1 对象的创建
划分可用空间:
- 指针碰撞
- 空闲列表
线程安全:
- CAS配上失败重试的方法保证更新操作的原子性
- 本地线程分配缓冲(TLAB)
2.2 对象的内存布局
- 对象头
- 实例数据
- 对齐数据
2.3 对象的访问定位
- 句柄访问
- 直接指针访问
三. OOM异常
除程序计数器外,虚拟机栈、本地方法栈、Java堆、方法区都会发生OOM,不是《java虚拟机规范》定义的直接内存也会。
虚拟机栈和本地方法栈还会发生StackOverFlowError。
3.1 Java堆溢出
java.lang.OutOfMemoryError: Java heap space
判断是内存泄漏还是内存溢出
- 内存泄漏:堆中对象已死亡但无法回收
查找泄漏对象到GC Roots的引用链。- 内存溢出:堆中对象确实存活
检查Java虚拟机的堆参数设置、从代码上检查是否存在某些对象生命周期过长、持有状态时间过长、存储结构设计不合理等。
3.2 虚拟机栈和本地方法区溢出
3.3 方法区和运行时常量池溢出
3.4 本机直接内存溢出
参考文献: 深入理解Java虚拟机 第3版 周志明著