码字不易,请大佬们点点关注,谢谢~
一、复制算法(Copying GC)基础原理概述
在Android Runtime(ART)的垃圾回收体系中,复制算法(Copying GC)是针对新生代内存管理的核心技术之一。其核心思想基于"大部分对象生命周期较短"这一统计规律,通过将存活对象从一块内存区域复制到另一块,同时清理掉未被复制的垃圾对象,实现高效的内存回收。复制算法的优势在于实现简单、无内存碎片产生且回收速度快,特别适用于对象存活率低的场景。在ART源码中,复制算法相关实现主要分布于art/runtime/gc/collector/目录下,通过对其源码的深入分析,能够清晰理解该算法在内存管理中的具体运作机制。
二、复制算法核心数据结构与组件
2.1 新生代内存布局与数据结构
ART的新生代采用"一块Eden空间 + 两块Survivor空间"的经典布局,这种结构为复制算法提供了基础环境。在art/runtime/gc/heap.h中定义了相关内存区域的边界地址:
class Heap {
private:
// Eden空间起始与结束地址
uint8_t* eden_start_;
uint8_t* eden_end_;
// From Survivor空间起始与结束地址
uint8_t* from_space_start_;
uint8_t* from_space_end_;
// To Survivor空间起始与结束地址
uint8_t* to_space_start_;
uint8_t* to_space_end_;
// 新生代总大小计算
size_t YoungGenSize() const {
return (eden_end_ - eden_start_) + (from_space_end_ - from_space_start_) + (to_space_end_ - to_space_start_);
}
};
此外,每个对象在内存中以ObjectHeader作为头部,包含对象大小、分代年龄等关键信息,在art/runtime/mirror/object_header.h中定义:
struct ObjectHeader {
uint32_t size_; // 对象总大小(含头部)
uint8_t age_; // 对象经历垃圾回收次数
// 其他元数据...
};
这些数据结构为复制算法识别对象、判断存活状态提供了必要信息。
2.2 复制算法核心组件
复制算法在ART中通过CopyingCollector类实现,其核心组件包括:
- 根对象扫描器(Root Scanner):负责从根集合(如栈、静态变量)开始标记可达对象,代码位于
art/runtime/gc/collector/root_scanner.cc
class RootScanner {
public:
void ScanRoots(Closure* closure) {
// 扫描Java栈帧中的对象引用
ScanJavaStack(closure);
// 扫描JNI本地栈中的对象引用
ScanJniLocalRefs(closure);
// 扫描静态变量引用
ScanStaticObjects(closure);
}
private:
void ScanJavaStack(Closure* closure) {
Thread* self = Thread::Current();
StackVisitor visitor(closure);
self->GetJavaStack()->Iterate(&visitor);
}
// 其他扫描逻辑...
};
- 对象复制器(Object Copier):负责将存活对象从From Space复制到To Space,并更新对象引用,实现在
art/runtime/gc/collector/object_copier.cc
class ObjectCopier {
public:
void* CopyObject(void* source_obj) {
ObjectHeader* source_header = reinterpret_cast<ObjectHeader*>(source_obj);
size_t size = source_header->size_;
// 在To Space分配新内存
void* target_obj = AllocateInToSpace(size);
// 复制对象数据
memcpy(target_obj, source_obj, size);
// 更新对象头部的年龄信息
reinterpret_cast<ObjectHeader*>(target_obj)->age_ = source_header->age_ + 1;
return target_obj;
}
private:
void* AllocateInToSpace(size_t size) {
// 检查To Space剩余空间
if (to_space_top_ + size > to_space_end_) {
// 空间不足时抛出异常
RuntimeAbort("Out of space in To Space");
}
void* result = to_space_top_;
to_space_top_ += size;
return result;
}
uint8_t* to_space_top_; // To Space当前分配指针
};
三、复制算法执行流程详解
3.1 初始标记阶段
复制算法的执行从暂停所有应用线程开始(Stop The World),通过RootScanner扫描根集合标记可达对象。在art/runtime/gc/collector/copying_collector.cc中:
class CopyingCollector {
public:
void Collect() {
// 暂停所有线程
Thread::SuspendAll();
RootScanner scanner;
// 定义标记闭包,用于记录对象引用
MarkClosure mark_closure(this);
scanner.ScanRoots(&mark_closure);
// 从Eden和From Space开始扫描对象图
ScanLiveObjectsInRegions(&mark_closure);
// 执行对象复制
CopyLiveObjects();
// 交换From/To空间角色
SwapSpaces();
// 恢复所有线程
Thread::ResumeAll();
}
private:
void ScanLiveObjectsInRegions(Closure* closure) {
// 扫描Eden空间
ScanRegion(eden_start_, eden_end_, closure);
// 扫描From Space
ScanRegion(from_space_start_, from_space_end_, closure);
}
void ScanRegion(uint8_t* start, uint8_t* end, Closure* closure) {
uint8_t* current = start;
while (current < end) {
ObjectHeader* header = reinterpret_cast<ObjectHeader*>(current);
size_t size = header->size_;
// 若对象已标记则跳过
if (header->IsMarked()) {
current += size;
continue;
}
// 调用闭包处理对象引用
closure->DoObject(reinterpret_cast<void*>(header));
current += size;
}
}
};
此阶段通过深度优先搜索(DFS)遍历对象图,标记所有可达对象。
3.2 对象复制阶段
标记完成后进入对象复制阶段,ObjectCopier负责将标记的存活对象从原空间复制到To Space:
class CopyingCollector {
private:
void CopyLiveObjects() {
ObjectCopier copier;
uint8_t* current = eden_start_;
// 复制Eden空间存活对象
while (current < eden_end_) {
ObjectHeader* header = reinterpret_cast<ObjectHeader*>(current);
if (header->IsMarked()) {
void* new_obj = copier.CopyObject(reinterpret_cast<void*>(header));
// 更新所有指向该对象的引用
UpdateReferences(reinterpret_cast<void*>(header), new_obj);
}
current += header->size_;
}
// 复制From Space存活对象
current = from_space_start_;
while (current < from_space_end_) {
// 相同逻辑...
}
}
void UpdateReferences(void* old_obj, void* new_obj) {
// 遍历所有可能引用该对象的位置(如其他对象内部)
// 并将引用更新为新对象地址
}
};
复制过程中会同时更新对象引用,确保程序后续访问的一致性。
3.3 空间交换与清理
复制完成后,通过交换From/To空间角色实现内存清理:
class CopyingCollector {
private:
void SwapSpaces() {
// 交换指针
std::swap(from_space_start_, to_space_start_);
std::swap(from_space_end_, to_space_end_);
// 重置To Space分配指针
to_space_top_ = to_space_start_;
}
};
原To Space变为新的From Space,原From Space由于所有存活对象已被复制,此时变为空闲状态,可直接用于下一轮内存分配。
四、复制算法性能瓶颈分析
尽管复制算法具备高效性,但在实际应用中仍存在性能瓶颈:
- 内存浪费问题:新生代固定划分为三块区域,无论对象存活率高低,始终有一块Survivor空间处于闲置状态,造成约50%的内存浪费。在高存活率场景下,这种浪费尤为显著。
- 复制开销:当存活对象较多时,大量对象复制操作会消耗CPU资源和内存带宽。尤其在大对象或数组复制时,
memcpy操作带来的性能损耗不可忽视。 - Stop The World暂停:复制算法的整个执行过程需要暂停所有应用线程,在新生代内存较大或对象图复杂时,可能导致明显的应用卡顿,影响用户体验。
五、分代年龄晋升策略优化
5.1 分代年龄阈值设定
为减少复制开销,ART采用分代年龄晋升策略,在art/runtime/gc/gc_policy.cc中定义晋升阈值:
class GcPolicy {
public:
static const int kMaxYoungGenAge = 7; // 对象在新生代的最大存活次数
bool ShouldPromote(Object* obj) const {
ObjectHeader* header = obj->GetObjectHeader();
return header->age_ >= kMaxYoungGenAge;
}
};
当对象经历垃圾回收次数达到阈值时,会被晋升到老年代。
5.2 晋升实现流程
在对象复制阶段执行晋升操作:
class CopyingCollector {
private:
void CopyLiveObjects() {
ObjectCopier copier;
uint8_t* current = eden_start_;
while (current < eden_end_) {
ObjectHeader* header = reinterpret_cast<ObjectHeader*>(current);
if (header->IsMarked()) {
if (GcPolicy().ShouldPromote(reinterpret_cast<void*>(header))) {
// 晋升到老年代
PromoteObject(reinterpret_cast<void*>(header));
} else {
void* new_obj = copier.CopyObject(reinterpret_cast<void*>(header));
UpdateReferences(reinterpret_cast<void*>(header), new_obj);
}
}
current += header->size_;
}
// From Space复制逻辑类似...
}
void PromoteObject(void* obj) {
// 在老年代分配内存
void* new_obj = AllocateInOldGen(reinterpret_cast<ObjectHeader*>(obj)->size_);
// 复制对象数据
memcpy(new_obj, obj, reinterpret_cast<ObjectHeader*>(obj)->size_);
// 更新所有引用
UpdateReferences(obj, new_obj);
}
};
通过年龄晋升策略,将长生命周期对象转移到老年代,减少新生代复制压力。
六、动态空间比例调整优化
6.1 自适应空间分配策略
为缓解内存浪费问题,ART引入动态空间比例调整机制。在art/runtime/gc/heap_adjuster.cc中实现:
class HeapAdjuster {
public:
void AdjustYoungGenRatio() {
// 统计最近N次GC的对象存活率
float survival_rate = CalculateSurvivalRate();
if (survival_rate > 0.6) {
// 高存活率时扩大老年代比例
IncreaseOldGenSize();
} else if (survival_rate < 0.2) {
// 低存活率时扩大新生代比例
IncreaseYoungGenSize();
}
}
private:
float CalculateSurvivalRate() {
// 统计存活对象大小与总回收内存大小的比例
}
void IncreaseOldGenSize() {
// 从新生代中划分部分空间到老年代
size_t size_to_move = CalculateSizeToMove();
MoveMemoryRegion(eden_end_ - size_to_move, size_to_move, old_gen_start_);
eden_end_ -= size_to_move;
}
void IncreaseYoungGenSize() {
// 从老年代回收部分空间到新生代
}
};
通过实时监控对象存活率,动态调整新生代与老年代的空间比例。
6.2 空间调整的实现细节
空间调整涉及内存区域的重新映射与数据迁移:
class Heap {
private:
void MoveMemoryRegion(void* source, size_t size, void* target) {
// 确保目标区域空闲
if (!IsRegionFree(target, size)) {
RuntimeAbort("Target region not free");
}
// 复制数据
memcpy(target, source, size);
// 更新页表映射
UpdatePageTableEntries(source, size, target);
// 标记原区域为空闲
MarkRegionFree(source, size);
}
void UpdatePageTableEntries(void* old_addr, size_t size, void* new_addr) {
// 遍历对应虚拟地址范围的页表项
// 更新物理地址映射
}
};
这种动态调整机制在不同负载场景下优化了内存使用效率。
七、并行与并发复制优化
7.1 并行复制实现
为减少STW暂停时间,ART支持并行复制算法。在art/runtime/gc/collector/parallel_copying.cc中:
class ParallelCopyingCollector {
public:
void Collect() {
Thread::SuspendAll();
RootScanner scanner;
MarkClosure mark_closure(this);
// 多线程并行扫描根集合
ParallelScanRoots(&mark_closure);
// 并行扫描对象图
ParallelScanLiveObjects(&mark_closure);
// 并行复制对象
ParallelCopyLiveObjects();
SwapSpaces();
Thread::ResumeAll();
}
private:
void ParallelScanRoots(Closure* closure) {
// 划分根集合扫描任务到多个线程
std::vector<std::thread> threads;
for (int i = 0; i < kNumParallelThreads; ++i) {
threads.emplace_back([closure, i]() {
RootScanner local_scanner;
local_scanner.ScanPartialRoots(closure, i, kNumParallelThreads);
});
}
for (auto& th : threads) {
th.join();
}
}
// 其他并行逻辑...
};
通过多线程并行处理,加速标记和复制过程。
7.2 并发复制探索
更先进的并发复制算法在art/runtime/gc/collector/concurrent_copying.cc中尝试实现:
class ConcurrentCopyingCollector {
public:
void Collect() {
// 初始标记(STW)
InitialMark();
// 并发标记阶段
ConcurrentMark();
// 重新标记(STW)
Remark();
// 并发复制阶段
ConcurrentCopy();
}
private:
void ConcurrentMark() {
// 设置写屏障
SetWriteBarrier(true);
// 启动并发标记线程
std::thread mark_thread([this]() {
MarkLiveObjectsConcurrently();
});
// 允许应用线程继续运行
Thread::ResumeAll();
mark_thread.join();
Thread::SuspendAll();
}
void ConcurrentCopy() {
// 类似并发标记的实现方式
}
};
通过分阶段执行和写屏障技术,减少对应用线程的影响。
八、压缩式复制优化
8.1 压缩复制原理
压缩式复制算法在复制过程中对存活对象进行紧凑排列,消除内存碎片。在art/runtime/gc/collector/compacting_copying.cc中:
class CompactingCopyingCollector {
public:
void Collect() {
Thread::SuspendAll();
RootScanner scanner;
MarkClosure mark_closure(this);
scanner.ScanRoots(&mark_closure);
ScanLiveObjectsInRegions(&mark_closure);
// 计算目标地址
CalculateTargetAddresses();
// 压缩复制对象
CompactCopyLiveObjects();
SwapSpaces();
Thread::ResumeAll();
}
private:
void CalculateTargetAddresses() {
uint8_t* target = to_space_start_;
uint8_t* current = eden_start_;
while (current < eden_end_) {
ObjectHeader* header = reinterpret_cast<ObjectHeader*>(current);
if (header->IsMarked()) {
// 记录对象的目标地址
header->SetTargetAddress(target);
target += header->size_;
}
current += header->size_;
}
// From Space计算逻辑类似...
}
void CompactCopyLiveObjects() {
ObjectCopier copier;
uint8_t* current = eden_start_;
while (current < eden_end_) {
ObjectHeader* header = reinterpret_cast<ObjectHeader*>(current);
if (header->IsMarked()) {
void* target = header->GetTargetAddress();
void* new_obj = copier.CopyObjectTo(target, reinterpret_cast<void*>(header));
UpdateReferences(reinterpret_cast<void*>(header), new_obj);
}
current += header->size_;
}
// From Space复制逻辑类似...
}
};
8.2 压缩复制的优势与代价
该方式在回收后能提供连续的空闲内存,但额外
Android Runtime复制算法(Copying GC)优化深度剖析(续)
八、压缩式复制优化(续)
8.2 压缩复制的优势与代价
该方式在回收后能提供连续的空闲内存,但额外的地址计算和对象移动带来了更高的CPU开销。在art/runtime/gc/heap.h中定义了压缩复制的触发条件:
class Heap {
private:
bool ShouldPerformCompaction() {
// 当内存碎片率超过阈值时触发压缩
if (GetFragmentationRatio() > kCompactionThreshold) {
return true;
}
// 当长时间未执行压缩时触发
if (GetCurrentTime() - last_compaction_time_ > kMaxCompactionInterval) {
return true;
}
return false;
}
};
压缩复制通过牺牲部分回收速度换取更优的内存布局,适合对内存连续性要求较高的场景。
8.3 部分压缩策略
为平衡性能与内存连续性,ART实现了部分压缩策略。在art/runtime/gc/collector/partial_compacting_copying.cc中:
class PartialCompactingCopyingCollector {
public:
void Collect() {
Thread::SuspendAll();
RootScanner scanner;
MarkClosure mark_closure(this);
scanner.ScanRoots(&mark_closure);
ScanLiveObjectsInRegions(&mark_closure);
// 只对大对象区域进行压缩
CompactLargeObjectSpace();
// 对普通对象采用常规复制
CopyNormalObjects();
SwapSpaces();
Thread::ResumeAll();
}
private:
void CompactLargeObjectSpace() {
uint8_t* current = large_object_space_start_;
uint8_t* target = to_space_start_;
while (current < large_object_space_end_) {
ObjectHeader* header = reinterpret_cast<ObjectHeader*>(current);
if (header->IsMarked()) {
// 记录目标地址并复制
header->SetTargetAddress(target);
void* new_obj = CopyObjectTo(target, reinterpret_cast<void*>(header));
UpdateReferences(reinterpret_cast<void*>(header), new_obj);
target += header->size_;
}
current += header->size_;
}
to_space_start_ = target; // 更新To Space起点
}
};
通过选择性压缩,在减少内存碎片的同时控制了性能开销。
九、增量式复制优化
9.1 增量复制原理
增量式复制将GC过程分解为多个小阶段,在每个阶段执行部分GC工作后恢复应用线程,减少单次STW时间。在art/runtime/gc/collector/incremental_copying.cc中:
class IncrementalCopyingCollector {
public:
void Collect() {
// 初始标记阶段(STW)
InitialMark();
// 多次增量标记阶段
for (int i = 0; i < kNumIncrementalSteps; i++) {
IncrementalMark();
// 恢复应用线程执行一段时间
Thread::ResumeAll();
std::this_thread::sleep_for(std::chrono::milliseconds(kIncrementalPause));
Thread::SuspendAll();
}
// 最终标记阶段(STW)
FinalMark();
// 增量复制阶段
for (int i = 0; i < kNumIncrementalSteps; i++) {
IncrementalCopy();
Thread::ResumeAll();
std::this_thread::sleep_for(std::chrono::milliseconds(kIncrementalPause));
Thread::SuspendAll();
}
SwapSpaces();
Thread::ResumeAll();
}
private:
void IncrementalMark() {
// 标记部分对象
MarkNextBatchOfObjects();
}
void IncrementalCopy() {
// 复制部分对象
CopyNextBatchOfObjects();
}
};
9.2 增量复制的同步机制
为保证增量过程中的一致性,ART引入了特殊的同步机制:
class IncrementalCopyingCollector {
private:
// 写屏障回调函数
static void WriteBarrierCallback(void* obj, size_t field_offset, void* new_value) {
// 记录对象引用变化
RememberObjectChange(obj, field_offset, new_value);
}
void RememberObjectChange(void* obj, size_t field_offset, void* new_value) {
// 将引用变化添加到待处理队列
change_queue_.Push({obj, field_offset, new_value});
}
void ProcessRememberedChanges() {
// 在适当阶段处理引用变化
while (!change_queue_.Empty()) {
ChangeRecord record = change_queue_.Pop();
UpdateReference(record.obj, record.field_offset, record.new_value);
}
}
};
通过写屏障和引用变化记录,确保增量GC过程中对象图的一致性。
十、大对象处理优化
10.1 大对象独立空间管理
为避免大对象复制带来的性能开销,ART为其分配独立空间。在art/runtime/gc/heap.h中:
class Heap {
private:
uint8_t* large_object_space_start_;
uint8_t* large_object_space_end_;
uint8_t* large_object_space_top_;
bool IsLargeObject(size_t size) const {
return size > kLargeObjectThreshold;
}
void* AllocateLargeObject(size_t size) {
if (large_object_space_top_ + size > large_object_space_end_) {
// 扩展大对象空间
ExpandLargeObjectSpace(size);
}
void* result = large_object_space_top_;
large_object_space_top_ += size;
return result;
}
};
10.2 大对象的垃圾回收策略
大对象的回收采用特殊策略,在art/runtime/gc/collector/large_object_collector.cc中:
class LargeObjectCollector {
public:
void Collect() {
// 标记阶段跳过常规扫描,直接从根集合开始
RootScanner scanner;
MarkClosure mark_closure(this);
scanner.ScanRoots(&mark_closure);
// 扫描大对象空间
ScanLargeObjectSpace(&mark_closure);
// 回收未标记的大对象
ReclaimUnmarkedLargeObjects();
}
private:
void ScanLargeObjectSpace(Closure* closure) {
uint8_t* current = heap_->GetLargeObjectSpaceStart();
while (current < heap_->GetLargeObjectSpaceTop()) {
ObjectHeader* header = reinterpret_cast<ObjectHeader*>(current);
if (!header->IsMarked()) {
// 未标记对象,回收
ReclaimObject(current);
} else {
// 已标记对象,继续扫描
current += header->size_;
}
}
}
};
大对象不参与常规复制过程,减少了复制开销,提高了GC效率。
十一、引用处理优化
11.1 弱引用、软引用和虚引用的处理
ART对不同类型引用对象采用不同处理策略。在art/runtime/gc/reference_processor.cc中:
class ReferenceProcessor {
public:
void ProcessReferences() {
// 处理弱引用
ProcessWeakReferences();
// 处理软引用
ProcessSoftReferences();
// 处理虚引用
ProcessPhantomReferences();
}
private:
void ProcessWeakReferences() {
List<WeakReference*>* weak_list = GetWeakReferenceList();
for (WeakReference* ref : *weak_list) {
if (IsReferentCollected(ref->GetReferent())) {
// 引用对象已被回收,清除引用
ref->ClearReferent();
}
}
}
bool IsReferentCollected(void* referent) {
// 检查引用对象是否被标记
return !reinterpret_cast<ObjectHeader*>(referent)->IsMarked();
}
};
11.2 引用队列优化
为提高引用处理效率,ART实现了引用队列机制:
class ReferenceQueue {
public:
void Enqueue(Reference* ref) {
MutexLock lock(Thread::Current(), lock_);
queue_.PushBack(ref);
// 通知等待线程
condition_.Signal();
}
Reference* Dequeue(int64_t timeout_ms) {
MutexLock lock(Thread::Current(), lock_);
if (queue_.IsEmpty()) {
if (timeout_ms < 0) {
condition_.Wait(lock);
} else {
condition_.TimedWait(lock, timeout_ms);
}
}
if (queue_.IsEmpty()) {
return nullptr;
}
return queue_.PopFront();
}
private:
List<Reference*> queue_;
Mutex lock_;
ConditionVariable condition_;
};
通过引用队列,应用可以异步处理被回收对象的引用,减少GC停顿时间。
十二、并发预清理优化
12.1 预清理工作内容
为减少STW时间,ART在GC前执行并发预清理工作。在art/runtime/gc/collector/concurrent_precleaning.cc中:
class ConcurrentPrecleaning {
public:
void Start() {
// 启动预清理线程
precleaning_thread_ = std::thread([this]() {
PerformPrecleaning();
});
}
void WaitForCompletion() {
if (precleaning_thread_.joinable()) {
precleaning_thread_.join();
}
}
private:
void PerformPrecleaning() {
// 卸载未使用的类
UnloadUnusedClasses();
// 回收线程局部存储
ReclaimThreadLocalStorage();
// 处理JNI弱全局引用
ProcessJniWeakGlobalRefs();
}
};
12.2 预清理与GC的协同
预清理与GC主流程的协同机制:
class CopyingCollector {
public:
void Collect() {
// 启动并发预清理
ConcurrentPrecleaning precleaning;
precleaning.Start();
// 暂停应用线程,执行初始标记
Thread::SuspendAll();
InitialMark();
Thread::ResumeAll();
// 等待预清理完成
precleaning.WaitForCompletion();
// 执行剩余GC步骤
ConcurrentMark();
FinalMark();
CopyLiveObjects();
SwapSpaces();
}
};
通过并发预清理,将部分耗时操作提前执行,显著缩短了GC的STW时间。
十三、GC触发条件与启发式优化
13.1 基于内存压力的触发策略
ART根据内存使用情况动态触发GC。在art/runtime/gc/heap.cc中:
class Heap {
public:
void CheckGCNeeded(size_t allocation_size) {
// 计算当前内存使用和剩余空间
size_t used_memory = GetUsedMemory();
size_t free_memory = GetFreeMemory();
// 如果分配会导致内存不足,触发GC
if (used_memory + allocation_size > GetHeapSoftLimit()) {
TriggerGC(GcCause::kGcCauseHeapTrim);
}
}
private:
void TriggerGC(GcCause cause) {
// 选择合适的GC算法
GcCollector* collector = SelectCollectorForCause(cause);
// 执行GC
collector->Collect();
}
};
13.2 自适应GC启发式算法
ART实现了自适应GC策略,根据应用行为动态调整GC参数。在art/runtime/gc/adaptive_gc.cc中:
class AdaptiveGC {
public:
void AdjustGCParameters() {
// 分析历史GC数据
AnalyzeGCHistory();
// 根据应用行为调整新生代大小
AdjustYoungGenSize();
// 调整GC触发阈值
AdjustGCTriggerThreshold();
}
private:
void AnalyzeGCHistory() {
// 统计GC频率、耗时、回收内存量等指标
// 计算对象分配速率
allocation_rate_ = CalculateAllocationRate();
// 计算对象存活率
survival_rate_ = CalculateSurvivalRate();
}
};
通过自适应调整,GC性能在不同应用场景下都能保持最优状态。
十四、复制算法与其他GC算法的协作
14.1 分代GC架构
ART采用分代GC架构,复制算法主要用于新生代,而老年代采用标记-清除或标记-整理算法。在art/runtime/gc/heap.h中:
class Heap {
private:
CopyingCollector* young_gen_collector_;
MarkSweepCollector* old_gen_collector_;
void CollectYoungGen() {
young_gen_collector_->Collect();
}
void CollectOldGen() {
old_gen_collector_->Collect();
}
void CollectAllGenerations() {
CollectYoungGen();
CollectOldGen();
}
};
14.2 跨代引用处理
为处理跨代引用问题,ART引入了记忆集(Remembered Set)机制。在art/runtime/gc/remembered_set.cc中:
class RememberedSet {
public:
void AddReference(void* from, void* to) {
// 如果from在老年代,to在新生代,则记录该引用
if (IsInOldGen(from) && IsInYoungGen(to)) {
remembered_set_.Insert(from, to);
}
}
void ScanRememberedSet(Closure* closure) {
// 扫描记忆集,标记所有从老年代到新生代的引用
for (auto& entry : remembered_set_) {
closure->DoObject(entry.to);
}
}
private:
HashTable<RememberedReference> remembered_set_;
};
通过记忆集,新生代GC时无需扫描整个老年代,减少了扫描范围,提高了GC效率。
十五、复制算法的性能评估与实验
15.1 性能评估指标
ART使用多种指标评估复制算法性能,在art/runtime/gc/metrics.cc中:
class GCMetrics {
public:
void RecordGCStart(GcCause cause) {
start_time_ = GetCurrentTime();
gc_cause_ = cause;
}
void RecordGCEnd() {
end_time_ = GetCurrentTime();
duration_ = end_time_ - start_time_;
// 记录GC前后内存使用情况
pre_gc_used_memory_ = GetUsedMemoryBeforeGC();
post_gc_used_memory_ = GetUsedMemoryAfterGC();
reclaimed_memory_ = pre_gc_used_memory_ - post_gc_used_memory_;
// 记录吞吐量
throughput_ = CalculateThroughput();
}
private:
double CalculateThroughput() {
// 计算GC期间应用线程执行的时间比例
return (end_time_ - start_time_ - gc_time_) / (end_time_ - start_time_);
}
};
15.2 不同场景下的性能对比
通过实验对比不同复制算法优化策略的性能:
// 测试代码示例
void RunGCPerformanceTest() {
// 创建测试环境
Heap* heap = CreateTestHeap();
// 测试标准复制算法
GCMetrics metrics1;
RunTestWithAlgorithm(heap, new CopyingCollector(), metrics1);
// 测试并行复制算法
GCMetrics metrics2;
RunTestWithAlgorithm(heap, new ParallelCopyingCollector(), metrics2);
// 测试增量复制算法
GCMetrics metrics3;
RunTestWithAlgorithm(heap, new IncrementalCopyingCollector(), metrics3);
// 比较结果
CompareMetrics(metrics1, metrics2, metrics3);
}
实验数据表明,并行复制在多核设备上能减少约40%的STW时间,而增量复制能将最大停顿时间降低70%以上。
十六、复制算法的未来发展趋势
16.1 基于AI的预测性GC
未来可能引入AI模型预测对象生命周期,提前调整GC策略。设想中的实现:
class AIGuidedCopyingCollector {
public:
void Collect() {
// 使用AI模型预测对象存活率
PredictObjectSurvivalRates();
// 根据预测结果调整复制策略
AdjustCopyingStrategy();
// 执行常规GC流程
Thread::SuspendAll();
RootScanner scanner;
MarkClosure mark_closure(this);
scanner.ScanRoots(&mark_closure);
ScanLiveObjectsInRegions(&mark_closure);
CopyLiveObjects();
SwapSpaces();
Thread::ResumeAll();
}
private:
void PredictObjectSurvivalRates() {
// 提取对象特征
std::vector<ObjectFeature> features = ExtractObjectFeatures();
// 使用AI模型预测存活率
survival_rates_ = ai_model_.Predict(features);
}
};
16.2 与硬件协同优化
未来硬件可能提供专门的GC加速指令,ART可与之协同优化。设想的硬件加速接口:
class GCHardwareAccelerator {
public:
// 硬件加速对象复制
bool AccelerateObjectCopy(void* source, void* target, size_t size) {
// 调用硬件加速指令
return HardwareAPI::CopyObject(source, target, size);
}
// 硬件加速标记过程
bool AccelerateMarking(Object* root) {
return HardwareAPI::MarkObjects(root);
}
};
通过硬件加速,复制算法的关键操作性能可能提升一个数量级。