您现在的位置是:亿华云 > 系统运维

一篇带给你 V8 GC 的实现

亿华云2025-10-03 06:38:48【系统运维】7人已围观

简介前言:GC 是一个古老、复杂并且很 Cool 的技术,本文大概介绍一下早期 V8 中关于 GC 实现的部分,代码版本 0.1.5,早期版本利于快速理解整体的逻辑,因为现代版本已经非常复杂。Handle

前言:GC 是篇带一个古老、复杂并且很 Cool 的篇带技术,本文大概介绍一下早期 V8 中关于 GC 实现的篇带部分,代码版本 0.1.5,篇带早期版本利于快速理解整体的篇带逻辑,因为现代版本已经非常复杂。篇带

HandleScope 和 Handle

首先看一下 Handle 一般的篇带用法,Handle 是篇带 GC 非常核心的概念。

HandleScope scope;

Handle<JSObject> obj(object);

这两句代码里涉及了两个核心的篇带概念。首先看看 HandleScope。篇带

class HandleScope {

public:

// 记录之前的篇带上下文

HandleScope() : previous_(current_){

current_.extensions = 0;

}

~HandleScope() {

// 如果自己申请了内存,则释放

if (current_.extensions > 0) DeleteExtensions();

// 出栈,篇带指向前面那个

current_ = previous_;

}

static void** CreateHandle(void* value);

class Data {

public:

// 分配的篇带内存块数

int extensions;

// 下一个可用的位置

void** next;

// 达到limit执行的地址后说明当前内存块用完了

void** limit;

inline void Initialize() {

extensions = -1;

next = limit = NULL;

}

};

// 当前的HandleScope

static Data current_;

// 上一个HandleScope

const Data previous_;

}

首先看一下 Data 数据结构,Data 有三个字段 :

next 表示下次创建 Handle 时使用的篇带地址。 limit表示超过 limit 则需要重新申请内存,篇带V8 是按块申请的,limit 就是块的源码下载末地址。  extensions 表示该 HandleScope 申请了多少块内存。

接着继续看 current 类静态变量,这个变量记录了当前用于分配 Handle 的地址信息,接着看 HandleScope 类本身。HandleScope 是基于 RAII 机制管理一个函数中,多个 Handle 创建和销毁的对象。HandleScope 在构造函数中记录了当前的上下文,然后在析构函数中进行恢复。我们看到在 HandleScope 的构造函数中把当前上下文记录在 previous_ 里,然后重置 extensions,表示该 HandleScope 还没有申请内存,因为新的 HandleScope 会沿用前一个 HandleScope 的空闲内存,因为 V8 只把 current 赋值给 previous_ ,然后接着使用 current 去分配内存,只有当 HandleScope 内存不够时,才会分配新的内存块。看一下下面的图。

首先看一下上图的左边,当我们创建第一个 HandleScope 时,该 HandleScope 对象就会保存当前 HandleScope 的上下文,这时候当前 HandleScope 上下文是服务器托管 NULL,然后创建第一个 Handle 时因为 next == limit == NULL,所以需要申请一块内存,即 extensions 为 1。当创建第二个 Handle 后,就变成了左上角的样子。接着我们又定义了第二个 HandleScope,那么首先 第二个 HandleScope 同样在 previous_ 中记录当前的上下文,即 current 的值,接着在第二个 HandleScope 中分配一个 Handle 就变成了右图的样子。接下来看退出 HandleScope 时的逻辑,从代码中可以看到,首先判断本 HandleScope 是否创建了额外的内存块,是则释放,然后把 previous_ 中保存的上下文赋值给 current,那么 current 就恢复到了上一个 HandleScope 的上下文。

了解了 HandleScope 和 Handle 之后,我们接下来分析一下堆内存,主要是分析内存分配器、新生代和老生代(不包括代码区,map 区等)。

内存分配器 MemoryAllocator

V8 堆内存的云服务器申请和回收由内存分配器管理,新生代和老生代只是负责使用。首先看看 MemoryAllocator 的定义。

