new 一个对象时 虚拟机干了啥

1、相应类加载检查过程
 Java程序中的“new”操作会转换为Class文件中方法的“new”字节码指令。

       JVM(本文特指HotSpot)遇到new指令时,先检查指令参数是否能在常量池中定位到一个类的符号引用:

       (A)、如果能定位到,检查这个符号引用代表的类是否已被加载、解析和初始化过;

       (B)、如果不能定位到,或没有检查到,就先执行相应的类加载过程;首先通过类加载器将类class文件,加载到内存方法区,并且会创建相应的java.lang.class类对象,虚拟机还必须以某种方式把这个class对象和存储在方法区的类型数据关联起来。

         - 验证阶段:检验类的结构是否正确 
         - 准备阶段:对类的变量进行分配内存,并默认初始化 
         - 解析阶段:将二进制文件的符号引用(任何形式的字面值)解析为直接引用

2、为对象分配内存
      对象所需内存的大小在类加载完成后便完全确定(JVM可以通过普通Java对象的类元数据信息确定对象大小);

      为对象分配内存相当于把一块确定大小的内存从Java堆里划分出来;

(A)、分配方式:

(I)、指针碰撞

      如果Java堆是绝对规整的:一边是用过的内存,一边是空闲的内存,中间一个指针作为边界指示器;

      分配内存只需向空闲那边移动指针,这种分配方式称为"指针碰撞"(Bump the Pointer);

(II)、空闲列表

      如果Java堆不是规整的:用过的和空闲的内存相互交错;

      需要维护一个列表,记录哪些内存可用;

      分配内存时查表找到一个足够大的内存,并更新列表,这种分配方式称为"空闲列表"(Free List);

      Java堆是否规整由JVM采用的垃圾收集器是否带有压缩功能决定的;

      所以,使用Serial、ParNew等带Compact过程的收集器时,JVM采用指针碰撞方式分配内存;而使用CMS这种基于标记-清除(Mark-Sweep)算法的收集器时,采用空闲列表方式;

      后面再介绍垃圾收集算法和垃圾收集器,了解垃圾收集时应注意这里的内容;

(B)、线程安全问题

      并发时,上面两种方式分配内存的操作都不是线程安全的,有两种解决方案:

(I)、同步处理

      对分配内存的动作进行同步处理:

      JVM采用CAS(Compare and Swap)机制加上失败重试的方式,保证更新操作的原子性;

      CAS:有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做;

(II)、本地线程分配缓冲区

      把分配内存的动作按照线程划分在不同的空间中进行:

      在每个线程在Java堆预先分配一小块内存,称为本地线程分配缓冲区(Thread Local Allocation Buffer,TLAB);

      哪个线程需要分配内存就从哪个线程的TLAB上分配;

      只有TLAB用完需要分配新的TLAB时,才需要同步处理;

JVM通过"-XX:+/-UseTLAB"指定是否使用TLAB;

3、对象内存初始化为零
      对象内存初始化为零,但不包括对象头; 如果使用TLAB,提前至分配TLAB时;

      这保证了程序中对象(及实例变量)不显式初始赋零值,程序也能访问到零值;

      对象的成员(变量)先进行默认初始化;基本类型为基本类型默认值,引用类型为null,即引用变量的引用地址存放在栈(          stack)内存中,对象的成员变量及值存放在堆内存中 ;如果new对象有引用变量指向它,栈内存存放引用变量指向null对象(未初始化,默认为null)

4、对对象进行必要的设置
      主要设置对象头信息,包括类元数据引用、对象的哈希码、对象的GC分代年龄等(详见下节);

      对象成员初始化,对栈内存中的成员变量指定值; 
      第一步显式初始化; 
      第二步构造代码块初始化; 

5、执行对象实例方法<init>
      该方法把对象(实例变量)按照程序中定义的初始赋值进行初始化;

注明:

在中期时,类加载指的是静态成员变量和静态代码块;
存在继承时: 
原则:先静后非,先父后子,先块后器 
执行顺序如下: 
第一步:父类静态成员变量(方法区) 
第二步:父类静态代码块(多个按照顺序执行) 
注意:根据静态代码块和变量位置顺序初始化变量 
第三步:子类静态成员变量(方法区) 
第四步:子类静态代码块 
第五步:父类成员变量和子类成员变量栈内存创建一片内存,指向值为null,先父类成员变量显式初始化(如果有的话) 
第六步:父类代码块(父类成员变量初始化) 
第七步:父类构造器 
第八步:子类成员变量显式初始化(如果有的话) 
第九步:子类代码块(子类成员变量初始化) 
第十步:子类构造器
例子分析:

class Base {
    private String name="base" ;
    static {
        System.out.println("Base static"  );

    }
    public Base() {
        System.out.println("base "  );
        System.out.println("base.name "  + name );

        tellName();
        printName();
    }
    public void tellName() {
        System.out.println("Base tell name: " + name);
    }
    public void printName() {
        System.out.println("Base print name: " + name);
    }
}
public class Dervied extends Base {
    private String name = "dervied";
    static {
        System.out.println("Dervied static"  );

    }
    public Dervied() {
        System.out.println("Dervied "  );
        System.out.println("Dervied.name "  + name );

        tellName();
        printName();
    }
    public void tellName() {
        System.out.println("Dervied tell name: " + name);
    }
    public void printName() {
        System.out.println("Dervied print name: " + name);
    }
    public static void main(String[] args){
        new Dervied();
    }
}
Base static
Dervied static
base 
base.name base
Dervied tell name: null
Dervied print name: null
Dervied 
Dervied.name dervied
Dervied tell name: dervied
Dervied print name: dervied


下来解释这程序过程:

声明父类成员变量name父=null,子类成员变量name=null;
显示初始化父类成员变量,name父=“base”;
执行父类构造器,执行tellName();方法,此处省略this关键字,实际是this.tellName(),this指当前对象Dervied子类,子类的name值还没有被初始化,所以默认为null,所以调用子类的tellName(),打印Dervied tell name: null,同理打印Dervied print name: null;
初始化子类成员变量name为dervied,所以打印 
Dervied tell name: dervied 
Dervied print name: dervied
这里单独分析类加载机制:APC

All 全盘负责:当一个类加载器加载某个类时,全盘负责将其类依赖的类一并加载;
Parent 父类委托:先父类加载器去试图加载该class,当父类加载器无法加载该class时,才从自己的类路径中加载该类;
Cache 缓存机制:缓存机制会将所有曾经类加载器加载过的类存入缓存中,当程序中需要某个类先去缓存中查找,如果查不到,则系统会读取该类的class文件继续缓存。这样算是为什么每次修改类后要重启JVM的原因
 

参考文献:

《new一个对象,java虚拟机做了什么》

https://blog.csdn.net/mrb1289798400/article/details/75637158

《Java对象与JVM(一) Java对象在Java虚拟机中的创建过程》

https://blog.csdn.net/tjiyu/article/details/53923392

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值