Note of deep JVM(2)_hotspot JVM

对象创建


当JVM遇到一条new指令,首先会检查这个指令参数能否在常量池中定位到一个类的符号引用,并检查这个引用代表的类是否已被加载,解析,初始化过。如果没有,则先执行上述过程。然后虚拟机将为新生对象分配内存,对象所需的内存空间在加载完成后即可完全确定。一般来说,分配的方式有两种

1.指针碰撞
java堆中内存绝对规整,从指针向空闲内存移动与对象大小相等的距离

2.空闲列表
虚拟机维护一个列表,记录哪块内存是可用的,在分配时从列表中找到一块足够大的空间,并分配给对象实例,并更新列表

选择哪种分配方式取决于java堆内存是否规整。

分配内存的原子性

解决方案1:cas操作
解决方案2:每个线程单独在java堆中分配一小块内存,称为本地线程分配缓冲区,只有在缓冲区满时,才需要同步锁定,虚拟机是否使用TLAB,可以用-Xx:+/-UseTLAB参数来设定

在内存分配完后,虚拟机需要将分配到内存空间的都初始化为0(不包括对象头)。

接下来,虚拟机将会为对象实例设置对象头,对象头主要包含:这个对象是哪个类的实例,如何才能找到类的元数据信息,对象的hashcode,对象的GC分代年龄等信息。

最后执行Init方法。

对应源码:

CASE(_new): {
        u2 index = Bytes::get_Java_u2(pc+1);
        constantPoolOop constants = istate->method()->constants();
        if (!constants->tag_at(index).is_unresolved_klass()) {
          // Make sure klass is initialized and doesn't have a finalizer
          oop entry = constants->slot_at(index).get_oop();
          assert(entry->is_klass(), "Should be resolved klass");
          klassOop k_entry = (klassOop) entry;
          assert(k_entry->klass_part()->oop_is_instance(), "Should be instanceKlass");
          instanceKlass* ik = (instanceKlass*) k_entry->klass_part();
          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
            bool need_zero = !ZeroTLAB;
            if (UseTLAB) {
              result = (oop) THREAD->tlab().allocate(obj_size);
            }
            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()) {
                if (Atomic::cmpxchg_ptr(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) {
                  goto retry;
                }
                result = (oop) compare_to;
              }
            }
            if (result != NULL) {
              // Initialize object (if nonzero size and need) and then the header
              //根据是否启用偏向锁来设置对象头信息
              if (need_zero ) {
                HeapWord* to_zero = (HeapWord*) result + sizeof(oopDesc) / oopSize;
                obj_size -= sizeof(oopDesc) / oopSize;
                if (obj_size > 0 ) {
                  memset(to_zero, 0, obj_size * HeapWordSize);
                }
              }
              if (UseBiasedLocking) {
                result->set_mark(ik->prototype_header());
              } else {
                result->set_mark(markOopDesc::prototype());
              }
              result->set_klass_gap(0);
              result->set_klass(k_entry);
              //对象引用入栈
              SET_STACK_OBJECT(result, 0);
              UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
            }
          }
        }
        // Slow case allocation
        CALL_VM(InterpreterRuntime::_new(THREAD, METHOD->constants(), index),
                handle_exception);
        SET_STACK_OBJECT(THREAD->vm_result(), 0);
        THREAD->set_vm_result(NULL);
        UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
      }

对象内存布局


对象在内存中存储的布局分3块:对象头,实例数据,对齐填充

1.对象头
除了上面所说的外,还包含锁状态标志,线程持有锁,偏向线程ID,偏向时间戳;除此之外,对象头还包含另一部分:类型指针。即对象指向它的类元数据的指针,虚拟机可通过这个指针来确定对象是哪个类的实例。

2.实例数据
实例数据存储顺序收到虚拟机分配的策略参数和字段在java源码中定义的顺序影响。hotspot默认分配策略为longs/doubles,ints,shorts/chars,bytes/booleans,oops(Ordinary Object Pointers),可以看出,相同宽度的字段总是被分配到一起。
在满足以上前提条件下,弗雷中定义的变量会出现在子类之前,如果compactFields参数设置为True(默认),那么子类中较窄的变量也会可能被插入到弗雷的变量空隙之中。

3.对齐填充
不是必须,填充站位作用,由于hotspot自动管理内存系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍,对象头正好是8字节的倍数,因此对象实例数据没有对齐时,就需要对齐填充来补全。

对象访问定位


reference类型在java虚拟机中只规定了一个指向对象的引用,并没有定义怎么取定位,怎么定位取决于不同虚拟机实现,主流的都是通过句柄和直接指针两种。

1.如果是使用句柄的话,java堆中将会划分出一款内存来作为句柄池,reference就是对象的句柄地址,句柄中则包含对象实例数据与类型数据各自的具体地址信息。如图:
这里写图片描述

2.直接指针,那么java堆对象的布局就必须要考虑如何房子访问类型数据的相关信息,reference存储的直接就是对象地址:如图:这里写图片描述

前者在对象被移动(GC期间移动非常频繁)时,只会改变句柄中的实例数据指针,而reference不需要被修改
后者速度更快,节省了一次指针定位的时间开销,由于对象访问非常频繁,因此这类开销是一项非常可观的执行成本。hotspot基于后者。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值