Java对象创建、对象分配、对象存储布局

本文解析Java对象创建过程中的指令重排序现象,讲解内存布局(markword、classpointer、instancedata等)及对象分配策略。重点讨论栈上分配原则和类加载内存中的静态变量初始化问题。
摘要由CSDN通过智能技术生成
1.对象创建(初始化)的过程:
    以下代码是使用JClassLib插件可查看的字节码信息
    0 new #2 <java/lang/Object>
    3 dup
    4 invokespecial #1 <java/lang/Object.<init> : ()V>
    7 astore_1
    8 return

    步骤0 new来申请内存,属性会有默认值
    步骤4 调用特殊的方法构造方法
    步骤7 建立关联,引用与对象之间
    步骤8 执行结束

         对象创建的过程会涉及对象半初始化状态的问题:cpu有时为了执行效率的提升会把一些指令提前执行,也就是所谓的指令重排序,关于指令重排序的条件,我在这里不赘述了。在发生指令重排序时可能把步骤7提前执行:将对象与引用关联,那么这时候可能构造方法还没有完成,也就造成了对象的版初始化状态。
         这个现象其实在DCL中也有涉及,就是实例对象要不要加volatile的问题。DCL已经保证了多线程情况单例的安全性,但是在并发量及其大的时候获取单例,可能会出现一个半初始化的对象,所以要加volatile,其实这种情况极少发生,排查起来也无从下手。

            volatile修饰变量的作用:
                        1.禁止指令重排序
                        2.线程间可见 

2.对象在内存中的存储布局:就是new出来占据的内存空间
    第一部分markword    8字节
        作为同步监视器时会在mark work标记锁的类型
        mark work中记录的三大信息:hash值、锁信息、GC信息(对象回收的状态,年龄),
        这几个信息拎出来会涉及到java的锁机制,垃圾回收算法(三色标记算法...)
    第二部分class pointer(类指针)    4字节
        指向.class的指针
    第三部分instance data(实例数据)    引用指针4字节
        属性
    第四部分padding(对齐)
        jvm是8字节对齐,不够就加,满足求余8整除即可
        64位的虚拟机8字节。
        使用-XX:-UseCompressedClassPointer来取消类指针压缩。
        使用-XX:-UseCompressedOops取消普通类型指针压缩

3.对象怎么分配
    a.先尝试在栈上分配,只要弹栈,操作系统就可以帮你回收,栈的空间小Xss来调整栈的大小
    再栈空间分配的好处:自动弹出,不需要GC介入。
    在栈上分配的原则:
        1.可以进行标量替换的对象
        2.逃逸分析:再栈中分配的对象,当栈帧执行完毕,弹栈,对象消失,那么那个外部的
    引用就成了空指针了
    b.不满足栈分配的原则看对象大小,如果过大,直接往Old扔,如果不大往TLAB(Thread Local Allocation Buffer)
    线程本地分配缓冲区,每个线程在new出来时,线程都有一个线程的私有空间,也是在Eden区。
    有限往自己的兜里装。但始终在Eden区

    ----------------------------------------------------------------------------

        64位和32位系统指的是指针的寻址空间的位数,那为什么jvm里的引用指针为什么是4字节
    因为进行了指针压缩。32位指针的寻址空间有4G个,即4G个地址,如果一个空间是8bit那么
    就可以控制32G内存

Class载入内存

Class文件如何放入内存:
    class————    loading--->linking--->initializing    ————gc
    linking分为三步:
        verification--->preparation--->resolution
        在preparation阶段为静态变量赋默认值
    initializing指的是类的初始化

  以下代码:交换T001中两个静态属性的位置,输出有何变化?

public class T {
    public static void main(String[] args) {
        System.out.println(T001.count);
    }
}

class T001{
    public static T001 t = new T001();
    public static int count = 2;

    private T001(){
        count ++;
    }
}

在类装入内存的过程中count经历了从 0 ---> 1 ---> 2的变化,所以这个结果输出为 2

public class T {
    public static void main(String[] args) {
        System.out.println(T001.count);
    }
}

class T001{
    public static int count = 2;
    public static T001 t = new T001();

    private T001(){
        count ++;
    }
}

在类载入内存中count的值经历了 0 ---> 2 ---> 3,所以最后输出结果为 3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值