6.关键字new的执行过程

java代码

编写如下代码,new一个对象

public class test {
	public static void main(String[] args) {
		Person p = new Person("liming");
		System.out.println(p);
	}
}

class Person {
	String name;

	public Person(String name) {
		super();
		this.name = name;
	}

}

查看classfile

通过命令查看classfile

javap -verbose test.class

如下:

Classfile /D:/test.class
  Last modified 2018-12-7; size 467 bytes
  MD5 checksum 84f0a803effe2eab9e624b6d817ce1ce
  Compiled from "test.java"
public class test
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #8.#17         // java/lang/Object."<init>":()V
   #2 = Class              #18            // Person
   #3 = String             #19            // liming
   #4 = Methodref          #2.#20         // Person."<init>":(Ljava/lang/String;)V
   #5 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;
   #6 = Methodref          #23.#24        // java/io/PrintStream.println:(Ljava/lang/Object;)V
   #7 = Class              #25            // test
   #8 = Class              #26            // java/lang/Object
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               main
  #14 = Utf8               ([Ljava/lang/String;)V
  #15 = Utf8               SourceFile
  #16 = Utf8               test.java
  #17 = NameAndType        #9:#10         // "<init>":()V
  #18 = Utf8               Person
  #19 = Utf8               liming
  #20 = NameAndType        #9:#27         // "<init>":(Ljava/lang/String;)V
  #21 = Class              #28            // java/lang/System
  #22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
  #23 = Class              #31            // java/io/PrintStream
  #24 = NameAndType        #32:#33        // println:(Ljava/lang/Object;)V
  #25 = Utf8               test
  #26 = Utf8               java/lang/Object
  #27 = Utf8               (Ljava/lang/String;)V
  #28 = Utf8               java/lang/System
  #29 = Utf8               out
  #30 = Utf8               Ljava/io/PrintStream;
  #31 = Utf8               java/io/PrintStream
  #32 = Utf8               println
  #33 = Utf8               (Ljava/lang/Object;)V
{
  public test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=1
         0: new           #2                  // class Person
         3: dup
         4: ldc           #3                  // String liming
         6: invokespecial #4                  // Method Person."<init>":(Ljava/lang/String;)V
         9: astore_1
        10: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
        13: aload_1
        14: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        17: return
      LineNumberTable:
        line 3: 0
        line 4: 10
        line 5: 17
}
SourceFile: "test.java"

字节码分析

Constant pool中包含java类中调用的所有方法,包括构造,main,和println方法
main的方法栈中包含调用顺序
LineNumberTable标记栈中每个命令在java文件中的行数

  • 0: new #2 // class Person
    代表new一个Person对象
  • 3: dup
    代表复制栈顶的内容再压入栈,也就是把栈顶的内容做个备份
  • 4: ldc #3 // String liming
    把String类型的"liming"入栈
  • 6: invokespecial #4 // Method Person."":(Ljava/lang/String;)V
    调用Person类的构造方法
  • 9: astore_1
    构造方法内的赋值,将当前栈顶引用类型的值"liming"赋值给slot1处的局部变量p的name,然后出栈
  • 10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
    加载public final static PrintStream out = null;
  • 13: aload_1
    将位置为1的对象引用局部变量压入栈
  • 14: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
    调用println方法
  • 17: return
    结束方法

new的分析

在如下网页查看源码里的解释器bytecodeInterpreter.cpp

http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/interpreter/bytecodeInterpreter.cpp#l1816