class MemoryAllocator : public AllStatic {

public:

static const int kMaxNofChunks = 1 << Page::kPageSizeBits;

static const int kPagesPerChunk = 64;

static const int kChunkSize = kPagesPerChunk * Page::kPageSize;

private:

// Maximum space size in bytes.

static int capacity_;

// Allocated space size in bytes.

static int size_;

// The initial chunk of virtual memory.

static VirtualMemory* initial_chunk_;

class ChunkInfo BASE_EMBEDDED {

public:

ChunkInfo() : address_(NULL), size_(0), owner_(NULL) { }

void init(Address a, size_t s, PagedSpace* o) {

address_ = a;

size_ = s;

owner_ = o;

}

Address address() { return address_; }

size_t size() { return size_; }

PagedSpace* owner() { return owner_; }

private:

Address address_;

size_t size_;

PagedSpace* owner_;

};

static Listchunks_;

static Listfree_chunk_ids_;

static int max_nof_chunks_;

static int top_;

};

MemoryAllocator 中由 chunks_ 负责管理具体的内存,chunks_ 是一个 ChunkInfo 链表,从 ChunkInfo 中可以看到它主要记录了内存的起始地址和大小,以及所属的 space(新生代、老生代)。

接下来看 MemoryAllocator 的初始化。

// Setup memory allocator and allocate an initial chunk of memory. The

// initialchunk is double the size of the new space to ensure that we can

// find a pair of semispaces that are contiguous and aligned to their size.

// capacity 最大的内存大小(新生代 + 老生代)

bool MemoryAllocator::Setup(int capacity) {

// 页的整数倍

capacity_ = RoundUp(capacity, Page::kPageSize);

// 最大的chunk数,

max_nof_chunks_ = (capacity_ / (kChunkSize - Page::kPageSize)) + 4;

size_ = 0;

ChunkInfo info;

for (int i = max_nof_chunks_ - 1; i >= 0; i--) {

chunks_.Add(info);

free_chunk_ids_.Add(i);

}

top_ = max_nof_chunks_;

return true;

}

主要是初始化了内部的一些数据结构,没有实质的操作。接下来才是分配内存。

void* chunk = MemoryAllocator::ReserveInitialChunk(2 * young_generation_size_);

接着看 ReserveInitialChunk。

void* MemoryAllocator::ReserveInitialChunk(const size_t requested) {

// 新建一个VM对象,分配size的虚拟内存,记录在VM对象

initial_chunk_ = new VirtualMemory(requested);

size_ += requested;

// 返回分配内存的首地址

return initial_chunk_->address();}VirtualMemory::VirtualMemory(size_t size, void* address_hint) {

// 映射一块内存

address_ = mmap(address_hint, size, PROT_NONE,

MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,

kMmapFd, kMmapFdOffset);

size_ = size;

}

这就完成了内存的申请。

新生代堆内存管理// 刚才分配的空间中,老生代在开始位置

Address old_space_start = reinterpret_cast

(chunk);

// 紧接着是新生代,算出大小是young_generation_size_的n倍,值大于old_space_start的最小值

Address new_space_start = RoundUp(old_space_start, young_generation_size_);

// 代码空间等于新生代开始+新生代大小Address code_space_start = new_space_start + young_generation_size_;

// 老生代空间大小int old_space_size = new_space_start - old_space_start;

/

*

因为chunk的空间两倍的young_generation_size_,新生代大小占了一半,

所以还有一半,剩下的一半老生代占了old_space_size,所以剩下的代码区大小

*/

因为chunk的空间两倍的young_generation_size_,新生代大小占了一半,

所以还有一半,剩下的一半老生代占了old_space_size,所以剩下的代码区大小

*/int code_space_size = young_generation_size_ - old_space_size;/

*

|young_generation_size_|

chunk => -----------------------------------

^ ^ ^ ^

| | | |

old new code end*/

// 分配一个管理新生代地址空间的对象,传入初始值和最大值

// 因为新生代分配from和to,所以这两个初始化值是每个空间的属性

new_space_ = new NewSpace(initial_semispace_size_, semispace_size_);

进行了一系列的计算后,开始初始化新生代。接下来看 NewSpace。

