创建对象的方式
代码从字节码方式看
public void static main(String[] args){
Object obj = new Object();
}
①在字节码文件中,首先new了一个#2链接的东西,在方法区的运行时常量区找到指向的是Object
②dup是复制的意思,当new了一个obj后,将obj放入操作数栈,随后复制一个也放入操作数栈,底部的用来赋值,压在上面的负责调用方法。
③invokespecial是调用Object的构造器
④将操作数栈中的obj出栈存入局部变量表
⑤放回
创建对象的步骤
1.判断对象的类是否加载连接初始化
虚拟机遇到一条new指令,首先去检查这个指令的参数能否在Metaspace的常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化。(即判断类元信息是否存\在)。如果没有,那么在双亲委派模式下,使用当前类加载器以ClassLoader+包名+类名为Key进行查找对应的.class文件。如果没有找到文件,则抛出ClassNotFoundException 异常,如果找到,则进行类加载,并生成对应的Class类对象
2.为对象分配内存
①如果内存规整
②如果不规整
选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
3.处理并发问题
①采用CAS失败重试、区域加锁保证更新的原子性
CAS是啥?
CAS是3个英文单词Compare And Swap的首字母,翻译过来就是比较并且替换。
CAS机制中使用了三个基本操作数:内存V,旧的预期值A,要修改的新值B
更新一个变量时,只有当变量的预期值A和内存地址V的实际值相同时,才会将内存地址V对应的值修改成B。
空口无凭,我们来看一个例子
从思想上来说,synchronized属于悲观锁,悲观的认为程序中的并发情况严重,所以严防死守,CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去重试更新。
在java中除了上面提到的Atomic系列类,以及Lock系列类夺得底层实现,甚至在JAVA1.6以上版本,synchronized转变为重量级锁之前,也会采用CAS机制。
②每个线程预先分配一块TLAB 通过-XX:+/-UseTLAB参数来设定
堆空间中为了防止线程的抢占调用,分配了一个线程私有的TLAB区域就可以不用采取同步也可以让并发安全了,因为只有一个线程能够操作TLAB
4.初始化分配的空间
属性的默认初始化可以保证对象在不赋值可以直接使用
5.设置对象头
6.执行init方法初始化
对象的内存布局
代码:
public class customer{
int id = 1001;
String name;
Account acct;
{
name =“匿名客户";
}
public customer(){
acct = new Account();
}
class Account{
}
public static void main(){
Customer cus = new Customer();
}
代码对应内存布局的图示
对象的访问定位
Jvm是如何通过对象引用访问到其内部的对象实例的呢
访问方式
①句柄访问
好处:reference中存储稳定句柄地址,对象被移动(垃圾收集时移动对象很普遍)时只会改变句柄中实例数据指针即可,reference本身不需要被修改。
②直接指针(hotspot采用)