Android 对象内存占用

2,727 阅读10分钟

代码基于 AOSP android11-release 分支

LeakCanary 2 分析 一文中,粗略提到 LeakCanary 2 利用 ByteArray 直接存储关键信息。由于每组数组被连续地储存在 ByteArray 中,所以节省了 Object Header 所需的空间。

具体 Object Header 大小是多少呢?进一步延伸,ART 中一个 Object 所占用的内存大小又该怎么计算呢?了解清楚这点对我们以后分析内存占用会有更准确的认知。

在分析 Android 对象内存占用之前,推荐先看下《一个Java对象到底占多少内存》了解 HotSpot 虚拟机中 Java 对象的内存占用

对象头

// art/runtime/mirror/object.h

// C++ mirror of java.lang.Object
class MANAGED LOCKABLE Object {
 private:
  // The Class representing the type of the object.
  HeapReference<Class> klass_;
  // Monitor and hash code information.
  uint32_t monitor_;

// art/build/Android.common_build.mk
// art/build/art.go
// art/runtime/read_barrier_config.h
// 在上述三个文件中,默认不会定义 USE_BROOKS_READ_BARRIER
#ifdef USE_BROOKS_READ_BARRIER
  // Note names use a 'x' prefix and the x_rb_ptr_ is of type int
  // instead of Object to go with the alphabetical/by-type field order
  // on the Java side.
  uint32_t x_rb_ptr_;      // For the Brooks pointer.
  uint32_t x_xpadding_;    // For 8-byte alignment. TODO: get rid of this.
#endif
}

在 Object 的 native 定义中,只有四个普通变量,其中 klass_uint32_t 指针,其余变量都是 uint32_t。对于 #ifdef USE_BROOKS_READ_BARRIER,默认情况下必定为 false,此时 Object 对象为 8 bytes,即 Object Header 为 8 bytes。若为 true,则为 16 bytes。

对象大小

要了解对象的大小,可以直接看 Object 的 SizeOf 方法

// art/runtime/mirror/object-inl.h

template<VerifyObjectFlags kVerifyFlags>
inline size_t Object::SizeOf() {
  // Read barrier is never required for SizeOf since objects sizes are constant. Reading from-space
  // values is OK because of that.
  size_t result;
  constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
  if (IsArrayInstance<kVerifyFlags>()) {
    // 数组对象大小
    result = AsArray<kNewFlags>()->template SizeOf<kNewFlags>();
  } else if (IsClass<kNewFlags>()) {
    // Class 对象大小
    result = AsClass<kNewFlags>()->template SizeOf<kNewFlags>();
  } else if (IsString<kNewFlags>()) {
    // String 对象大小
    result = AsString<kNewFlags>()->template SizeOf<kNewFlags>();
  } else {
    // 普通对象大小
    result = GetClass<kNewFlags, kWithoutReadBarrier>()->template GetObjectSize<kNewFlags>();
  }
  return result;
}

可见普通对象的大小,通过 Class::GetObjectSize 获取,而其他的对象,则通过其对应的 SizeOf 方法获取。

数组对象

我们先来看一下数组对象的大小

// art/runtime/mirror/array-inl.h

template<VerifyObjectFlags kVerifyFlags>
inline size_t Array::SizeOf() {
  // 根据数组元素的类型计算偏移
  size_t component_size_shift =
      GetClass<kVerifyFlags, kWithoutReadBarrier>()->GetComponentSizeShift();
  // 数组长度
  int32_t component_count =
      GetLength<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>();
  // 头长度
  size_t header_size = DataOffset(1U << component_size_shift).SizeValue();
  // 数据长度
  size_t data_size = component_count << component_size_shift;
  return header_size + data_size;
}

可见数组占用的空间由 header、data 决定,而这两个数据的计算依赖于 component_size_shift,先来看下它的计算过程

// art/libdexfile/dex/primitive.h

static constexpr size_t kObjectReferenceSize = 4;

constexpr size_t ComponentSizeShiftWidth(size_t component_size) {
  return component_size == 1u ? 0u :
      component_size == 2u ? 1u :
          component_size == 4u ? 2u :
              component_size == 8u ? 3u : 0u;
}

