V8的内存回收机制之Mark-Compact的Sweep功能
其定义位于//v8/src/heap/mark-compact.ccMarkCompactCollector::Sweep函数:
完成工作:

  • 按照特定顺序清理v8堆中的各个内存空间(大对象,普通页,新生代对象)
  • 检查是否为MemoryChunk::BLACK_ALLOCATED标志位,代表为存活对象,不是则进行下一步检查
  • 检查是否标记过,是则代表存活,否则代表对象已死亡,需要释放;普通页以及新生代内存释放操作在evacuate阶段进行。
    showLineNumbers
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    void MarkCompactCollector::Sweep() {
    DCHECK(!sweeper_->sweeping_in_progress());
    DCHECK(queued_pages_to_be_freed_.empty());

    sweeper_->InitializeMajorSweeping();

    TRACE_GC_EPOCH_WITH_FLOW(
    heap_->tracer(), GCTracer::Scope::MC_SWEEP, ThreadKind::kMain,
    sweeper_->GetTraceIdForFlowEvent(GCTracer::Scope::MC_SWEEP),
    TRACE_EVENT_FLAG_FLOW_OUT);
    #ifdef DEBUG
    state_ = SWEEP_SPACES;
    #endif

    {
    GCTracer::Scope sweep_scope(heap_->tracer(), GCTracer::Scope::MC_SWEEP_LO,
    ThreadKind::kMain);
    SweepLargeSpace(heap_->lo_space());
    }
    {
    GCTracer::Scope sweep_scope(
    heap_->tracer(), GCTracer::Scope::MC_SWEEP_CODE_LO, ThreadKind::kMain);
    SweepLargeSpace(heap_->code_lo_space());
    }
    if (heap_->shared_space()) {
    GCTracer::Scope sweep_scope(heap_->tracer(),
    GCTracer::Scope::MC_SWEEP_SHARED_LO,
    ThreadKind::kMain);
    SweepLargeSpace(heap_->shared_lo_space());
    }
    {
    GCTracer::Scope sweep_scope(heap_->tracer(), GCTracer::Scope::MC_SWEEP_OLD,
    ThreadKind::kMain);
    StartSweepSpace(heap_->old_space());
    }
    {
    GCTracer::Scope sweep_scope(heap_->tracer(), GCTracer::Scope::MC_SWEEP_CODE,
    ThreadKind::kMain);
    StartSweepSpace(heap_->code_space());
    }
    if (heap_->shared_space()) {
    GCTracer::Scope sweep_scope(
    heap_->tracer(), GCTracer::Scope::MC_SWEEP_SHARED, ThreadKind::kMain);
    StartSweepSpace(heap_->shared_space());
    }
    {
    GCTracer::Scope sweep_scope(
    heap_->tracer(), GCTracer::Scope::MC_SWEEP_TRUSTED, ThreadKind::kMain);
    StartSweepSpace(heap_->trusted_space());
    }
    if (heap_->shared_trusted_space()) {
    GCTracer::Scope sweep_scope(
    heap_->tracer(), GCTracer::Scope::MC_SWEEP_SHARED, ThreadKind::kMain);
    StartSweepSpace(heap_->shared_trusted_space());
    }
    {
    GCTracer::Scope sweep_scope(heap_->tracer(),
    GCTracer::Scope::MC_SWEEP_TRUSTED_LO,
    ThreadKind::kMain);
    SweepLargeSpace(heap_->trusted_lo_space());
    }
    if (v8_flags.minor_ms && heap_->new_space()) {
    GCTracer::Scope sweep_scope(heap_->tracer(), GCTracer::Scope::MC_SWEEP_NEW,
    ThreadKind::kMain);
    StartSweepNewSpace();
    }

    sweeper_->StartMajorSweeping();
    }
    存在三种sweep函数
  • SweepLargeSpace:大对象空间
  • StartSweepSpace:普通分页空间
  • StartSweepNewSpace:新生代空间(如果开启minor-ms)
    最终启动并发清理StartMajorSweeping
    MarkCompactCollector::SweepLargeSpace,处理大对象空间:
    showLineNumbers
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    void MarkCompactCollector::SweepLargeSpace(LargeObjectSpace* space) {
    PtrComprCageBase cage_base(heap_->isolate());
    size_t surviving_object_size = 0;
    const bool postpone_freeing = ShouldPostponeFreeingEmptyPages(space);
    const bool add_to_pool =
    v8_flags.large_page_pool && space->identity() == NEW_LO_SPACE;
    DCHECK_IMPLIES(add_to_pool, !postpone_freeing);
    std::vector<LargePageMetadata*> pages_to_pool;
    if (add_to_pool) {
    pages_to_pool.reserve(space->memory_chunk_list().size());
    }
    for (auto it = space->begin(); it != space->end();) {
    LargePageMetadata* current = *(it++);
    DCHECK(!current->Chunk()->IsFlagSet(MemoryChunk::BLACK_ALLOCATED));
    Tagged<HeapObject> object = current->GetObject();
    if (!marking_state_->IsMarked(object)) {
    // Object is dead and page can be released.
    space->RemovePage(current);
    if (postpone_freeing) {
    queued_pages_to_be_freed_.push_back(current);
    } else if (add_to_pool) {
    pages_to_pool.push_back(current);
    } else {
    heap_->memory_allocator()->Free(MemoryAllocator::FreeMode::kImmediately,
    current);
    }
    continue;
    }
    if (!v8_flags.sticky_mark_bits) {
    MarkBit::From(object).Clear();
    current->SetLiveBytes(0);
    }
    current->marking_progress_tracker().ResetIfEnabled();
    surviving_object_size += static_cast<size_t>(object->Size(cage_base));
    }
    space->set_objects_size(surviving_object_size);

    if (add_to_pool && !pages_to_pool.empty()) {
    heap()->memory_allocator()->FreeLargePagesPooled(std::move(pages_to_pool));
    }
    }

