代码基于 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_size
及 header_size
关系如下:
类型 | component_size | header_size |
---|---|---|
void | N/A | N/A |
boolean | 1 | 12 |
byte | 1 | 12 |
char | 2 | 12 |
short | 2 | 12 |
int | 4 | 12 |
float | 4 | 12 |
long | 8 | 16 |
double | 8 | 16 |
object_reference | 4 | 12 |
再来看回所有元素占用的空间计算
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_type
为 kAllocatorTypeBumpPointer
、kAllocatorTypeRegion
、kAllocatorTypeTLAB
或 kAllocatorTypeRegionTLAB
,申请空间时会以 8 位进行对齐。而默认情况下 allocator_type
为 kAllocatorTypeRosAlloc
,所以不会做对齐。换句话说,默认情况下传入的 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>(¤t_field, &field_offset, &grouped_and_sorted_fields, &gaps);
ShuffleForward<4>(¤t_field, &field_offset, &grouped_and_sorted_fields, &gaps);
ShuffleForward<2>(¤t_field, &field_offset, &grouped_and_sorted_fields, &gaps);
ShuffleForward<1>(¤t_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。而该过程中,主要做了以下几样工作:
- 将自身的 fields 汇总并按 object reference -> long -> double -> int -> float -> char -> short -> boolean -> byte 的顺序进行排序
- 根据类型,按顺序计算 fields 的 offset,并使它们按照对应的 component_size 进行对齐
- 若此前对齐所产生的额外空间能存放后面的 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;
}
主要做了三件事:
- 通过
LinkStaticFields
计算类的大小 - 若为原始类型、接口、抽象类,直接用传入的 klass 作为最终的 Class 对象,即其大小等于
SizeOfClassWithoutEmbeddedTables
计算的大小 - 若为普通对象或数组,通过第一步计算的
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,目前应用层若要获取具体的数值,除自行计算外,只能通过 JVMTI
的 GetObjectSize
方法获取)
总结
至此,我们已经了解到数组对象及普通对象占用空间大小的计算方法:
- 数组对象 —— 默认情况下,
size = RoundUp(12, component_size) + length * component_size
- String 对象 —— 默认情况下,
size = RoundUp(16 + (IsCompressed() ? 1 : 2) * length, 8)
- 普通对象 —— 默认情况下(
allocator_type
为kAllocatorTypeRosAlloc
),Class.objectSize
即为对象占用的大小(若allocator_type
是kAllocatorTypeBumpPointer
、kAllocatorTypeRegion
、kAllocatorTypeTLAB
或kAllocatorTypeRegionTLAB
,需要使用Class.objectSizeAllocFastPath
) - Class 对象 ——
(经验证无法通过反射获取Class.classSize
Class.classSize
对应的 Field,目前应用层若要获取具体的数值,除自行计算外,只能通过JVMTI
的GetObjectSize
方法获取)