1. ART运行时堆的创建过程。
2. ART运行时的对象分配过程。
3. ART运行时的垃圾收集过程。
MarkSweep*
Zygote Active堆、Card Table、Heap
ART
HeapBitmap MarSta
art@classes dex中,以后只要系统启动类路径中的DEX文件不发生变化(即不发生更新升级),那么以后每次系统启动只需要将文件system@framework@boot
boot oat文件。再接下来就是Zygote
Header中。此外,system@framework@boot
dex中,以后只要系统启动类路径中的DEX文件不发生变化(即不发生更新升级),那么以后每次系统启动只需要将文件system@framework@boot
oat文件。再接下来就是Zygote
ImageSpace就将内部的live_bitmap_同时作为Live
ART
Zygote Space Allocation Space
Dalvik和ART运行时环境的区别_ Zygote Active堆、Card
Heap
CollectGarbage System.gc/collector/gc_type
ConcurrentGC GC
CollectGarbageInternal // ART
MarkSweep*
PartialMarkSweep和StickyMarkSweep三种类型的垃圾收集器组成。它们的类关系如图4所示:
1. GetBytesAllocated: 获得当前已经分配的字节数。
2. GetObjectsAllocated: 获得当前已经分配的对象数。
3. GetTotalBytesAllocated: 获得Space自创建以来所分配的字节数。
4. GetTotalObjectsAllocated: 获得Space自创建以来所分配的对象数。
5. Alloc: 在Space上分配一个指定大小的对象。
6. AllocationSize: 获得一个对象占据的内存块大小。
7. Free: 释放一个对象占据的内存块。
8. FreeList: 批量释放一系列对象占据的内存块。
ART运行时垃圾收集机制简要介绍和学习计划
GetLiveObjects GetMarkObjects
Image Space boot Zygote Space Allocation Space LargeObjectMapSpace描述的是Large
CardTable Live Object Bitmap。
Live Bitmap。 Mark Object Bitmap。
Mark Bitmap。
Mark Live Allocation Stack等概念,如图1所示:
mirror
1. Create: 一个静态成员函数,用来创建一个LargeObjectMapSpace。
2. Alloc: 重写了父类AllocSpace的成员函数Alloc。
3. AllocationSize: 重写了父类AllocSpace的成员函数AllocationSize。
4. Free: 重写了父类AllocSpace的成员函数Free。
ModUnionTable CardTable
除了Garbage Collector和Space,ART运行时垃圾收集机制比Dalvik垃圾收集机制还多了一个称Mod Union Table的概念。Mod Union Table是与Card Table配合使用的,用来记录在一次GC过程中,记录不会被回收的Space的对象对会被回收的Space的引用。例如,Image Space的对象对Zygote Space和Allocation Space的对象的引用,以及Zygote Space的对象对Allocation Space的对象的引用
Runtime Init
live-bitmap.reset HeapBitmap
mark-bitmap.reset HeapBitmap
ljkkjkkh
Image Space -》 Mod Union Table3)Heap
为default_mark_stack_size,即64KB
max
system@framework@boot.art@classes.dex中,以后只要系统启动类路径中的DEX文件不发生变化(即不发生更新升级),那么以后每次系统启动只需要将文件system@framework@boot
/data/dalvik-cache目录下是否存在一个system@framework@boot
ImageSpace
callee_save_method ArtMethod*
space->oat_file_.reset
space->oat_file_.recent_free_pos_
ArtMethod*
CreateFromMemMap CreateMallocSpace
live-bitmap mark-bitmap
Allocation Space
void Heap::MarkAllocStack(accounting::SpaceBitmap* bitmap, accounting::SpaceSetMap* large_objects,
accounting::ObjectStack* stack) {
mirror::Object** limit = stack->End();
for (mirror::Object** it = stack->Begin(); it != limit; ++it) {
const mirror::Object* obj = *it;
DCHECK(obj != NULL);
if (LIKELY(bitmap->HasAddress(obj))) {
bitmap->Set(obj);
} else {
large_objects->Set(obj);
}
}
}
mirror::Object**
AllocObj mirror
Zygote Space Allolication Space large_object_space
IsOutOf
kGcTypeFull
kGcTypeSticky kGcTypePartial Allocation
For Alloc Background
InitializePhase
SuspendAll Thread
Mark Phantom
CollectGarbageInternal
gc_complete_cond_->Broadcast self
mark_sweep
kGcTypeSticky、kGcTypePartial和kGcTypeFull的垃垃圾收集器组成
MarkSweep类用来回收Zygote Space和Allocation Space的垃圾,PartialMarkSweep类用来回收Allocation Space的垃圾,StickyMarkSweep类用来回收上次GC以来在Allcation Space上分配的最终又没有被引用的垃圾。
MarkSweep Zygote Allocation
ParallelGCThreads指定。
StickyMarkSweep
ART运行时提供给由DEX字节码翻译而来的本地机器代码使用的一个函数表中,包含了一个pCheckSuspend函数指针,
DeX 字节码
WaitForConcurrentGcToComplete
Wait
cond_is notified
TransitionFromRunnableToSuspended
TransitionFromSuspendedToRunnable
kGcRetentionPolicyAlwaysCollect
Allocation
current_mark_bitmap_
Default Mark Bitmaps
kGcRetentionPolicyAlwaysCollect Space
Mark Bitmap4)Mark
Mark Sweep ParallelGCThreads指定。
Zygote Allocation
Sticky
Handle Dirty Object阶段
Heap类的成员函数ProcessCards用来处理Card Table里面的Dirty Card,它是在Marking阶段被调用的
while (word_cur < word_end) {
while ((expected_word = *word_cur) != 0) {
new_word =
(visitor((expected_word >> 0) & 0xFF) << 0) |
(visitor((expected_word >> 8) & 0xFF) << 8) |
(visitor((expected_word >> 16) & 0xFF) << 16) |
(visitor((expected_word >> 24) & 0xFF) << 24);
if (new_word == expected_word) {
// No need to do a cas.
break;
}
if (LIKELY(android_atomic_cas(expected_word, new_word,
reinterpret_cast<int32_t*>(word_cur)) == 0)) {
for (size_t i = 0; i < sizeof(uintptr_t); ++i) {
const byte expected_byte = (expected_word >> (8 * i)) & 0xFF;
const byte new_byte = (new_word >> (8 * i)) & 0xFF;
if (expected_byte != new_byte) {
modified(reinterpret_cast<byte*>(word_cur) + i, expected_byte, new_byte);
}
}
break;
}
}
++word_cur;
}
uintptr_t
expected_word >>
它是一个能够存储指针的无符号int。这通常意味着它与指针的大小相同。
if (LIKELY(android_atomic_cas(expected_word, new_word,
reinterpret_cast<int32_t*>(word_cur)) == 0)) {
for (size_t i = 0; i < sizeof(uintptr_t); ++i) {
const byte expected_byte = (expected_word >> (8 * i)) & 0xFF;
const byte new_byte = (new_word >> (8 * i)) & 0xFF;
if (expected_byte != new_byte) {
modified(reinterpret_cast<byte*>(word_cur) + i, expected_byte, new_byte);
i 为第几个字节
4字节对齐 4字节为边界
%N == 0
ContinousSpace * s31寄存器的ART方法,即由被调用者保存非参数使用的通用寄存器以及所有的浮点数寄存器。
ModUnionClearCardSetVisitor
MarkReferences
VisitConcurrentRoots Dex Cache。关于Dex
mutator_lock_
MarkSweep
checkpoint_function-
VisitRoots MarkSweep
Mark Sweep也不会对上次GC以后分配的对象进行垃圾回收。由于Sticky Mark Sweep刚好相反,它要对上次GC以后分配的对象进行垃圾回收,因此,它就必须要重写MarkSweep类的成员函数MarkReachableObjects。
StickyMarkSweep
uintptr_t begin = reinterpret_cast<uintptr_t>(space->Begin());
uintptr_t end = reinterpret_cast<uintptr_t>(space->End());
atomic_finger_ = static_cast<int32_t>(0xFFFFFFFF);
uintptr_t
MarkSweep .RecursiveMark
Populates the mark stack based on the set of marked objects and
// recursively marks until the mark stack is emptied.
void MarkSweep::RecursiveMark() {
base::TimingLogger::ScopedSplit split("RecursiveMark", &timings_);
......
if (kUseRecursiveMark) {
const bool partial = GetGcType() == kGcTypePartial;
ScanObjectVisitor scan_visitor(this);
auto* self = Thread::Current();
ThreadPool* thread_pool = heap_->GetThreadPool();
size_t thread_count = GetThreadCount(false);
const bool parallel = kParallelRecursiveMark && thread_count > 1;
mark_stack_->Reset();
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
if ((space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) ||
(!partial && space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect)) {
current_mark_bitmap_ = space->GetMarkBitmap();
......
if (parallel) {
// We will use the mark stack the future.
// CHECK(mark_stack_->IsEmpty());
// This function does not handle heap end increasing, so we must use the space end.
uintptr_t begin = reinterpret_cast<uintptr_t>(space->Begin());
uintptr_t end = reinterpret_cast<uintptr_t>(space->End());
atomic_finger_ = static_cast<int32_t>(0xFFFFFFFF);
// Create a few worker tasks.
const size_t n = thread_count * 2;
while (begin != end) {
uintptr_t start = begin;
uintptr_t delta = (end - begin) / n;
delta = RoundUp(delta, KB);
if (delta < 16 * KB) delta = end - begin;
begin += delta;
auto* task = new RecursiveMarkTask(thread_pool, this, current_mark_bitmap_, start,
begin);
thread_pool->AddTask(self, task);
}
thread_pool->SetMaxActiveWorkers(thread_count - 1);
thread_pool->StartWorkers(self);
thread_pool->Wait(self, true, true);
thread_pool->StopWorkers(self);
} else {
// This function does not handle heap end increasing, so we must use the space end.
uintptr_t begin = reinterpret_cast<uintptr_t>(space->Begin());
uintptr_t end = reinterpret_cast<uintptr_t>(space->End());
current_mark_bitmap_->VisitMarkedRange(begin, end, scan_visitor);
}
}
}
}
ProcessMarkStack(false);
}
MarkStackPush () {
mark_stack_pos_ /= 2;
}
for (mirror::Object **it = mark_stack_->Begin(), **end = mark_stack_->End(); it < end; ) {
mirror::Object**it = mark_stack_->Begin **end = mark_stack_->End; it < end;
Mark Sweep Partial Mark Sweep
Sticky Mark Sweep Allocation Stack3
Image Zygote Allocation Space
ReMarkRoots CardTable
. 调用成员函数ProcessReferences处理Soft Reference、Weak Reference、Phantom Reference和Finalizer Reference等引用对象。
Soft Weak Phantom Finalizer Reference、Phantom
Runtime
Marking阶段对Card
Handle Dirty Objects过程中处理的。
我们知道,Image Space、Zygote Space和Allocation Space都是Continuous Space,它们的回收策略分别为kGcRetentionPolicyNeverCollect、kGcRetentionPolicyAlwaysCollect和kGcRetentionPolicyFullCollect,
void MarkSweep::SweepCallback(size_t num_ptrs, Object** ptrs, void* arg) {
SweepCallbackContext* context = static_cast<SweepCallbackContext*>(arg);
MarkSweep* mark_sweep = context->mark_sweep;
Heap* heap = mark_sweep->GetHeap();
space::AllocSpace* space = context->space;
Thread* self = context->self;
Locks::heap_bitmap_lock_->AssertExclusiveHeld(self);
// Use a bulk free, that merges consecutive objects before freeing or free per object?
// Documentation suggests better free performance with merging, but this may be at the expensive
// of allocation.
size_t freed_objects = num_ptrs;
// AllocSpace::FreeList clears the value in ptrs, so perform after clearing the live bit
size_t freed_bytes = space->FreeList(self, num_ptrs, ptrs);
heap->RecordFree(freed_objects, freed_bytes);
mark_sweep->freed_objects_.fetch_add(freed_objects);
mark_sweep->freed_bytes_.fetch_add(freed_bytes);
}
Object** ptrs
参数ptrs指向一组需要回收的对象占用的内存块的地址,而参数num_ptrs则表示这组内存块的个数。第三个参数arg指向一个SweepCallbackContext对象,这个对象的成员变量space描述了参数ptrs指向的内存块是所属的Space。有了这些信息之后,就可以调用相应的Space的成员函数FreeList进行真正的垃圾回收了。我们知道,Allocation Space是使用DlMallocSpace来描述的,因此,这里就是调用DlMallocSpace类的成员函数FreeList来回收内存。
从前面ART运行时为新创建对象分配内存的过程分析一文可以知道,DlMallocSpace类是通过C库提供的内存管理接口malloc来分配内存的,因此,它在回收内存时,也是使用对应的C库内存管理接口free来回收内存。这一点与Dalvik虚拟机垃圾收集(GC)过程分析一文分析的Dalvik虚拟机的垃圾回收逻辑也是一样的。
MarkSweep类的静态成员函数SweepCallback最后还调用了Heap类的成员函数Record记录了当前释放的对象个数和内存字节数,以及更新Mark Sweep内部的释放对象个数和内存字节数。
MarkSweep类的静态成员函数ZygoteSweepCallback的实现如下所示:
MarkSweep
SweepCallbackContext*
AllocSpace*
freed_objects (对象数) freed_bytes_(字节数)
size_t freed_objects = 0;
size_t freed_bytes = 0;
for (const Object* obj : large_live_objects->GetObjects()) {
if (!large_mark_objects->Test(obj)) {
freed_bytes += large_object_space->Free(self, const_cast<Object*>(obj));
++freed_objects;
}
}
size_t freed_bytes = 0;
Large Object Space
从前面ART运行时为新创建对象分配内存的过程分析一文可以知道,ART运行时使用的Large Object Space是一个LargeObjectMapSpace,它通过映射匿名共享内存为新创建对象分配内存,因此,LargeObjectMapSpace类的成员函数Free来释放内存时,就相应地删除对应的匿名共享内存就行了,如下所示:
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
size_t LargeObjectMapSpace::Free(Thread* self, mirror::Object* ptr) {
MutexLock mu(self, lock_);
MemMaps::iterator found = mem_maps_.find(ptr);
CHECK(found != mem_maps_.end()) << "Attempted to free large object which was not live";
DCHECK_GE(num_bytes_allocated_, found->second->Size());
size_t allocation_size = found->second->Size();
num_bytes_allocated_ -= allocation_size;
--num_objects_allocated_;
delete found->second;
mem_maps_.erase(found);
return allocation_size;
}
iterator found mem-maps_ find
found second Size
FreeList(self, chunk_freed_objects, objects_to_chunk_free);
FreeList
1. ART运行时堆的划分和管理更细致,它分为Image Space、Zygote Space、Allocation Space和Large Object Space四个Space,再加上一个Allocation Stack。其中,Allocation Space和Large Object Space和Dalvik虚拟机的Zygote堆和Active堆作用是一样的,而其余的Space则有特别的作用,例如,Image Space的对象是永远不需要回收的。
2. ART运行时的每一个Space都有不同的回收策略,ART运行时根据这个特性提供了Mark Sweep、Partial Mark Sweep和Sticky Mark Sweep等三种回收力度不同的垃圾收集器。其中,Mark Sweep的垃圾回收力度最大,它会同时回收Zygote Space、Allocation Space和Large Object Space的垃圾,Partial Mark Sweep的垃圾回收力度居中,它只会同时回收Allocation Space和Large Object Space的垃圾,而Sticky Mark Sweep的垃圾回收力度最小,它只会回收Allocation Stack的垃圾,即上次GC以后分配出来的又不再使用了的对象。力度越大的垃圾收集器,回收垃圾时需要的时候也就越长。这样我们就可以在应用程序运行的过程中根据不同的情景使用不同的垃圾收集器,那就可以更有效地执行垃圾回收过程。
3. ART运行时充分地利用了设备的CPU多核特性,在并行GC的执行过程中,将每一个并发阶段的工作划分成多个子任务,然后提交给一个线程池执行,这样就可以更高效率地完成整个GC过程,避免长时间对应用程序造成停顿。
Image Zygote Allocation Large Space
Active堆、Card Zygote
0x01-0x03
0000 0001 0000 0010 0000 0011
CPU要对成员变量c进行访问时,只需要一个读周期即可
0x00处读取了4个字节(注意由于是32位架构),
0x00 这个字节存放c
然后将0x01-0x03的3个字节暂存
接着又花费了一个读周期读取了从0x04-0x07的4字节数据,将0x04这个字节与刚刚暂存的3个字节进行拼接从而读取到成员变量i的值。
0x04 -0x07共4个字节;接下来看数据成员s的自身对齐值
pragma pack (value) value
pragma pack (2)
pragma pack()
sizeof struct A sizeof struct B
假设变量a存放在内存中的起始地址为0x00,那么其成员变量c的起始地址为0x00,
自身对齐值 指定对齐值 有效对齐值:上述两个对齐值中最小的那个。
c =1 4 0x00%1==0 0x00
i = 4 4 0x01%4 != 0 因此它应该被存放在0x04地址处,占用0x05,0x06,0x07共4个字节
s= 2 4 0x08 % 2 == 0 0x08
A = 4 4 0x0A%4 != 0
由此可见sizeof(struct A)的结果应该是=1+3(空闲空间)+4+2+2(结构体补充)=12字节。
07.#pragma pack (2) /* 指定按2字节对齐 */
08.struct B {
09. char c;
10. short s;
11. int i;
12.};
c = 1 2 1 0x00%1==0 0x00
s= 2 2 2 0x01 % 2 != 0 0x02 0x03
i = 4 2 2 0x04 % 2 = 0 0x04 0x05 0x06 0x07
假设编译器按默认4字节进行对齐
view plaincopy to clipboardprint?
01.#pragma pack(8)
02.struct S1 {
03. char a;
04. long b;
05.};
06.
07.struct S2 {
08. char c;
09. struct S1 d;
10. long long e;
11.};
12.#pragma pack()
13.
pragma pack(8) 8 字节对齐
#pragma pack()
sizeof (struct S1)
自身对齐值 指定对齐值 有效对齐值:上述两个对齐值中最小的那个。
c =1 4 0x00%1==0 0x00
i = 4 4 0x01%4 != 0 因此它应该被存放在0x04地址处,占用0x05,0x06,0x07共4个字节
s= 2 4 0x08 % 2 == 0 0x08
A = 4 4 0x0A%4 != 0
sizeof S1
a =1 8 1 0x00%1==0 0x00
b = 4 8 4 0x01%4 != 0 因此它应该被存放在0x04地址处,占用0x05,0x06,0x07共4个字节
sizeof S2
c= 1 8 1 0x00%1==0 0x00
S1 = S1的自身对齐值为成员的最大自身对齐值,即4字节
8 4 0x01%4 != 0 0x04开始8个字节,并且占用8个字节(0x04+0x08=0x0C),其中0x01-0x03被用来填充。
e = 8 8 8 0x0d%8 != 0 0x10 开始8个字节
我们一般说的对齐在N上,都是指有效对齐在N上。
Heap和Stack
简单说下:
Heap内存是指java运行环境用来分配给对象和JRE类的内存. 是应用的内存空间.
Stack内存是相对于线程Thread而言的, 它保存线程中方法中短期存在的变量值和对Heap中对象的引用等.
Stack内存, 顾名思义, 是类Stack方式, 总是后进先出(LIFO)的.
我们通常说的GC的针对Heap内存的. 因为Stack内存相当于是随用随销的.
Heap和Stack
Class Load
System Class 系统Class Loader加载的类. 例如java运行环境中rt.jar中类, 比如java.util.* package中的类.
Thread 运行中的线程
JNI 中的本地/全局变量, 用户自定义的JNI代码或是JVM内部的.
Busy Monitor 任何调用了wait()或notify()方法, 或是同步化的(synchronized)的东西. 可以理解为同步监控器.
Java本地实例, 还在运行的Thread的stack中的方法创建的对象.
Class Loader
Thread
JNI
wait notifyAll synchronized Java Stack
Eden Survivor Unrefer
Refer
Young Old Performance
Generation
GC那些事儿--Android内存优化第一弹
https://www.jianshu.com/p/5db05db4f5ab
Android是如何管理App内存的--Android内存优化第二弹
https://www.jianshu.com/p/4ad716c72c12
dex2oat
onReceive BroadcastReceiver () {
}
startService
LRUCache
Heap内存是指java运行环境用来分配给对象和JRE类的内存 Size在系统限制的最大值之内是随着应用的使用情况而变化的
dalvik.vm.heapstartsize
getprop
Tools, 出来接活了--Android内存优化第三弹
https://www.jianshu.com/p/080473ae050b
Memory Monitor
Dump Java Heap HPROF Viewer了 Analyzer
Allocation Traking按钮
Allocation Tracker
HPROF Viewer
Class View
App Image Zygote framework Heap
Class List View
Package Tree View
GC Root
Instance Depth Dominating Size
Reference Tree
Allocation Tracker
Group Method
Group Allocator
Method视图中的列含义如下:
列 解释
Method 方法
Count 该方法分配的实例总数
Size 该方法分配的内存总量(byte)
MAT Java Heap Heap dumps文件
LeakCanary
$ adb shell dumpsys meminfo com.udinic.perfdemo
Memory MAT
Memory Monitor, HPROF Viewer, MAT等等
HPROF Viewer MAT
ListenerManager
private List<SampleListener> listeners = new ArrayList<>();
ListenerManager.getInstance().addListener(this);
Analyzer Tasks Reference Tree
此例中, 比较简单, 可以很清晰看到是ListenerManager的静态单例sInstance最终支配了MemoryLeakActivity. sIntance连接到GC Roots, 故而导致MemoryLeakActivity GC Roots可达, 无法被回收.
Package Tree View
Dominate Tree
对象没有被回收是因为他有到GC Roots的可达路径
GC Roots
LeakCanary install