文章目录
2.2 运行时数据区域
- Java 虚拟机运行时数据区域
名称 | |
---|---|
程序计数器 | 1.当前线程所执行的字节码行号执行器;2.线程私有 |
Java虚拟机栈 | 1.线程私有;2.描述Java方法执行的内存模型;3.每个方法在执行的同时创建一个栈帧来存储局部变量表(基本数据类型、对象引用)、操作数栈、动态链接、方法出口等;4.方法执行看成栈帧入栈出栈过程;5.请求栈深度大于虚拟机允许深度StackOverflowError;6.无法申请足够空间OutOfMemoryError |
本地方法栈 | 1.虚拟机使用到的 Native 方法 |
Java堆 | 1.所管理内存最大一块;2.线程共享;3.存放对象的实例;4.所有对象实例和数组都在堆上分配,并不是绝对;5.又称GC堆;6.分代回收算法;7.新生代、老年代;8.Eden、From Survivor[sərˈvaɪvər]、To Survivor;9.堆中没有内存或对无法扩展OutOfMemoryError |
方法区 | 1.线程共享;2.存储已被加载类信息、常量、静态变量、即时编译器编译后的代码等数据 ;3.别名,Non-Heap(非堆);4.可以选择不实现垃圾收集,5.OutOfMemoryError |
运行时常量池 | 1.是方法区一部分;2.Class文件一部分信息存放,编译器生成的各种字面量和符号引用;3.具备动态性;4.OutOfMemoryError |
直接内存 | 1.不是虚拟机运行时数据区一部分,不是Java虚拟机规范定义内存区域;2.JDK1.4新加入NIO,基于通道(Channel)与缓冲区(Buffer)I/O方式,Navite函数库直接分配堆外内存,通过Java堆中 DirectByteBuffer对象作为内存引用,避免Java堆和Native来回复制数据;3.OutOfMemoryError |
2.3.1 对象的创建
- 内存分配
1.指针碰撞(指针移动)
2.空闲列表(维护一个列表) - 并发问题
1.对分配内存空间的动作进行同步处理,虚拟机采用的CAS配上失败重试的方式保证原子性
2.内存分配动作安卓线程划分不同的空间之中进行,本地线程分配缓冲(TLAB) - Java对象创建
1.类加载检查
2.类加载
3.分配内存
4.内存空间初始化为零值
5.对对象进行设置(信息存放在对象头)
6.执行init方法
2.3.2 对象的内存布局
- HotSpot 虚拟机中,对象内存布局分为三块区域
1.对象头(Header)
2.实例数据(Instance Data)
3.对齐填充(Padding)(并不是必然存在) - 对象头
存储内容 | 标志位 | 状态 |
---|---|---|
对象哈希码、对象分代年龄 | 01 | 未锁定 |
指向锁记录指针 | 00 | 轻量级锁定 |
指向重量级锁的指针 | 10 | 膨胀(重量级锁定) |
空,不需要记录信息 | 11 | GC标志 |
偏向锁ID、偏向时间戳、对象分代年龄 | 01 | 可偏向 |
2.3.3 对象的访问定位
-
访问方式分为两种
1.句柄池
2.直接指针 -
句柄池
1.Java堆划分一块内存作为句柄池
2.reference中存储对象的句柄(包含对象实例数据和类型数据的具体地址信息)地址。
-
直接指针
1.reference存储直接就是对象地址
方式 | 优缺点 |
---|---|
句柄池 | 1.reference存储稳定的句柄地址,对象移动,只会改变句柄中的实例数据指针,本身不需要修改 |
直接指针 | 1.速度快,节省一次指针定位时间开销 |
2.4 实战:OutOfMemoryError 异常
- 除了程序计数器外,其他运行时区域都有发生的可能。
2.4.1 Java 堆溢出
/*VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*-Xms 堆最小值
*-Xmx 堆最大值
*-XX:+HeapDumpOnOutOfMemoryError Dump 出当前内存堆转存快照
*/
public class HeapOOM {
static class OOMObject {}
public static void main(String[] args){
List<OOMObject> list = new ArrayList<>();
while(true) {
list.add(new OOMObject());
}
}
}
- 内存溢出:内存不够了。
- 内存泄露:使用完,没及时回收。
2.4.2 虚拟机栈和本地方法栈溢出
- 使用 -Xss 参数减少栈内存容量
- 单线程操作,结论
1.使用 -Xss 参数减少栈内存容量,StackOverflowError
2.定义大量本地变量,增大此方法帧中本地变量表的长度,StackOverflowError
2.4.3 方法区和运行时常量池溢出
- String.intern():是一个Native 方法,如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象,否则,将此对象包含的字符串添加到常量池中,并且返回此String对象的引用。
- 例子
public void test() {
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1);
//1.6 false inter() 首次遇到字符串实例复制到永久代中,返回这个字符串实例引用,而 StringBuilder 创建字符串实例在 Java 堆上,不是同一个引用
//1.7 true 不会复制实例,intern() 和 StringBuilder 是同一个
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2);
//1.6 false
//1.7 false 不符合首次出现
}
2.4.4 本地直接内存溢出
- DirectMemory 容量可通过 -XX:MaxDirectMemorySize 指定,不指定,默认与 Java 堆最大值(-Xmx指定)一样。
- 发生 OOM 之后 Dump 文件很小,程序有直接或间接使用 NIO