class NewSpace : public Malloced {

public:

// Create a new space with a given allocation capacity (ie, the capacity of

// *one* of the semispaces). The constructor does not allocate heap memory

// from the OS. When the space is set up, it is given a contiguous chunk of

// memory of size 2 * semispace_capacity. To support fast containment

// testing in the new space, the size of this chunk must be a power of two

// and it must be aligned to its size.

NewSpace(int initial_semispace_capacity, int maximum_semispace_capacity);

private:

int capacity_;

int maximum_capacity_;

// The semispaces.

SemiSpace* to_space_;

SemiSpace* from_space_;

Address start_;

uint32_t address_mask_;

uint32_t object_mask_;

uint32_t object_expected_;

/

*

struct AllocationInfo {

Address top; // current allocation top

Address limit; // current allocation limit

};

*/

// 管理新生代内存的数据结构

AllocationInfo allocation_info_;

AllocationInfo mc_forwarding_info_;

};

新生代分为两个 SemiSpace。结构差不多,就不再介绍。了解了定义,看看初始化的逻辑。

// 设置需要管理的地址空间,start是首地址,size是大小bool NewSpace::Setup(Address start, int size) {

// to区

if (to_space_ == NULL

|| !to_space_->Setup(start, maximum_capacity_)) {

return false;

}

// from区,和to区一人一半

if (from_space_ == NULL

|| !from_space_->Setup(start + maximum_capacity_, maximum_capacity_)) {

return false;

}

// 开始地址

start_ = start;

/

*

address_mask的高位是地址的有效位,

size是只有一位为一,减一后一变成0,一右边

的全部0位变成1,然后取反,高位的0变成1,再加上size中本来的1,

即从左往右的1位地址有效位

*/

address_mask_ = ~(size - 1);

// 计算堆对象地址掩码,低位是标记位,判断的时候需要保留,kHeapObjectTag是堆对象的标记

object_mask_ = address_mask_ | kHeapObjectTag;

// 见 contains 函数,对象地址里低位是标记位,判断的时候需要带上

object_expected_ = reinterpret_cast(start) | kHeapObjectTag;

// 初始化管理的内存信息

allocation_info_.top = to_space_->low();

allocation_info_.limit = to_space_->high();

mc_forwarding_info_.top = NULL;

mc_forwarding_info_.limit = NULL;

return true;

}

至此,新生代的数据结构和初始化分析完成。接下来看老生代,老生代的数据结构和新生代差不多,不再分析,区别是老生代的内存是用链表管理的,而不是连续的内存,直接看初始化流程。

bool PagedSpace::Setup(Address start, size_t size) {

int num_pages = 0;

first_page_ = MemoryAllocator::CommitPages(start, size, this, &num_pages);

// 初始化page链表,清除记录集

for (Page* p = first_page_; p->is_valid(); p = p->next_page()) {

p->ClearRSet();

}

// 设置管理内存分配的结构体,为第一个空闲页

SetAllocationInfo(&allocation_info_, first_page_);

return true;

}

接着看 CommitPages。

// 保存和管理虚拟地址start到start+size的空间

Page* MemoryAllocator::CommitPages(Address start, size_t size, PagedSpace* owner, int* num_pages) {

// chunk中的页数,一个chunk包括多个页

*num_pages = PagesInChunk(start, size);

// 保存内存信息到chunkInfo

int chunk_id = Pop();

chunks_[chunk_id].init(start, size, owner);

// 按页初始化内存信息

return InitializePagesInChunk(chunk_id, *num_pages, owner);

}

继续看 InitializePagesInChunk。

// 初始化某块内存为page管理的结构

Page* MemoryAllocator::InitializePagesInChunk(int chunk_id, int pages_in_chunk, PagedSpace* owner) {

Address chunk_start = chunks_[chunk_id].address();

// 算出有效的开始地址,即要对齐

Address low = RoundUp(chunk_start, Page::kPageSize);

Address page_addr = low;

for (int i = 0; i < pages_in_chunk; i++) {

Page* p = Page::FromAddress(page_addr);

// 保存下一页的地址和当前所属的chunk_id

p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id;

// 不是large object

p->is_normal_page = 1;

// 指向下一页地址

page_addr += Page::kPageSize;

}

// page_addr此时执行最后一页的末尾,减去一页大小得到最后一页的起始地址

Page* last_page = Page::FromAddress(page_addr - Page::kPageSize);

// 下一页地址为0

last_page->opaque_header = OffsetFrom(0) | chunk_id;

return Page::FromAddress(low);

}

