跟dalvik很相似。只是gc不一样,art虚拟机gc
mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* space,
size_t alloc_size, size_t* bytes_allocated) {
mirror::Object* ptr;
// The allocation failed. If the GC is running, block until it completes, and then retry the
// allocation.
collector::GcType last_gc = WaitForConcurrentGcToComplete(self);
if (last_gc != collector::kGcTypeNone) {
// A GC was in progress and we blocked, retry allocation now that memory has been freed.
ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated);
if (ptr != NULL) {
return ptr;
}
}
// Loop through our different Gc types and try to Gc until we get enough free memory.
for (size_t i = static_cast<size_t>(last_gc) + 1;
i < static_cast<size_t>(collector::kGcTypeMax); ++i) {
bool run_gc = false;
collector::GcType gc_type = static_cast<collector::GcType>(i);
switch (gc_type) {
case collector::kGcTypeSticky: {
const size_t alloc_space_size = alloc_space_->Size();
run_gc = alloc_space_size > min_alloc_space_size_for_sticky_gc_ &&
alloc_space_->Capacity() - alloc_space_size >= min_remaining_space_for_sticky_gc_;
break;
}
case collector::kGcTypePartial:
run_gc = have_zygote_space_;
break;
case collector::kGcTypeFull:
run_gc = true;
break;
default:
break;
}
if (run_gc) {
// If we actually ran a different type of Gc than requested, we can skip the index forwards.
collector::GcType gc_type_ran = CollectGarbageInternal(gc_type, kGcCauseForAlloc, false);
DCHECK_GE(static_cast<size_t>(gc_type_ran), i);
i = static_cast<size_t>(gc_type_ran);
// Did we free sufficient memory for the allocation to succeed?
ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated);
if (ptr != NULL) {
return ptr;
}
}
}
// Allocations have failed after GCs; this is an exceptional state.
// Try harder, growing the heap if necessary.
ptr = TryToAllocate(self, space, alloc_size, true, bytes_allocated);
if (ptr != NULL) {
return ptr;
}
......
// We don't need a WaitForConcurrentGcToComplete here either.
CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true);
return TryToAllocate(self, space, alloc_size, true, bytes_allocated);
}
这个函数定义在文件art/runtime/gc/heap.cc中。
Heap类的成员函数AllocateInternalWithGc主要是通过垃圾回收来满足请求分配的内存,它的执行逻辑如下所示:
- 调用Heap类的成员函数WaitForConcurrentGcToComplete检查是否有并行GC正在执行。如果有的话,就等待其执行完成,并且得到它的类型last_gc。如果last_gc如果不等于collector::kGcTypeNone,就表示有并行GC并且已经执行完成,因此就可以调用Heap类的成员函数TryToAllocate在不增长当前堆大小的前提下再次尝试分配请求的内存了。如果分配成功,则返回得到的内存起始地址给调用者。否则的话,继续往下执行。
- 依次执行kGcTypeSticky、kGcTypePartial和kGcTypeFull三种类型的GC。每次GC执行完毕,都尝试调用Heap类的成员函数TryToAllocate在不增长当前堆大小的前提下再次尝试分配请求的内存。如果分配内存成功,则返回得到的内存起始地址给调用者,并且不再执行下一种类型的GC。
这里需要注意的一点是,kGcTypeSticky、kGcTypePartial和kGcTypeFull三种类型的GC的垃圾回收力度是依次加强:kGcTypeSticky只回收上次GC后在Allocation Space中新分配的垃圾对象;kGcTypePartial只回收Allocation Space的垃圾对象;kGcTypeFull同时回收Zygote Space和Allocation Space的垃圾对象。通过这种策略,就有可能以最小代价解决分配对象时遇到的内在不足问题。不过,对于类型为kGcTypeSticky和kGcTypePartial的GC,它们的执行有前提条件的。
类型为kGcTypeSticky的GC的执行代码虽然是最小的,但是它能够回收的垃圾也是最小的。如果回收的垃圾不足于满足请求分配的内存,那就相当于做了一次无用功了。因此,执行类型为kGcTypeSticky的GC需要满足两个条件。第一个条件是上次GC后在Allocation Space上分配的内存要达到一定的阀值,这样才有比较大的概率回收到较多的内存。第二个条件Allocation Space剩余的未分配内存要达到一定的阀值,这样可以保证在回收得到较少内存时,也有比较大的概率满足请求分配的内存。前一个阀值定义在Heap类的成员变量min_alloc_space_size_for_sticky_gc_中,它的值设置为2M,而上次GC以来分配的内存通过当前Allocation Space的大小估算得到,即通过调用Heap类的成员变量alloc_space_指向的一个DlMallocSpace对象的成员函数Size获得。后一个阀值定义在Heap类的成员变量min_remaining_space_for_sticky_gc_中,它的值设置为1M,而Allocation Space剩余的未分配内存可以用Allocation Space的总大小减去当前Allocation Space的大小得到。通过调用Heap类的成员变量alloc_space_指向的一个DlMallocSpace对象的成员函数Capacity获得其总大小。
类型为kGcTypePartial的GC的执行前提是已经从Zygote Space中划分出Allocation Space。从前面ART运行时Java堆创建过程分析一文可以知道,当Heap类的成员变量have_zygote_space_的值等于true时,就表明已经从Zygote Space中划分出Allocation Space了。因此,在这种情况下,就可以执行类型为kGcTypePartial的GC了。
每一种类型的GC都是通过调用Heap类的成员函数CollectGarbageInternal来执行。注意这时候调用Heap类的成员函数CollectGarbageInternal传递的第三个参数为false,表示不对那些只被软引用对象引用的对象进行回收。如果上述的三种类型的GC执行完毕,还是不能满足分配请求的内存,则继续往下执行。
3. 经过前面三种类型的GC后还是不能成功分配到内存,那就说明能够回收的内存还是太小了,因此,这时候只能通过在允许范围内增长堆的大小来满足内存分配请求了。前面分析Heap类的成员函数TryToAllocate时,将第四个参数设置为true,即可在允许范围内增长堆大小的前提下进行内存分配。如果在允许范围内增长了堆的大小还是不能成功分配到请求的内存,那就只能出最后的一个大招了。
4. 最后的大招是首先执行一个类型为kGcTypeFull的、要求回收那些只被软引用对象引用的对象的GC,接着再在允许范围内增长堆大小的前提下尝试分配内存。这一次如果还是失败,那就真的是内存不足了。
至此,我们就对ART运行时在堆上为新创建对象分配内存的过程分析完成了。从中我们就可以看到,ART运行时面临的最大挑战就是内存不足问题,它要通过在允许范围内增长堆大小以及垃圾回收两个手段来解决。其中,垃圾回收会对程序造成影响,因此在执行垃圾回收时,使用的力度要从小到大。