码字不易,请大佬们点点关注,谢谢~
一、内存碎片化问题概述
在Android Runtime(ART)的内存管理体系中,内存碎片化是影响内存使用效率和应用性能的关键问题。随着应用程序不断进行内存分配与释放操作,原本连续的内存空间会被分割成大量不连续的小块,导致后续大内存分配请求难以满足,即使总空闲内存足够,也会因缺乏连续空间而失败。内存碎片化分为外部碎片化和内部碎片化:外部碎片化指空闲内存总量充足,但分散为小块无法满足大内存请求;内部碎片化则是已分配内存中存在未使用的部分。ART通过多种策略协同处理这两类碎片化问题,其核心逻辑分布在art/runtime/gc/、art/runtime/memory/等目录的源码中。
二、内存碎片化检测机制
2.1 碎片化程度评估指标
ART通过计算多个指标量化内存碎片化程度,主要在art/runtime/gc/heap.cc中实现。核心指标包括:
- 空闲内存块平均大小:计算所有空闲内存块的平均尺寸,若该值过小,说明碎片化严重。
// Heap类中计算空闲内存块平均大小的方法
size_t Heap::CalculateAverageFreeBlockSize() const {
size_t total_free_size = 0;
size_t num_free_blocks = 0;
// 遍历所有空闲链表(不同大小类别的空闲内存块链表)
for (size_t i = 0; i < kNumSizeClasses; ++i) {
FreeList* free_list = free_lists_[i];
FreeBlock* current = free_list->GetHead();
while (current != nullptr) {
total_free_size += current->size_;
num_free_blocks++;
current = current->next_;
}
}
// 避免除零错误
return (num_free_blocks > 0)? total_free_size / num_free_blocks : 0;
}
- 空闲内存块数量与分布:统计不同大小的空闲块数量,分析其分布情况。若小尺寸空闲块过多,表明碎片化加剧。
- 碎片化率:通过公式
(总空闲内存 - 最大连续空闲内存) / 总空闲内存计算,直观反映内存碎片化比例。
2.2 动态监测与触发机制
ART采用定期检测与事件触发相结合的方式监测碎片化。在art/runtime/gc/gc_controller.cc中,GcController类负责管理GC触发逻辑:
class GcController {
public:
void MonitorFragmentation() {
// 定期检查(如每5秒一次)
std::thread([this]() {
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(5));
size_t avg_free_size = heap_->CalculateAverageFreeBlockSize();
// 若平均空闲块大小低于阈值,触发碎片化处理
if (avg_free_size < kFragmentationThreshold * heap_->GetHeapSize()) {
TriggerDefragmentation();
}
}
}).detach();
}
void TriggerDefragmentation() {
// 选择合适的碎片化处理策略(如标记-压缩GC)
GcCollector* collector = SelectDefragmentationCollector();
collector->Collect();
}
private:
static const double kFragmentationThreshold = 0.05; // 阈值设定为总堆内存的5%
Heap* heap_;
};
此外,在内存分配失败时,也会立即检测碎片化并尝试处理,避免因碎片化导致的分配失败问题持续恶化。
三、基于垃圾回收的碎片化处理策略
3.1 标记-压缩(Mark-Compact)算法
标记-压缩算法是ART处理碎片化的核心手段,在art/runtime/gc/collector/mark_compact_collector.cc中实现。其执行流程如下:
- 标记阶段:暂停所有应用线程(Stop The World),从根集合(如栈、静态变量)开始,标记所有可达对象。
class MarkCompactCollector {
private:
void MarkPhase() {
Thread::SuspendAll();
RootScanner scanner;
MarkClosure mark_closure(this);
scanner.ScanRoots(&mark_closure);
// 扫描堆内存,标记可达对象
ScanHeapForLiveObjects(&mark_closure);
Thread::ResumeAll();
}
void ScanHeapForLiveObjects(Closure* closure) {
uint8_t* current = heap_->GetHeapStart();
uint8_t* end = heap_->GetHeapEnd();
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;
}
}
};
- 压缩阶段:将所有存活对象移动到内存一端,紧凑排列,同时回收空闲内存。
class MarkCompactCollector {
private:
void CompactPhase() {
Thread::SuspendAll();
uint8_t* target = heap_->GetHeapStart();
uint8_t* current = heap_->GetHeapStart();
while (current < heap_->GetHeapEnd()) {
ObjectHeader* header = reinterpret_cast<ObjectHeader*>(current);
size_t size = header->size_;
if (header->IsMarked()) {
// 复制存活对象到目标位置
memcpy(target, current, size);
// 更新对象引用(指向新地址)
UpdateObjectReferences(current, target);
target += size;
}
current += size;
}
// 更新堆内存边界
heap_->SetHeapEnd(target);
Thread::ResumeAll();
}
};
通过标记-压缩,ART可有效消除外部碎片化,提供连续的空闲内存空间。
3.2 分代回收策略优化
ART采用分代回收架构,针对不同生命周期的对象采用不同策略。新生代(Young Generation)使用复制算法快速回收短命对象,避免碎片化;老年代(Old Generation)则通过标记-压缩处理碎片化。在art/runtime/gc/gc_policy.cc中定义分代策略:
class GcPolicy {
public:
GcType SelectGcTypeForGeneration(Generation generation) {
if (generation == kYoungGeneration) {
return kCopying; // 新生代用复制算法
} else {
return kMarkCompact; // 老年代用标记-压缩算法
}
}
};
分代策略减少了不必要的内存移动,提高了碎片化处理效率。例如,新生代对象存活率低,复制算法能快速回收空间且无碎片;老年代对象存活率高,标记-压缩更适合处理碎片化。
四、内存分配器优化策略
4.1 伙伴系统(Buddy System)
伙伴系统是ART处理大块内存分配的重要手段,在art/runtime/gc/allocator/buddy_allocator.cc中实现。其核心思想是将内存按2的幂次方大小划分,通过合并和分割操作减少碎片化。
class BuddyAllocator {
private:
// 内存块链表,每个链表存储特定大小的空闲块
BuddyBlock* free_lists_[kMaxBuddyLevels];
// 计算满足需求的最小2的幂次方内存块级别
int CalculateBuddyLevel(size_t size) {
int level = 0;
size_t block_size = 1;
while (block_size < size) {
block_size <<= 1;
level++;
}
return level;
}
// 分配内存块
void* Allocate(size_t size) {
int level = CalculateBuddyLevel(size);
for (int i = level; i < kMaxBuddyLevels; ++i) {
if (free_lists_[i] != nullptr) {
BuddyBlock* block = free_lists_[i];
RemoveFromList(block, i);
if (i > level) {
// 分割大块内存
SplitBlock(block, i);
return Allocate(size);
}
return block->data;
}
}
return nullptr; // 分配失败
}
// 回收内存块
void Free(void* block) {
BuddyBlock* buddy_block = reinterpret_cast<BuddyBlock*>(block - offsetof(BuddyBlock, data));
int level = buddy_block->level;
BuddyBlock* buddy = GetBuddy(buddy_block);
if (buddy != nullptr && buddy->IsFree()) {
// 合并相邻的伙伴块
MergeBlocks(buddy_block, buddy);
Free(buddy_block);
return;
}
AddToList(buddy_block, level); // 无法合并则加入链表
}
};
伙伴系统通过高效的分割与合并,确保大块内存分配时减少碎片化,提高内存利用率。
4.2 空闲链表管理优化
ART使用空闲链表(Free List)管理不同大小的空闲内存块,在art/runtime/gc/allocator/free_list.cc中实现。为减少碎片化,采用以下策略:
- 按大小分类:将空闲块按尺寸分为多个链表,分配时优先从合适大小的链表中查找,避免大材小用。
class FreeList {
public:
FreeList(size_t size_class) : size_class_(size_class) {}
FreeBlock* GetHead() const { return head_; }
// 插入空闲块到链表
void InsertBlock(FreeBlock* block) {
block->next_ = head_;
if (head_ != nullptr) {
head_->prev_ = block;
}
head_ = block;
}
// 从链表中移除并分配空闲块
FreeBlock* AllocateBlock(size_t size) {
FreeBlock* current = head_;
while (current != nullptr) {
if (current->size_ >= size) {
RemoveFromList(current);
return current;
}
current = current->next_;
}
return nullptr;
}
private:
FreeBlock* head_;
size_t size_class_;
};
- 合并相邻空闲块:在内存释放时,检查相邻块是否空闲,若空闲则合并,减少小碎片数量。
class FreeList {
private:
void MergeAdjacentBlocks() {
FreeBlock* current = head_;
while (current != nullptr) {
FreeBlock* next = current->next_;
if (next != nullptr && IsAdjacent(current, next)) {
current->size_ += next->size_;
RemoveFromList(next);
}
current = current->next_;
}
}
bool IsAdjacent(FreeBlock* block1, FreeBlock* block2) {
return reinterpret_cast<uint8_t*>(block1->data) + block1->size_ == reinterpret_cast<uint8_t*>(block2->data);
}
};
五、内存整理(Defragmentation)后台任务
5.1 定期内存整理机制
为避免碎片化问题累积,ART启动后台线程定期执行内存整理任务,在art/runtime/gc/background_defragmentation.cc中实现:
class BackgroundDefragmentationTask : public Task {
public:
BackgroundDefragmentationTask(Heap* heap) : heap_(heap) {}
void Run() override {
while (true) {
std::this_thread::sleep_for(std::chrono::minutes(10)); // 每10分钟检查一次
size_t avg_free_size = heap_->CalculateAverageFreeBlockSize();
if (avg_free_size < kFragmentationThreshold * heap_->GetHeapSize()) {
// 碎片化严重,执行标记-压缩GC
GcCollector* collector = new MarkCompactCollector(heap_);
collector->Collect();
delete collector;
}
}
}
private:
Heap* heap_;
static const double kFragmentationThreshold = 0.05;
};
后台任务在系统负载较低时执行,减少对应用性能的影响。
5.2 增量式内存整理
为进一步降低停顿时间,ART支持增量式内存整理,将整理过程拆分为多个小阶段。在art/runtime/gc/collector/incremental_mark_compact.cc中实现:
class IncrementalMarkCompactCollector {
public:
void Collect() {
// 初始标记阶段(STW)
InitialMarkPhase();
// 多次增量标记阶段
for (int i = 0; i < kNumIncrementalSteps; ++i) {
IncrementalMarkPhase();
Thread::ResumeAll();
std::this_thread::sleep_for(std::chrono::milliseconds(kIncrementalPause));
Thread::SuspendAll();
}
// 最终标记阶段(STW)
FinalMarkPhase();
// 增量压缩阶段
for (int i = 0; i < kNumIncrementalSteps; ++i) {
IncrementalCompactPhase();
Thread::ResumeAll();
std::this_thread::sleep_for(std::chrono::milliseconds(kIncrementalPause));
Thread::SuspendAll();
}
Thread::ResumeAll();
}
private:
// 各阶段具体实现...
};
增量式整理通过分阶段执行和写屏障(Write Barrier)记录对象引用变化,在减少停顿时间的同时保证内存整理的正确性。
六、内存压缩(Memory Compression)技术
6.1 压缩原理与实现
当物理内存不足且碎片化严重时,ART利用内存压缩技术缓解压力。在art/runtime/memory/memory_compression.cc中实现:
class MemoryCompressor {
public:
void CompressMemory() {
// 检查内存压力(如空闲内存低于阈值)
if (IsMemoryPressureHigh()) {
PageTable* page_table = GetCurrentProcessPageTable();
for (size_t i = 0; i < kNumPageTableEntries; ++i) {
PageTableEntry* entry = &page_table->entries_[i];
if (entry->is_present &&!entry->is_dirty && entry->last_access_time < kLongTimeThreshold) {
void* physical_page = entry->physical_address;
// 压缩页面数据
CompressPage(physical_page);
entry->is_present = false;
entry->compressed_data = GetCompressedData(physical_page);
}
}
}
}
private:
bool IsMemoryPressureHigh() const {
// 获取系统空闲内存并与阈值比较
size_t free_memory = GetFreePhysicalMemory();
return free_memory < kLowMemoryThreshold;
}
void CompressPage(void* physical_page) {
// 调用压缩算法(如zlib库)
char* compressed_data = CompressData(reinterpret_cast<char*>(physical_page), kPageSize);
// 存储压缩后的数据
StoreCompressedData(compressed_data);
}
};
内存压缩将不常用页面数据压缩存储,释放物理内存,减少因碎片化导致的内存分配失败。
6.2 解压缩与数据恢复
当再次访问压缩页面时,需进行解压缩:
class MemoryCompressor {
public:
void* DecompressPage(void* compressed_data) {
// 分配新的物理页面
void* new_physical_page = AllocatePhysicalPage();
if (new_physical_page != nullptr) {
char* decompressed_data = DecompressData(compressed_data);
memcpy(new_physical_page, decompressed_data, kPageSize);
free(decompressed_data);
}
return new_physical_page;
}
};
内存压缩技术在不释放内存的情况下提升了内存使用效率,是应对碎片化和内存压力的有效补充手段。
七、内存分配策略动态调整
7.1 基于负载的分配策略切换
ART根据应用负载动态调整内存分配策略。在art/runtime/gc/allocator/allocator_policy.cc中实现:
class AllocatorPolicy {
public:
Allocator* SelectAllocator() {
// 统计最近一段时间的内存分配模式
AllocationPattern pattern = AnalyzeAllocationPattern();
if (pattern == kLargeObjectDominant) {
return new BuddyAllocator(); // 适合大块内存分配
} else if (pattern == kSmallObjectFrequent) {
return new FreeListAllocator(); // 适合小对象频繁分配
}
return nullptr;
}
private:
AllocationPattern AnalyzeAllocationPattern() {
// 统计不同大小内存分配的频率和占比
// 根据阈值判断分配模式
}
};
通过动态切换分配策略,减少因策略不当导致的碎片化问题。
7.2 自适应内存分配阈值
ART还会根据内存使用情况调整分配阈值。例如,在内存紧张时缩小TLAB(Thread-Local Allocation Buffer)大小,避免过度分配导致碎片化:
class Heap {
public:
void AdjustTlabSize() {
size_t free_memory = GetFreeMemory();
if (free_memory < kLowMemoryThreshold) {