一个内存块里面包括多个 Page,上面的代码是按 Page 为单位初始化内存。整体流程完成后,结构图如下。

新建对象。

分析完数据结构和初始化流程,接下来看创建对象时的内存分配。

Localv8::Object::New() {

i::Handleobj = i::Factory::NewJSObject(i::Top::object_function());

return Utils::ToLocal(obj);

}

HandleFactory::NewJSObject(Handleconstructor, PretenureFlag pretenure) {

// CALL_HEAP_FUNCTION 会在分配失败时进行 GC

CALL_HEAP_FUNCTION(Heap::AllocateJSObject(*constructor, pretenure), JSObject);

}

Object* Heap::AllocateJSObject(JSFunction* constructor, PretenureFlag pretenure) {

return AllocateJSObjectFromMap(constructor->initial_map(), pretenure);

}

Object* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) {

Object* properties = AllocatePropertyStorageForMap(map);

// Allocate the JSObject.

// 选择分配的 Space

AllocationSpace space = (pretenure == TENURED) ? OLD_SPACE : NEW_SPACE;

// 对象所需内存太大(map 中记录了大小),则直接在大对象 Space 分配

if (map->instance_size() > MaxHeapObjectSize())

space = LO_SPACE;

// 分配内存

Object* obj = Allocate(map, space);

// Initialize the JSObject.

InitializeJSObjectFromMap(JSObject::cast(obj),

FixedArray::cast(properties),

map);

return obj;

}

接着看 Allocate。

// 根据map的type分配一个对象的空间,然后设置该对象的map属性是map

Object* Heap::Allocate(Map* map, AllocationSpace space) {

Object* result = AllocateRaw(map->instance_size(), space);

// 转成HeapObject才有set_map函数

HeapObject::cast(result)->set_map(map);

return result;

}

// 在哪块空间分配多少内存

Object* Heap::AllocateRaw(int size_in_bytes, AllocationSpace space) {

// 在新生代中分配内存

if (NEW_SPACE == space) {

return new_space_->AllocateRaw(size_in_bytes);

}

Object* result;

// 在对应的空间分配大小

if (OLD_SPACE == space) {

result = old_space_->AllocateRaw(size_in_bytes);

} else if (CODE_SPACE == space) {

result = code_space_->AllocateRaw(size_in_bytes);

} else if (LO_SPACE == space) {

result = lo_space_->AllocateRaw(size_in_bytes);

} else {

result = map_space_->AllocateRaw(size_in_bytes);

}

return result;

}

Object* AllocateRaw(int size_in_bytes) {

// allocation_info_ 就是初始化时记录的内存信息,allocation_info_ 负责管理内存的分配

return AllocateRawInternal(size_in_bytes, &allocation_info_);

}

Object* NewSpace::AllocateRawInternal(int size_in_bytes, AllocationInfo* alloc_info) {

// alloc_info->top保存了该空间当前可分配的地址,加上当前申请分配的

Address new_top = alloc_info->top + size_in_bytes;

// 超过了内存大小

if (new_top > alloc_info->limit) {

return Failure::RetryAfterGC(size_in_bytes, NEW_SPACE);

}

// 地址+低一位的标记,转成堆对象的地址表示,即低位置1

Object* obj = HeapObject::FromAddress(alloc_info->top);

// 更新指针,指向下一块可分配的内存

alloc_info->top = new_top;

return obj;

}

我们可以看到分配内存是非常快的,只需要移动一下指针。

垃圾回收

但是有一个需要注意的是,当内存不够时就需要进行 GC,具体处理逻辑在之前代码的 CALL_HEAP_FUNCTION 宏中。

#define CALL_HEAP_FUNCTION(FUNCTION_CALL, TYPE) \