class Primitive {
  static constexpr size_t ComponentSizeShift(Type type) {
    switch (type) {
      case kPrimVoid:
      case kPrimBoolean:
      case kPrimByte:    return 0;
      case kPrimChar:
      case kPrimShort:   return 1;
      case kPrimInt:
      case kPrimFloat:   return 2;
      case kPrimLong:
      case kPrimDouble:  return 3;
      case kPrimNot:     return ComponentSizeShiftWidth(kObjectReferenceSize);// 2
    }
  }
}

逻辑很简单,根据不同的类型返回不同的偏移值。接着看下 DataOffset 的逻辑

// art/runtime/mirror/array.h

class MANAGED Array : public Object {
 public:
  static constexpr MemberOffset DataOffset(size_t component_size) {
    // 对 offset 以 component_size 进行对齐
    size_t data_offset = RoundUp(OFFSETOF_MEMBER(Array, first_element_), component_size);
    return MemberOffset(data_offset);
  }

 private:
  // The number of array elements.
  // We only use the field indirectly using the LengthOffset() method.
  int32_t length_ ATTRIBUTE_UNUSED;
  // Marker for the data (used by generated code)
  // We only use the field indirectly using the DataOffset() method.
  uint32_t first_element_[0] ATTRIBUTE_UNUSED;
}

主要是获取 first_element_ 的偏移值,再根据传入的 component_size 进行对齐。

回到 header 的计算,MemberOffset::SizeValue 返回的是构造时传入的值,而 OFFSETOF_MEMBER(Array, first_element_) 恒定为 12(first_element_ 之前比 Object 只多了 int32_t length_,默认情况下即为 8 + 4 = 12)。所以 header_size 的计算可以简化为

// component_size 为 1U << component_size_shift
size_t header_size = RoundUp(12, component_size);

根据不同的元素类型,component_sizeheader_size 关系如下:

类型component_sizeheader_size
voidN/AN/A
boolean112
byte112
char212
short212
int412
float412
long816
double816
object_reference412

再来看回所有元素占用的空间计算

size_t data_size = component_count << component_size_shift;

相当于 component_count * component_size,即数组长度与单个元素占用大小的乘积

小结

所以,数组对象占用空间大小的计算公式为:

size = RoundUp(8 + 4, component_size) + length * component_size

String 对象

// art/runtime/mirror/string.h

// C++ mirror of java.lang.String
class MANAGED String final : public Object {
  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
  size_t SizeOf() REQUIRES_SHARED(Locks::mutator_lock_) {
    size_t size = sizeof(String);
    // 如果字符全为 ASCII 码,且允许压缩空间(目前 Java、Native 都默认为 true),每个字符以单个字节表示
    if (IsCompressed()) {
      size += (sizeof(uint8_t) * GetLength<kVerifyFlags>());
    } else {
      size += (sizeof(uint16_t) * GetLength<kVerifyFlags>());
    }
    return RoundUp(size, kObjectAlignment);
  }

 private:
  // If string compression is enabled, count_ holds the StringCompressionFlag in the
  // least significant bit and the length in the remaining bits, length = count_ >> 1.
  int32_t count_;

  uint32_t hash_code_;
}

// art/runtime/runtime_globals.h

// Required object alignment
static constexpr size_t kObjectAlignmentShift = 3;
static constexpr size_t kObjectAlignment = 1u << kObjectAlignmentShift;

不难看出,String 对象所占用的大小公式为:

size = RoundUp(16 + (IsCompressed() ? 1 : 2) * length,  8);

普通对象

Class 对象大小的计算比普通对象要更复杂些,但跟普通对象有一部分相同的逻辑。我们先来了解 Object 对象的计算过程,看一下 Class::GetObjectSize 方法

// art/runtime/mirror/class-inl.h

template<VerifyObjectFlags kVerifyFlags>
inline uint32_t Class::GetObjectSize() {
  return GetField32(ObjectSizeOffset());
}

// art/runtime/mirror/class.h

// C++ mirror of java.lang.Class
class MANAGED Class final : public Object {
 public:
  static constexpr MemberOffset ObjectSizeOffset() {
    return OFFSET_OF_OBJECT_MEMBER(Class, object_size_);
  }

 private:
  // Total object size; used when allocating storage on gc heap.
  // (For interfaces and abstract classes this will be zero.)
  // See also class_size_.
  uint32_t object_size_;
}

从上述代码中,我们可以了解到 Class::GetObjectSize 实际上只是返回 object_size_,接下来我们看下给 Object 分配空间的代码,了解一下实际上是不是 object_size_

// art/runtime/gc/heap-inl.h

