【JVM】对象创建流程

对象初始化顺序

几个理论依据

  • 对象创建前,若类未加载,会进行类加载(JVM第七章:类加载
    • static final 常量在类加载的准备阶段就完成初始化
  • 对象创建时,会先创建父类对象
  • 代码编译后会在字节码文件中生成
    • 实例构造器<init>方法,将所有的成员代码块和成员变量赋值动作按顺序收集在一起,在对象实例化中执行
    • 类构造器<cinit>方法,将所有的静态代码块和静态变量赋值动作按顺序收集在一起,在类加载过程中执行

代码示例

父类Parent

public class Parent {
    static {
        System.out.println("父类-执行-静态代码块");
    }

    static String staticA = getStaticA();

    public static String getStaticA() {
        System.out.println("父类-执行-静态方法");
        return null;
    }

    String b =  getPA();


    {
        System.out.println("父类-执行-方法块");
    }
    public String getPA() {
        System.out.println("父类-执行-方法");
        return null;
    }
    public Parent(){
        System.out.println("父类-执行-构造");

    }
}

子类Children

public class Children extends Parent{
    static {
        System.out.println("子类-执行-静态代码块");
    }

    {
        System.out.println("子类-执行-方法块");
    }
    static String staticC = getStaticA();

    public static String getStaticA() {
        System.out.println("子类-执行-静态方法");
        return null;
    }


    String c =  getA();

    public String getA() {
        System.out.println("子类-执行-方法");
        return null;
    }

    public Children(){
        System.out.println("子类-执行-构造");
    }
}

测试代码:

public class Test {
    public static void main(String[] args) {
        new Children();
        System.out.println("-----");
        // 第二次创建对象时,类已经加载过,不需要再加载,也不会执行静态方法和代码块
        new Children();
    }
}

执行结果:

父类-执行-静态代码块
父类-执行-静态方法
子类-执行-静态代码块
子类-执行-静态方法
父类-执行-方法
父类-执行-方法块
父类-执行-构造
子类-执行-方法块
子类-执行-方法
子类-执行-构造
-----
父类-执行-方法
父类-执行-方法块
父类-执行-构造
子类-执行-方法块
子类-执行-方法
子类-执行-构造

结论

结论:

  • 父类 按代码上下顺序执行 静态方法块和静态变量赋值语句
  • 子类 按代码上下顺序执行 静态方法块和静态变量赋值语句
  • 父类 按代码上下顺序执行 成员方法块和成员变量赋值
  • 父类 执行 构造方法
  • 子类 按代码上下顺序执行 成员方法块和成员变量赋值
  • 子类 执行 构造方法

参考

虚拟机中对象的创建

  1. 虚拟机遇到字节码new指令时,先检查能否在常量池中定位到类的符号引用,并检查该类是否被加载、解析、初始化过,若没有,则执行类加载过程,否则执行2
  2. 类加载检查通过后,虚拟机为新对象在分配内存(堆,小概率栈。涉及JIT、逃逸分析、垃圾收集等知识点)。对象所需内存大小在类加载完成后就可以确定。
  3. 对象初始化
  4. 栈的对象引用指向该空间地址

分配内存方式

  • 指针碰撞:在内存规整的情况下,用指针将使用过的内存和未使用过的内存分开,在分配或释放内存时移动指针。(Serial、ParNew收集器)
  • 空闲列表:在内存不规整的情况下,使用过的内存和未使用过的内存加错在一起,则虚拟机需要维护一个列表,记录哪块内存可用。(CMS)
    JAVA堆是否规整由垃圾收集器是否能空间压缩整理决定。

指针碰撞可能带来的问题:

对象的创建较为频繁,在并发情况下,在给对象A分配内存时,指针还没有来得及修改,对象B又使用原来的指针分配内存。

解决方法:

  • 对分配内存的动作同步处理,如CAS加失败重试
  • 将内存分配的动作按照线程划分在不同的空间进行,即每个线程在Java堆中预分配一块内存,成为本地线程分配缓冲,一个线程需要分配内存时先在改线程内的缓冲区内分配,本地缓冲区使用光后,分配新的缓存区才同步锁定。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值