do { \

GC_GREEDY_CHECK(); \

Object* __object__ = FUNCTION_CALL; \

if (__object__->IsFailure()) { \

if (__object__->IsRetryAfterGC()) { \

if (!Heap::CollectGarbage( \

Failure::cast(__object__)->requested(), \

Failure::cast(__object__)->allocation_space())) { \ /* TODO(1181417): Fix this. */ \

v8::internal::V8::FatalProcessOutOfMemory("CALL_HEAP_FUNCTION"); \ } \

__object__ = FUNCTION_CALL; \ if (__object__->IsFailure()) { \ if (__object__->IsRetryAfterGC()) { \ /* TODO(1181417): Fix this. */ \

v8::internal::V8::FatalProcessOutOfMemory("CALL_HEAP_FUNCTION"); \ } \ return Handle<TYPE>(); \ } \ } else { \ return Handle<TYPE>(); \ } \ } \ return Handle<TYPE>(TYPE::cast(__object__)); \ } while (false)

重点看 Heap::CollectGarbage。

bool Heap::CollectGarbage(int requested_size, AllocationSpace space) {

// The VM is in the GC state until exiting this function.

VMState state(GC);

{ GCTracer tracer;

GarbageCollectionPrologue();

// 选择GC算法

GarbageCollector collector = SelectGarbageCollector(space);

tracer.set_collector(collector);

StatsRate* rate = (collector == SCAVENGER)

? &Counters::gc_scavenger : &Counters::gc_compactor;

rate->Start();

// 开始垃圾回收

PerformGarbageCollection(space, collector);

rate->Stop();

GarbageCollectionEpilogue();

}

// GC 后内存是否能满足当前需要分配的内存

switch (space) {

case NEW_SPACE:

return new_space_->Available() >= requested_size;

case OLD_SPACE:

return old_space_->Available() >= requested_size;

case CODE_SPACE:

return code_space_->Available() >= requested_size;

case MAP_SPACE:

return map_space_->Available() >= requested_size;

case LO_SPACE:

return lo_space_->Available() >= requested_size;

}

return false;

}

早期的 V8 GC 是同步的,还没有并行、并发等优化逻辑,继续 PerformGarbageCollection。在 V8 初始化时会初始化新生代堆内存的数据结构。

void Heap::PerformGarbageCollection(AllocationSpace space, GarbageCollector collector) {

if (collector == MARK_COMPACTOR) {

MarkCompact();

int promoted_space_size = PromotedSpaceSize();

promoted_space_limit_ =

promoted_space_size + Max(2 * MB, (promoted_space_size/100) * 35);

old_gen_exhausted_ = false;

if (space == NEW_SPACE && !MarkCompactCollector::HasCompacted()) {

Scavenge();

}

} else {

Scavenge();

}

}

这里我们可以看到经常听到的 Scavenge 和 MarkCompact 算法。首先看 Scavenge。

void Heap::Scavenge() {

gc_state_ = SCAVENGE;

// from区和to区互换,to区变成空白,from区则分配了各种对象

new_space_->Flip();

// 重置to区的指针,因为to区变空闲了

new_space_->ResetAllocationInfo();

CopyVisitor copy_visitor;

// 遍历根,复制对象到另一个区

IterateRoots(©_visitor);

// 设置临界线,以此判断新生代对象是否可以经历了二次 GC,即可以晋升带老生代

new_space_->set_age_mark(new_mark);

gc_state_ = NOT_IN_GC;

}

实际的逻辑比这个负责很多,不过这里只是大致了解流程。CopyVisitor 对象负责把对象复制到另一个区。

class CopyVisitor: public ObjectVisitor {

public:

void VisitPointer(Object** p) {

CopyObject(p);

}

void VisitPointers(Object** start, Object** end) {

// Copy all HeapObject pointers in [start, end)

for (Object** p = start; p < end; p++) CopyObject(p);

}

private:

void CopyObject(Object** p) {

if (!Heap::InFromSpace(*p)) return;

Heap::CopyObject(reinterpret_cast(p));

}

};

继续看 Heap::CopyObject。