template<bool kIsInstrumented, Class::AddFinalizer kAddFinalizer, bool kCheckAddFinalizer>
inline ObjPtr<Object> Class::Alloc(Thread* self, gc::AllocatorType allocator_type) {
  CheckObjectAlloc();
  gc::Heap* heap = Runtime::Current()->GetHeap();
  ...
  // Note that the `this` pointer may be invalidated after the allocation.
  ObjPtr<Object> obj =
      heap->AllocObjectWithAllocator<kIsInstrumented, /*kCheckLargeObject=*/ false>(
          self, this, this->object_size_, allocator_type, VoidFunctor());
  ...
  return obj;
}

对于 Heap::AllocObjectWithAllocator,粗略讲解一下,如果 allocator_typekAllocatorTypeBumpPointerkAllocatorTypeRegionkAllocatorTypeTLABkAllocatorTypeRegionTLAB,申请空间时会以 8 位进行对齐。而默认情况下 allocator_typekAllocatorTypeRosAlloc,所以不会做对齐。换句话说,默认情况下传入的 object_size_ 即为 Object 的大小。

再来看回 object_size_,它实际是在链接阶段被赋值

// art/runtime/class_linker.cc

bool ClassLinker::LinkFields(Thread* self,
                             Handle<mirror::Class> klass,
                             bool is_static,// Object 只统计 instance fields,is_static 必定为 false
                             size_t* class_size) {
  self->AllowThreadSuspension();
  const size_t num_fields = is_static ? klass->NumStaticFields() : klass->NumInstanceFields();
  LengthPrefixedArray<ArtField>* const fields = is_static ? klass->GetSFieldsPtr() :
      klass->GetIFieldsPtr();

  // Initialize field_offset
  MemberOffset field_offset(0);
  if (is_static) {
    field_offset = klass->GetFirstReferenceStaticFieldOffsetDuringLinking(image_pointer_size_);
  } else {
    ObjPtr<mirror::Class> super_class = klass->GetSuperClass();
    if (super_class != nullptr) {
      CHECK(super_class->IsResolved())
          << klass->PrettyClass() << " " << super_class->PrettyClass();
      field_offset = MemberOffset(super_class->GetObjectSize());
    }
  }

  CHECK_EQ(num_fields == 0, fields == nullptr) << klass->PrettyClass();

  // we want a relatively stable order so that adding new fields
  // minimizes disruption of C++ version such as Class and Method.
  //
  // The overall sort order order is:
  // 1) All object reference fields, sorted alphabetically.
  // 2) All java long (64-bit) integer fields, sorted alphabetically.
  // 3) All java double (64-bit) floating point fields, sorted alphabetically.
  // 4) All java int (32-bit) integer fields, sorted alphabetically.
  // 5) All java float (32-bit) floating point fields, sorted alphabetically.
  // 6) All java char (16-bit) integer fields, sorted alphabetically.
  // 7) All java short (16-bit) integer fields, sorted alphabetically.
  // 8) All java boolean (8-bit) integer fields, sorted alphabetically.
  // 9) All java byte (8-bit) integer fields, sorted alphabetically.
  //
  // Once the fields are sorted in this order we will attempt to fill any gaps that might be present
  // in the memory layout of the structure. See ShuffleForward for how this is done.
  std::deque<ArtField*> grouped_and_sorted_fields;
  const char* old_no_suspend_cause = self->StartAssertNoThreadSuspension(
      "Naked ArtField references in deque");
  for (size_t i = 0; i < num_fields; i++) {
    grouped_and_sorted_fields.push_back(&fields->At(i));
  }
  // 按上述顺序排序
  std::sort(grouped_and_sorted_fields.begin(), grouped_and_sorted_fields.end(),
            LinkFieldsComparator());

  // References should be at the front.
  size_t current_field = 0;
  size_t num_reference_fields = 0;
  FieldGaps gaps;

  for (; current_field < num_fields; current_field++) {
    ArtField* field = grouped_and_sorted_fields.front();
    Primitive::Type type = field->GetTypeAsPrimitiveType();
    bool isPrimitive = type != Primitive::kPrimNot;
    if (isPrimitive) {
      // 遇到 primitive 类型,证明 Object 引用已经处理完
      break;  // past last reference, move on to the next phase
    }
    if (UNLIKELY(!IsAligned<sizeof(mirror::HeapReference<mirror::Object>)>(
        field_offset.Uint32Value()))) {
      // 如果 offset 不是以 4 对齐,添加 gap 填充使其能对齐
      MemberOffset old_offset = field_offset;
      field_offset = MemberOffset(RoundUp(field_offset.Uint32Value(), 4));// 每个对象引用占 4 bytes
      AddFieldGap(old_offset.Uint32Value(), field_offset.Uint32Value(), &gaps);
    }
    DCHECK_ALIGNED(field_offset.Uint32Value(), sizeof(mirror::HeapReference<mirror::Object>));
    grouped_and_sorted_fields.pop_front();
    num_reference_fields++;
    field->SetOffset(field_offset);
    field_offset = MemberOffset(field_offset.Uint32Value() +
                                sizeof(mirror::HeapReference<mirror::Object>));
  }
  // Gaps are stored as a max heap which means that we must shuffle from largest to smallest
  // otherwise we could end up with suboptimal gap fills.
  // 以 8 对齐,下同。按照上面的排序规则,此处也按顺序分 4 次不同字节数的对齐
  // 另外,ShuffleForward 中会根据之前添加的 gap 与当前 field 大小决定
  // field 放入 gap 或新的空间中。
  ShuffleForward<8>(&current_field, &field_offset, &grouped_and_sorted_fields, &gaps);
  ShuffleForward<4>(&current_field, &field_offset, &grouped_and_sorted_fields, &gaps);
  ShuffleForward<2>(&current_field, &field_offset, &grouped_and_sorted_fields, &gaps);
  ShuffleForward<1>(&current_field, &field_offset, &grouped_and_sorted_fields, &gaps);
  ...

  // 以最后的 offset 作为 size,此时 field_offset 的数值为最后一个 field 的 offset + 类型占用大小 + gap
  size_t size = field_offset.Uint32Value();
  // Update klass
  if (is_static) {
    klass->SetNumReferenceStaticFields(num_reference_fields);
    *class_size = size;
  } else {
    ...
    if (!klass->IsVariableSize()) {
      ...
      // 赋值
      klass->SetObjectSize(size);
    }
  }

  ...
  return true;
}

