java内存区域与内存溢出异常
java内存区域与内存溢出异常
运行时数据区
程序计数器/PC寄存器
- 当前线程所执行的字节码的行号指示器
- 每条线程都是独立的,独立存储,线程私有内存
- 当线程执行一个java方法时,记录正在执行的虚拟机字节码指令地址. 若为native方法,则为空
- 唯一一个java虚拟机中没有规定OOM情况的区域
虚拟机栈/方法栈
-
每个方法在执行时都会创建一个栈帧用于存放局部变量表,操作数栈,动态链接,方法出口等信息.每一个方法从调用到完成对应这一个栈帧在虚拟机中的入栈到出栈
- 局部变量表:存放编译期可知的基本类型,对象的引用(引用指针,对象句柄或其它与对象相关的位置信息 非对象本身),returnAddress类型(指向了一条字节码指令的地址)
64位(long,double)会占用2个局部变量空间(slot),其余占用一个
在方法运行时不会改变大小
- 线程私有的,生命周期与线程保持一致
- 常说的栈内存指的就是虚拟机栈
- 异常信息:
StackOverFlowError:线程请求的栈深度大于虚拟机允许的栈深度
OutOfMemoryError:虚拟机可动态扩展,但扩展时无法申请到足够内存
本地方法栈
- 与虚拟机栈基本一致
区别:
仅保存Native方法信息
java堆/GC堆
-
存放对象实例
-
被所有线程共享,虚拟机启动时创建
- 共享总大小内存,对于每一个线程可能划分出私有的分配缓冲区
-
垃圾管理器主要区域,所以也被称为GC堆
- 因此分出年轻代(eden,survivor),老年代(old) 1:1:8
-
-Xms -Xmx
-
OOM
方法区(Non-Heap)
-
用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码
-
所有线程共享
-
在HotSpot也被称为永久代
- 在JDK1.7版本的HotSpot中已将放在永久代的字符串常量池移出
-
方法区可以不实现垃圾收集
-
OOM
运行时常量池
-
方法区 的一部分
-
用于存放编译期生成的字面量,符号引用,直接引用,当类加载完毕后加入
-
具备动态性,运行期间也可将新的常量放入池中, String.intern()
-
String.intern()
- jDK1.6 会将实例复制到永久代,并返回这个复制的引用
- JDK1.7 不在复制实例,仅在常量池中记录首次出现的实例引用
- String str = “java”;
String str2 = new String(“java”);
boolean flag = str == str2 ;
flag 是false
new String() 创建一个对象并在堆上,str2保存堆对象地址
“java“str保存的是常量池中的java地址;
-
-
OOM
直接内存
- 不属于运行时数据区,也不是java虚拟机规范中定义的内存区域
- OOM
对象
对象的创建
-
1.类加载检查. 先查询参数是否在常量池中有一个类的符号引用, 检查这个符号引用代表的类是否已经被加载,解析 初始化过,如果没有 ,则需要进行相应的类加载过程
-
- 虚拟机分配内存,并初始化为0(不包含对象头)
-
2.1 指针碰撞,要求java堆中内存绝对规整
-
2.2 空闲列表
-
2.3 线程安全问题
- 2.3.1 采用CAS和失败重试机制保证更新的原子性
- 2.3.2 本地线程分配缓冲(TLAB)
-
3.虚拟机对对象进行必要设置,实例信息,元数据信息,对象hash码,GC分代信息等,这些信息在对象头中
-
执行init方法 代码:P45
对象的内存布局
-
对象头
-
运行时数据 Mark Word
- HashCode,GC分代年龄,锁状态标示,线程持有的锁,偏向线程ID,偏向时间戳
- 32位:对象处于未锁定状态 25bit HashCode 4bit 分代年龄 2bit 锁标志位 1bit 固定0
-
类型指针
- 对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象时那个类的实例
-
-
实例数据
- 存储对象的有效信息,定义的字段内容,受到java编写顺序和虚拟机分配策略参数影响
-
对齐填充
- 非必然存在,仅仅是占位符的作用
对象的访问定位
-
通过栈中的reference数据来操作堆中的具体对象
-
直接指针 reference 中直接保存对象地址
SUN HotSpot 采用直接指针- 实例数据
- 类型数据指针
-
使用句柄 reference中保存句柄地址
-
在堆中维护一个句柄池
- 实例数据指针
- 类型数据指针
-
-
OOM
参数设置: -verbose:gc -Xms20M -Xmx20m -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8
-
java堆溢出
-
java heap space 会有dump文件 ,通过分析dump文件确定是溢出还是泄漏
- 泄漏: 查看GC roots 的引用链定位泄漏代码
- 溢出:调整物理机内存大小,查看对象的生命周期是否过长,持有状态过长, 减少程序运行期间的内存消耗
-
-
虚拟机栈和本地方法栈溢出
-
单线程
- 仅会抛出StackOverflowError
-
大小: 操作系统分配进程内存2G(32位 win) – Xmx(最大堆容量) – MaxPermSize(方法区) 剩余部分可大致认为是其大小
程序计数器忽略,忽略消耗- 所以当无法减少运行的线程数和无法换成64位机器时,可以考虑减少堆内存和方法区的内存大小来达到提升栈内存
-
-
方法区和运行时常量池溢出
- 动态生成大量的class时有可能发生,常见的有:动态生成大量JSP文件,基于OSGi的应用
-
本机直接内存溢出
- 通过-XX: MaxDirectMemorySize指定,默认与堆最大值一致(-Xmx)
- Heap Dump 文件中不会看见明显的异常,若OOM后Dump文件很小,程序中直接或间接使用了NIO则可能出现