MarkCompactCollector::StartSweepSpace函数,处理普通页:

showLineNumbers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
void MarkCompactCollector::StartSweepSpace(PagedSpace* space) {
DCHECK_NE(NEW_SPACE, space->identity());
space->ClearAllocatorState();

int will_be_swept = 0;
bool unused_page_present = false;

Sweeper* sweeper = heap_->sweeper();

// Loop needs to support deletion if live bytes == 0 for a page.
for (auto it = space->begin(); it != space->end();) {
PageMetadata* p = *(it++);
DCHECK(p->SweepingDone());

if (p->Chunk()->IsEvacuationCandidate()) {
DCHECK(!p->Chunk()->IsFlagSet(MemoryChunk::BLACK_ALLOCATED));
DCHECK_NE(NEW_SPACE, space->identity());
// Will be processed in Evacuate.
continue;
}

// If the page is black, just reset the flag and don't add the page to the
// sweeper.
if (p->Chunk()->IsFlagSet(MemoryChunk::BLACK_ALLOCATED)) {
ResetAndRelinkBlackAllocatedPage(space, p);
continue;
}

// One unused page is kept, all further are released before sweeping them.
if (p->live_bytes() == 0) {
if (unused_page_present) {
if (v8_flags.gc_verbose) {
PrintIsolate(heap_->isolate(), "sweeping: released page: %p",
static_cast<void*>(p));
}
ReleasePage(space, p);
continue;
}
unused_page_present = true;
}

sweeper->AddPage(space->identity(), p);
will_be_swept++;
}

if (v8_flags.sticky_mark_bits && space->identity() == OLD_SPACE) {
static_cast<StickySpace*>(space)->set_old_objects_size(space->Size());
}

if (v8_flags.gc_verbose) {
PrintIsolate(heap_->isolate(),
"sweeping: space=%s initialized_for_sweeping=%d",
ToString(space->identity()), will_be_swept);
}
}

MarkCompactCollector::SweepNewSpace函数,处理新生代对象:

showLineNumbers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 void MarkCompactCollector::StartSweepNewSpace() {
PagedSpaceForNewSpace* paged_space = heap_->paged_new_space()->paged_space();
paged_space->ClearAllocatorState();

int will_be_swept = 0;

heap_->StartResizeNewSpace();

DCHECK(empty_new_space_pages_to_be_swept_.empty());
for (auto it = paged_space->begin(); it != paged_space->end();) {
PageMetadata* p = *(it++);
DCHECK(p->SweepingDone());
DCHECK(!p->Chunk()->IsFlagSet(MemoryChunk::BLACK_ALLOCATED));

if (p->live_bytes() > 0) {
// Non-empty pages will be evacuated/promoted.
continue;
}

if (paged_space->ShouldReleaseEmptyPage()) {
ReleasePage(paged_space, p);
} else {
empty_new_space_pages_to_be_swept_.push_back(p);
}
will_be_swept++;
}

if (v8_flags.gc_verbose) {
PrintIsolate(heap_->isolate(),
"sweeping: space=%s initialized_for_sweeping=%d",
ToString(paged_space->identity()), will_be_swept);
}
}