在 LinkFields 过程中,只有 instance fields 会被汇总到 object size。而该过程中,主要做了以下几样工作:

  1. 将自身的 fields 汇总并按 object reference -> long -> double -> int -> float -> char -> short -> boolean -> byte 的顺序进行排序
  2. 根据类型,按顺序计算 fields 的 offset,并使它们按照对应的 component_size 进行对齐
  3. 若此前对齐所产生的额外空间能存放后面的 field,则利用这些空间,减少空间的损耗

这个过程,我们可以称为 fields 的布局。所以普通对象大小的计算,实际为 instance fields 布局所需的大小。

小结

从上述内容中,我们能意识到一个事实:object_size_ 中可能存在额外空间。若要自行计算,必定需要按照上述流程来处理,太过麻烦,只好另觅出路。

其实 java.lang.Class::art::mirror::Class 的映射,我们可以通过反射访问 Class.objectSize 从而获取 object_size_,这样就不需要进行复杂的计算。

Class 对象

// art/runtime/mirror/class.h

// C++ mirror of java.lang.Class
class MANAGED Class final : public Object {
 public:
  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
  uint32_t SizeOf() REQUIRES_SHARED(Locks::mutator_lock_) {
    return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, class_size_));
  }
}

可见跟普通对象类似,返回 class_size_,同样 java 层也有对应的 Class.classSize 直接获取。

原本想着这样蒙混过关算了,毕竟每个 class 都只会存在一个与之对应的 Class 对象,不会对内存造成大的影响。但看都看了这么多,不如再坚持一下权当了解过程。

class_size_ 的赋值只在类的定义、链接两个阶段

// art/runtime/class_linker.cc