void Heap::CopyObject(HeapObject** p) {

ASSERT(InFromSpace(*p));

HeapObject* object = *p;

// map 对象记录了对象的元信息

HeapObject* first_word = object->map();

int object_size = object->SizeFromMap(Map::cast(first_word));

Object* result;

// 是否满足晋升条件

/

*

bool Heap::ShouldBePromoted(Address old_address, int object_size) {

// 小于age_mark说明已经经过了一次gc回收了,第二次的时候还存活则可以晋升到老生代

// age_mark是from区的值,但是是在from区和to区交换之后,所以其实是to区的值

return old_address < new_space_->age_mark()

|| (new_space_->Size() + object_size) >= (new_space_->Capacity() >> 2);

}

*/

if (ShouldBePromoted(object->address(), object_size)) {

bool has_pointers =

type != HEAP_NUMBER_TYPE &&

(type >= FIRST_NONSTRING_TYPE ||

String::cast(object)->representation_tag() != kSeqStringTag);

// 在对应的 Space 分配内存,把对象复制过去

if (has_pointers) {

result = old_space_->AllocateRaw(object_size);

} else {

result = code_space_->AllocateRaw(object_size);

}

if (!result->IsFailure()) {

*p = MigrateObject(p, HeapObject::cast(result), object_size);

if (has_pointers) {

promoted_top -= kPointerSize;

Memory::Object_at(promoted_top) = *p;

}

return;

}

}

// 不晋升的复制到新生代的另一个区,下次还存活再晋升

result = new_space_->AllocateRaw(object_size);

*p = MigrateObject(p, HeapObject::cast(result), object_size);

}

MigrateObject 实现了对象的复制。

HeapObject* Heap::MigrateObject(HeapObject** source_p,

HeapObject* target,

int size) {

void** src = reinterpret_cast((*source_p)->address());

void** dst = reinterpret_cast(target->address());

int counter = size/kPointerSize - 1;

do {

*dst++ = *src++;

} while (counter-- > 0);

(*source_p)->set_map(reinterpret_cast(target));

return target;

}

至此就完成了新生代 GC 的分析,紧接着分析老生代的。

void Heap::MarkCompact() {

gc_state_ = MARK_COMPACT;

MarkCompactPrologue();

// 开始回收

MarkCompactCollector::CollectGarbage();

MarkCompactEpilogue();

gc_state_ = NOT_IN_GC;

}

老生代 GC 比较复杂。

void MarkCompactCollector::CollectGarbage() {

Prepare();

// 标记

MarkLiveObjects();

// 清除大对象

SweepLargeObjectSpace();

// 是否压缩,压缩是为了防止内存碎片,否则直接清楚

if (compacting_collection_) {

EncodeForwardingAddresses();

UpdatePointers();

RelocateObjects();

RebuildRSets();

} else {

SweepSpaces();

}

Finish();

}

这里我们只分析标记和清除,V8 早期也还没有并行标记,延迟清除等逻辑。

void MarkCompactCollector::MarkLiveObjects() {

// 新生代有一半空间时空闲的,可以使用,marking_stack用于遍历扫描对象的数据结构

marking_stack.Initialize(Heap::new_space()->FromSpaceLow(),

Heap::new_space()->FromSpaceHigh());

MarkingVisitor marking_visitor;

// 从根开始遍历,进行标记

Heap::IterateStrongRoots(&marking_visitor);

while (true) {

// IterateStrongRoots 会不断压对象进栈,这里继续遍历,直到为空

MarkObjectsReachableFromTopFrame();

// 遍历的时候,栈可能溢出,内存不够,ok 的话继续遍历其他的

if (!marking_stack.overflowed()) {

if (has_processed_weak_pointers) break;

GlobalHandles::MarkWeakRoots(&MustBeMarked);

GlobalHandles::IterateWeakRoots(&marking_visitor);

has_processed_weak_pointers = true;

continue;

}

// 清除标记

marking_stack.clear_overflowed();

// 继续遍历,到栈为空

ScanOverflowedObjects(&lo_it);

}

}

首先看如何从根开始遍历。

void Heap::IterateStrongRoots(ObjectVisitor* v) {

// 遍历 Handle,忽略其他根

HandleScopeImplementer::Iterate(v);

}

void HandleScopeImplementer::Iterate(ObjectVisitor* v) {

ImplementationUtilities::HandleScopeData* current =

ImplementationUtilities::CurrentHandleScope();

Iterate(v, thread_local.Blocks(), current);

}

