主要是阅读《深入理解Java虚拟机》 之后所做的一些笔记。
更多内容可以访问我的个人博客。
对象的创建
步骤: 类加载检查 —> 分配内存 —> 分配的内存空间初始化为零值 —> 设置对象头 —> 数据初始化
1. 类加载检查
虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析、初始化过。若没有,那必须先执行相应的类加载过程。
2. 分配内存
在类加载检查通过后,接下来虚拟机将为新生对象分配内存。选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
- 内存分配方式
① “指针碰撞” :内存绝对规整,即用过的内存放在一边,空闲内存放在一边,分配内存即把作为分界点的指针向空闲方向挪动与对象大小相等的距离即可。
② “空闲列表”: 内存不规整,已占用的内存和空闲内存交错,VM需维护一个列表,记录可用内存,分配时从内存列表找一块足够大的内存空间。
3. 分配的内存空间初始化为零值
内存分配完成后,虚拟机需将分配到的内存空间都初始化为零值(不包括对象头)——保证了对象的实例字段在Java代码中可以不赋初始值就可以直接使用
4. 设置对象头
VM对对象头中的信息进行必要的设置,例如这个对象是哪个类的实例、如何找到类的元数据信息、对象的哈希码、对象的GC分代年龄等。
5. 数据初始化
执行new指令之后会接着执行**< init >方法**,把对象按照程序员的意愿进行初始化。
对象的内存分布
对象在内存中的布局可以分为3块区域:①对象头(Head)②实例数据(Instance Data)③对齐填充(Padding)
1. 对象头:包括两个部分:
①对象自身的运行时数据:哈希码、GC分代年龄、锁状态标志、线程执有的锁、偏向线程ID、偏向时间戳等。
②类型指针(不一定有):即对象指向它的类元数据的指针。VM通过这个指针来确定这个对象是哪个类的实例。
2. 实例数据:对象真正存储的有效信息
HotSpot VM 默认的分配策略顺序为long/double、int、short/char、byte/boolean、OOPs(Ordinary Object Points)
父类中定义的变量在子类之前
若CompactField参数为true,则子类中的较短变量也可能插入到父类变量空隙中。
3. 对齐填充:不一定存在,起占位符作用
由于HotSpot VM的自动内存管理系统要求对象起始地址须是8字节的整数倍,所以需要对齐填充区域补全。
对象的访问定位
Java程序通过栈上的reference数据来操作堆上的具体对象。
由于reference类型在Java VM规范中只规定了一个指向对象的引用,并没有定义这个引用定位、访问堆中对象的方式,所以具体的对象访问方式也取决于虚拟机实现而定。
1. 使用句柄访问
Java堆中将会划分出一块内存来作为句柄池。
reference数据中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的地址。
**优点:**reference中存储的是稳定的句柄地址,在对象被移动时,只改变句柄中的实例指针,不改变reference.
2. 使用直接指针访问(HotSopt采用)
reference中存储的直接就是对象地址。
而Java堆对象的布局中必须考虑如何放置访问类型数据的相关信息。
**优点:**速度更快,节省了一次指针定位的时间开销。
JVM堆栈溢出
1. Java堆溢出——OutOfMemoryError
方法:不断创建对象,并保证GC Roots到对象之间有可达路径,以避免垃圾回收。
虚拟机参数: —Xms:堆的最小值 —Xmx:堆的最大值
2. 虚拟机栈和本地方法栈溢出
①线程请求的栈深度大于虚拟机所允许的最大深度,抛StackOverFlowError.
②虚拟机扩展栈时无法申请到足够的内存空间,抛OutOfMemoryError.
方法:
减少栈容量
定义大量本地变量
建立多线程,为每个线程申请栈区
虚拟机参数: —Xss:栈的最小值 —Xsx:栈的最大值
3. 方法区和运行时常量池溢出
方法区:产生大量的类
运行时常量池:向常量池添加大量常量(String.intern())
- 关于String.intern()方法:是一个Native方法,作用是如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象。否则,将此对象包含的字符串添加到常量池中,并返回引用。
public class RuntimeConstantPoolOOM{
public static void main(String[] args){
public static void main(String[] args){
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1);
String str2 = new StringBuilder("ja").append("va").toString();
Ststem.out.println(str2.intern() == str2);
}
}
}