ObjPtr<mirror::Class> ClassLinker::DefineClass(Thread* self,
                                               const char* descriptor,
                                               size_t hash,
                                               Handle<mirror::ClassLoader> class_loader,
                                               const DexFile& dex_file,
                                               const dex::ClassDef& dex_class_def) {
  ScopedDefiningClass sdc(self);
  StackHandleScope<3> hs(self);
  auto klass = hs.NewHandle<mirror::Class>(nullptr);

  ...

  if (klass == nullptr) {
    // Allocate a class with the status of not ready.
    // Interface object should get the right size here. Regular class will
    // figure out the right size later and be replaced with one of the right
    // size when the class becomes resolved.
    if (CanAllocClass()) {
      // ① 以 SizeOfClassWithoutEmbeddedTables 的结果申请空间,创建对应的 Class 对象
      klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
    } else {
      return sdc.Finish(nullptr);
    }
  }

  ...

  Runtime::Current()->GetRuntimeCallbacks()->ClassPreDefine(descriptor,
                                                            klass,
                                                            class_loader,
                                                            dex_file,
                                                            dex_class_def,
                                                            &new_dex_file,
                                                            &new_class_def);

  ...

  Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(klass);

  ...

  MutableHandle<mirror::Class> h_new_class = hs.NewHandle<mirror::Class>(nullptr);
  // ② 有必要的话,此处会将 vtable 所需的空间也纳入 Class 对象的大小计算中
  if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {
    // Linking failed.
    if (!klass->IsErroneous()) {
      mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self);
    }
    return sdc.Finish(nullptr);
  }

  ...

  Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(klass, h_new_class);

  // Notify native debugger of the new class and its layout.
  jit::Jit::NewTypeLoadedIfUsingJit(h_new_class.Get());

  return sdc.Finish(h_new_class);
}

Class 对象大小的计算过程分为两步,① 是以 ClassLinker::SizeOfClassWithoutEmbeddedTables 的结果作为 class_size_。而 ClassLinker::SizeOfClassWithoutEmbeddedTables 主要将所有 static fields 按占用位数分组统计,再返回 Class::ComputeClassSize 的结果(传入的 has_embedded_vtable 为 false)

// art/runtime/mirror/class-inl.h

inline uint32_t Class::ComputeClassSize(bool has_embedded_vtable,
                                        uint32_t num_vtable_entries,
                                        uint32_t num_8bit_static_fields,
                                        uint32_t num_16bit_static_fields,
                                        uint32_t num_32bit_static_fields,
                                        uint32_t num_64bit_static_fields,
                                        uint32_t num_ref_static_fields,
                                        PointerSize pointer_size) {
  // Space used by java.lang.Class and its instance fields.
  uint32_t size = sizeof(Class);
  // Space used by embedded tables.
  if (has_embedded_vtable) {
    size = RoundUp(size + sizeof(uint32_t), static_cast<size_t>(pointer_size));
    size += static_cast<size_t>(pointer_size);  // size of pointer to IMT
    size += num_vtable_entries * VTableEntrySize(pointer_size);
  }

  // Space used by reference statics.
  size += num_ref_static_fields * kHeapReferenceSize;
  if (!IsAligned<8>(size) && num_64bit_static_fields > 0) {
    uint32_t gap = 8 - (size & 0x7);
    size += gap;  // will be padded
    // Shuffle 4-byte fields forward.
    while (gap >= sizeof(uint32_t) && num_32bit_static_fields != 0) {
      --num_32bit_static_fields;
      gap -= sizeof(uint32_t);
    }
    // Shuffle 2-byte fields forward.
    while (gap >= sizeof(uint16_t) && num_16bit_static_fields != 0) {
      --num_16bit_static_fields;
      gap -= sizeof(uint16_t);
    }
    // Shuffle byte fields forward.
    while (gap >= sizeof(uint8_t) && num_8bit_static_fields != 0) {
      --num_8bit_static_fields;
      gap -= sizeof(uint8_t);
    }
  }
  // Guaranteed to be at least 4 byte aligned. No need for further alignments.
  // Space used for primitive static fields.
  size += num_8bit_static_fields * sizeof(uint8_t) + num_16bit_static_fields * sizeof(uint16_t) +
      num_32bit_static_fields * sizeof(uint32_t) + num_64bit_static_fields * sizeof(uint64_t);
  return size;
}

Class::ComputeClassSize 也是按照对齐逻辑来分别计算不同位数所需的个数,由于 has_embedded_vtable 为 false,所以 ① 处生成的 Class 对象的大小为 instance fields 与 static fields 布局所需的空间。

接下来看 ② 的逻辑

// art/runtime/class_linker.cc