主要看 ImplementationUtilities::CurrentHandleScope()。

static HandleScopeData* CurrentHandleScope() {

return &v8::HandleScope::current_;

}

v8::HandleScope::current_ 就是前面介绍的 HandleScope。

void HandleScopeImplementer::Iterate(

ObjectVisitor* v,

List* blocks,

ImplementationUtilities::HandleScopeData* handle_data) {

// Iterate over all handles in the blocks except for the last.

for (int i = blocks->length() - 2; i >= 0; --i) {

Object** block =

reinterpret_cast

v->VisitPointers(block, &block[kHandleBlockSize]);

}

// Iterate over live handles in the last block (if any).

if (!blocks->is_empty()) {

v->VisitPointers(reinterpret_cast

reinterpret_cast

}

}

Iterate 实现遍历全部的 Handle。接着看遍历时对对象的处理。具体实现在 MarkingVisitor。

void VisitPointer(Object** p) {

MarkObjectByPointer(p);

}

void MarkObjectByPointer(Object** p) {

Object* obj = *p;

if (!obj->IsHeapObject()) return;

MarkCompactCollector::MarkObject(HeapObject::cast(obj));

}

static inline void MarkObject(HeapObject* obj) {

// 没有标记则标记

if (!is_marked(obj)) MarkUnmarkedObject(obj);

}

void MarkCompactCollector::MarkUnmarkedObject(HeapObject* obj) {

// 打上标记

set_mark(obj);

// 押入栈,扫描它的子指针,类似遍历树

if (!marking_stack.overflowed()) {

marking_stack.Push(obj);

} else {

// Set objects stack overflow bit, wait for rescan.

// 溢出了则后面继续扫描

set_overflow(obj);

}

}

扫描和标记完全部对象后就进行清除。

void MarkCompactCollector::SweepSpaces() {

SweepSpace(Heap::old_space(), &DeallocateOldBlock);

}

static void SweepSpace(PagedSpace* space, DeallocateFunction dealloc) {

PageIterator it(space, PageIterator::PAGES_IN_USE);

while (it.has_next()) {

Page* p = it.next();

bool is_previous_alive = true;

Address free_start = NULL;

HeapObject* object;

// 遍历老生代的每一个对象,没有标记的则清除(回收内存)

for (Address current = p->ObjectAreaStart();

current < p->AllocationTop();

current += object->Size()) {

object = HeapObject::FromAddress(current);

// 如果标记,则先清除,然后回收(如果连续的内存则一起回收)

if (is_marked(object)) {

clear_mark(object);

if (!is_previous_alive) { // Transition from free to live.

dealloc(free_start, current - free_start);

is_previous_alive = true;

}

} else {

if (is_previous_alive) { // Transition from live to free.

free_start = current;

is_previous_alive = false;

}

}

}

if (!is_previous_alive) {

int free_size = p->AllocationTop() - free_start;

if (free_size > 0) {

dealloc(free_start, free_size);

}

}

}

}

接着看 dealloc 的回收逻辑。

void MarkCompactCollector::DeallocateOldBlock(Address start,

int size_in_bytes) {

// 清除记录集,记录集记录了老生代执行新生代的指针

Heap::ClearRSetRange(start, size_in_bytes);

Heap::old_space()->Free(start, size_in_bytes);

}

void Free(Address start, int size_in_bytes) {

int wasted_bytes = free_list_.Free(start, size_in_bytes);

accounting_stats_.DeallocateBytes(size_in_bytes);

accounting_stats_.WasteBytes(wasted_bytes);

}

int OldSpaceFreeList::Free(Address start, int size_in_bytes) {

FreeListNode* node = FreeListNode::FromAddress(start);

node->set_size(size_in_bytes);

int index = size_in_bytes >> kPointerSizeLog2;

node->set_next(free_[index].head_node_);

free_[index].head_node_ = node->address();

available_ += size_in_bytes;

needs_rebuild_ = true;

return 0;

}

这里的内存并不是直接释放,而是存到空闲链表,后续使用。可参考前面的图。

很赞哦!(24)