HotSpot虚拟机对象探秘-笔记

学习目的:探讨HotSpot虚拟机在Java堆中对象分配、布局和访问的全过程。

1.对象的创建

虚拟机在执行到一条new指令时,先要检查指令的参数(将要实例化的类)是否已经被加载、解析、初始化过,如果已经走过了这3个步骤,就可以直接给新生对象分配内存;如果没有走那就必须先把类加载过程走完才行。(把类看作模板,把对象实例看作产物,没有模板我们怎么得到产物?)

如果GC(垃圾收集器)带有压缩整理功能,那么堆内存就是规整连续的,这时虚拟机采用非常非常简单的“指针碰撞”算法来为对象划出一块内存;如果GC不带压缩整理功能,因为此时堆内存不规整,所以只能采用略微复杂的“空闲列表”算法,每次都从空闲地址列表中找一块足够大的内存块划给对象实例。

虚拟机分配内存时,如果有多个线程同时操作一块内存区域,就会出现并发问题。解决方案有2个,

第一个是借助处理器的CAS操作,加上失败重试。

查了一下CAS的相关资料:CAS 指令操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”

可以这么理解,CAS指令的目的,是检查内存位置(V)的值是否变更,如果没变,就主动把它改变。相当于是分配内存失败后的弥补机制。

第二个是TLAB本地线程分配缓冲(thread local allocation buffer),每个线程在Java堆中预先分配一小块内存区域,如果要使用内存创建对象,就都在各自的一亩三分地上做,不干扰其它线程。只有当这一小块内存用完后,才同步锁定,继续预分配,以此循环。

给对象实例分配内存空间完成之后,虚拟机要把这块内存空间初始化为全零。这么做的效果就是,我们在Java代码中使用new关键字创建对象实例后,不用给对象实例的字段赋初始值就可以直接访问,贴一段代码来演示:

public class Book {
private String bookName;
private int bookPageNum;
private boolean isBookOutDate; public String getBookName() {
return bookName;
} public void setBookName(String bookName) {
this.bookName = bookName;
} public int getBookPageNum() {
return bookPageNum;
} public void setBookPageNum(int bookPageNum) {
this.bookPageNum = bookPageNum;
} public boolean isBookOutDate() {
return isBookOutDate;
} public void setBookOutDate(boolean isBookOutDate) {
this.isBookOutDate = isBookOutDate;
} }
public class Test {
public static void main(String[] args) {
System.out.println(new Book().getBookName());//输出 null
System.out.println(new Book().getBookPageNum());//输出 0
System.out.println(new Book().isBookOutDate());//输出 false
}
}

从上面的代码我们可以看出来,给对象实例Book分配内存后(它占用的内存空间被置全零),其成员属性bookName的值是null,其

String a;
System.out.println(a);//The local variable a may not have been initialized 本地变量可能还没有被初始化
int b;
System.out.println(b);//The local variable b may not have been initialized
boolean c;
System.out.println(c);//The local variable c may not have been initialized
上一篇:利用并查集+贪心解决 Hdu1232


下一篇:【Spark-core学习之三】 Spark集群搭建 & spark-shell & Master HA