bool ClassLinker::LinkClass(Thread* self,
                            const char* descriptor,
                            Handle<mirror::Class> klass,
                            Handle<mirror::ObjectArray<mirror::Class>> interfaces,
                            MutableHandle<mirror::Class>* h_new_class_out) {
  ...

  size_t class_size;
  if (!LinkStaticFields(self, klass, &class_size)) {
    return false;
  }
  ...

  if (!klass->IsTemp() || (!init_done_ && klass->GetClassSize() == class_size)) {
    // 原始类型、接口、抽象类进入该分支
    ...
    h_new_class_out->Assign(klass.Get());
  } else {
    // 普通对象及数组对象进入该分支
    CHECK(!klass->IsResolved());
    // Retire the temporary class and create the correctly sized resolved class.
    StackHandleScope<1> hs(self);
    Handle<mirror::Class> h_new_class =
        hs.NewHandle(mirror::Class::CopyOf(klass, self, class_size, imt, image_pointer_size_));
    ...
    h_new_class_out->Assign(h_new_class.Get());
  }
  return true;
}

主要做了三件事:

  1. 通过 LinkStaticFields 计算类的大小
  2. 若为原始类型、接口、抽象类,直接用传入的 klass 作为最终的 Class 对象,即其大小等于 SizeOfClassWithoutEmbeddedTables 计算的大小
  3. 若为普通对象或数组,通过第一步计算的 class_size 作为新的大小,通过 CopyOf 创建新的 Class 对象

再来看下 class_size 的计算过程

// art/runtime/class_linker.cc

bool ClassLinker::LinkStaticFields(Thread* self, Handle<mirror::Class> klass, size_t* class_size) {
  CHECK(klass != nullptr);
  return LinkFields(self, klass, true, class_size);
}

bool ClassLinker::LinkFields(Thread* self,
                             Handle<mirror::Class> klass,
                             bool is_static,
                             size_t* class_size) {
  self->AllowThreadSuspension();
  const size_t num_fields = is_static ? klass->NumStaticFields() : klass->NumInstanceFields();
  LengthPrefixedArray<ArtField>* const fields = is_static ? klass->GetSFieldsPtr() :
      klass->GetIFieldsPtr();

  // Initialize field_offset
  MemberOffset field_offset(0);
  if (is_static) {
    // vtable 会纳入到 offset 中
    field_offset = klass->GetFirstReferenceStaticFieldOffsetDuringLinking(image_pointer_size_);
  } else {
    ...
  }

  // 1. 排序
  // 2. 计算每个 field 的 offset 及对齐

  // 以最后的 offset 作为 size,此时 field_offset 的数值为最后一个 field 的 offset + 类型占用大小 + gap
  size_t size = field_offset.Uint32Value();
  // Update klass
  if (is_static) {
    klass->SetNumReferenceStaticFields(num_reference_fields);
    *class_size = size;// 赋值到传入的 class_size 指针中
  } else {
    ...
  }

  ...
  return true;
}

具体逻辑跟普通对象的大小一样,区别在于将 instance fields 换成了 static fields。其中 field_offset 是通过 Class::GetFirstReferenceStaticFieldOffsetDuringLinking 进行初始化,而该方法会计算 vtable 占用的大小,返回的结果为 java.lang.Class 对象的大小与 vtable 的和,所以 ClassLinker::LinkFields 最终返回结果为:instance fields + static fields + vtable

小结

Class 对象的大小需区分 Class 本身的类型

  • 若为接口、抽象类、原始类型,结果为 instance fields + static fields 布局所需的大小
  • 若为普通对象、数组,则为 instance fields + static fields + vtable 布局所需的大小

同样的,因为布局计算过程涉及对齐,我们并不能很方便地自行计算。但可以通过反射访问 Class.classSize 从而获取 class_size_(经验证无法通过反射获取 Class.classSize 对应的 Field,目前应用层若要获取具体的数值,除自行计算外,只能通过 JVMTIGetObjectSize 方法获取)

总结

至此,我们已经了解到数组对象及普通对象占用空间大小的计算方法:

  • 数组对象 —— 默认情况下,size = RoundUp(12, component_size) + length * component_size
  • String 对象 —— 默认情况下,size = RoundUp(16 + (IsCompressed() ? 1 : 2) * length, 8)
  • 普通对象 —— 默认情况下(allocator_typekAllocatorTypeRosAlloc),Class.objectSize 即为对象占用的大小(若 allocator_typekAllocatorTypeBumpPointerkAllocatorTypeRegionkAllocatorTypeTLABkAllocatorTypeRegionTLAB,需要使用 Class.objectSizeAllocFastPath
  • Class 对象 —— Class.classSize(经验证无法通过反射获取 Class.classSize 对应的 Field,目前应用层若要获取具体的数值,除自行计算外,只能通过 JVMTIGetObjectSize 方法获取)

参考文章