bytecodeInterpreter代码分析

      CASE(_new): {
        //获取字节流中的索引值
        u2 index = Bytes::get_Java_u2(pc+1);
        //获取常量池
        ConstantPool* constants = istate->method()->constants();
        //如果该类型已经被初始化了
        if (!constants->tag_at(index).is_unresolved_klass()) {
          // Make sure klass is initialized and doesn't have a finalizer
          Klass* entry = constants->slot_at(index).get_klass();
          //断言entry是一个类
          assert(entry->is_klass(), "Should be resolved klass");
          Klass* k_entry = (Klass*) entry;
          //断言k_entry是该类型的一个实例
          assert(k_entry->oop_is_instance(), "Should be InstanceKlass");
          InstanceKlass* ik = (InstanceKlass*) k_entry;
          //如果该实例所属类型已经被初始化并且能够快速分配内存
          if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) {
            //取对象所需内存尺寸
            size_t obj_size = ik->size_helper();
            oop result = NULL;
            // If the TLAB isn't pre-zeroed then we'll have to do it
            //是否需要置零是tlab的否,若tlab已经置零了则不需要再置零,反之则需要
            bool need_zero = !ZeroTLAB;
            //如果使用TLAB分配内存
            if (UseTLAB) {
            //试图从线程的tlab区分配内存区域
              result = (oop) THREAD->tlab().allocate(obj_size);
            }
            // Disable non-TLAB-based fast-path, because profiling requires that all
            // allocations go through InterpreterRuntime::_new() if THREAD->tlab().allocate
            // returns NULL.
#ifndef CC_INTERP_PROFILE
            //如果result仍为null,即尚未分配内存区域
            if (result == NULL) {
            //需要置零
              need_zero = true;
              // Try allocate in shared eden
            //直接在堆内分配内存
            retry:
            //获取堆顶的位置
              HeapWord* compare_to = *Universe::heap()->top_addr();
             //获取堆分配对象所需内存区域后堆顶的位置
              HeapWord* new_top = compare_to + obj_size;
              //如果分配后的堆顶位置小于堆的最大内存地址(即剩余内存位置足够)
              if (new_top <= *Universe::heap()->end_addr()) {
              //采用原子操作试图分配内存,失败则重试,直到成功为止(使用CAS方式实现原子操作)
                if (Atomic::cmpxchg_ptr(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) {
                  goto retry;
                }
                //成功后把原堆顶位置赋值给对象的引用
                result = (oop) compare_to;
              }
            }
#endif
            if (result != NULL) {
              // Initialize object (if nonzero size and need) and then the header
              //如果分配成功且需要置零则置零处理
              if (need_zero ) {
              //跳过object head部分的内存,对象头不置零
                HeapWord* to_zero = (HeapWord*) result + sizeof(oopDesc) / oopSize;
                obj_size -= sizeof(oopDesc) / oopSize;
                if (obj_size > 0 ) {
                //内存置零
                  memset(to_zero, 0, obj_size * HeapWordSize);
                }
              }
              //如果对象使用了偏向锁则在对象头部中写入偏向锁状态为1
              if (UseBiasedLocking) {
                result->set_mark(ik->prototype_header());
              } else {
                result->set_mark(markOopDesc::prototype());
              }
              //进行内存对齐
              result->set_klass_gap(0);
              //在对象头部中写入类型的引用
              result->set_klass(k_entry);
              // Must prevent reordering of stores for object initialization
              // with stores that publish the new object.
              //预防用于对象初始化的帧栈内存区域被重排序,先暴露该线程对内存区域的改动
              OrderAccess::storestore();
              //对象入栈
              SET_STACK_OBJECT(result, 0);
              UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
            }
          }
        }

延伸

多线程时有时会发生内存访问重排序,为了产生错误,需要通过storestore()方法构造StoreStore内存屏障。

对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它线程可见。

也可以通过对可能被改变的变量添加volatile关键字解决该问题,volatile实质上相当于:
JMM基于保守策略的JMM内存屏障插入策略:

1.在每个volatile写操作的前面插入一个StoreStore屏障

2.在每个volatile写操作的后面插入一个SotreLoad屏障

3.在每个volatile读操作的后面插入一个LoadLoad屏障

4.在每个volatile读操作的后面插入一个LoadStore屏障

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值