日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
V8堆外內(nèi)存ArrayBuffer垃圾回收的實(shí)現(xiàn)

1.創(chuàng)建 ArrayBuffer

ArrayBuffer 的創(chuàng)建有很多種方式,比如在 JS 層創(chuàng)建 Uint8Array 或者 ArrayBuffer(對應(yīng)實(shí)現(xiàn) builtins-arraybuffer.cc),又比如自己在 C++ 層調(diào)用 V8 提供的 API 進(jìn)行創(chuàng)建,它們最終對應(yīng)的實(shí)現(xiàn)是一樣的。為了簡單起見,這里以通過 V8 API 創(chuàng)建的方式進(jìn)行分析。對應(yīng)頭文件是 v8-array-buffer.h 的 ArrayBuffer。創(chuàng)建方式有很多種,這里以最簡單的方式進(jìn)行分析。

static Local New(Isolate* isolate, size_t byte_length);

通過調(diào)用 ArrayBuffer::New 就可以創(chuàng)建一個 ArrayBuffer 對象。來看看具體實(shí)現(xiàn)。

Local v8::ArrayBuffer::New(Isolate* isolate, size_t byte_length) {
i::Isolate* i_isolate = reinterpret_cast(isolate);
i::MaybeHandle result =
i_isolate->factory()->NewJSArrayBufferAndBackingStore(
byte_length, i::InitializedFlag::kZeroInitialized);

i::Handle array_buffer;
if (!result.ToHandle(&array_buffer)) {
// ...
}

return Utils::ToLocal(array_buffer);
}

首先看 NewJSArrayBufferAndBackingStore。

MaybeHandle Factory::NewJSArrayBufferAndBackingStore(
size_t byte_length, InitializedFlag initialized,
AllocationType allocation) {
std::unique_ptr backing_store = nullptr;

if (byte_length > 0) {
// 分配一塊內(nèi)存
backing_store = BackingStore::Allocate(isolate(), byte_length,
SharedFlag::kNotShared, initialized);
}
// map 標(biāo)記對象的類型
Handle map(isolate()->native_context()->array_buffer_fun().initial_map(),
isolate());
// 新建一個 JSArrayBuffer 對象,默認(rèn)在新生代申請內(nèi)存
auto array_buffer = Handle::cast(NewJSObjectFromMap(map, allocation));
// 關(guān)聯(lián) JSArrayBuffer 和 內(nèi)存
array_buffer->Setup(SharedFlag::kNotShared, ResizableFlag::kNotResizable,
std::move(backing_store));
return array_buffer;
}

NewJSArrayBufferAndBackingStore 的邏輯非常多,每一步都是需要了解的,我們逐句分析。

std::unique_ptr BackingStore::Allocate(
Isolate* isolate, size_t byte_length, SharedFlag shared,
InitializedFlag initialized) {
void* buffer_start = nullptr;
// 獲取 array_buffer 內(nèi)存分配器,由 V8 調(diào)用方提供
auto allocator = isolate->array_buffer_allocator();
if (byte_length != 0) {
auto allocate_buffer = [allocator, initialized](size_t byte_length) {
if (initialized == InitializedFlag::kUninitialized) {
return allocator->AllocateUninitialized(byte_length);
}
void* buffer_start = allocator->Allocate(byte_length);
return buffer_start;
};
// 執(zhí)行 allocate_buffer 函數(shù)分配內(nèi)存
buffer_start = isolate->heap()->AllocateExternalBackingStore(
allocate_buffer, byte_length);
}
// 交給 BackingStore 管理
auto result = new BackingStore(buffer_start, // start
byte_length, // length
byte_length, // max length
byte_length, // capacity
shared, // shared
ResizableFlag::kNotResizable, // resizable
false, // is_wasm_memory
true, // free_on_destruct
false, // has_guard_regions
false, // custom_deleter
false); // empty_deleter
// 設(shè)置一些上下文,銷毀內(nèi)存是用
/*
void BackingStore::SetAllocatorFromIsolate(Isolate* isolate) {
type_specific_data_.v8_api_array_buffer_allocator = isolate->array_buffer_allocator();
}
*/
result->SetAllocatorFromIsolate(isolate);
return std::unique_ptr(result);
}

首先獲取 array_buffer_allocator 內(nèi)存分配器,該分配器由 V8 的調(diào)用方提供,比如 Node.js 的 NodeArrayBufferAllocator。然后通過該分配器分配內(nèi)存,通常是通過 calloc,malloc 等函數(shù)分配內(nèi)存。不過這里不是直接分配,而是通過封裝一個函數(shù)交給 AllocateExternalBackingStore 函數(shù)進(jìn)行處理。

void* Heap::AllocateExternalBackingStore(
const std::function& allocate, size_t byte_length) {
// 執(zhí)行函數(shù)分配內(nèi)存
void* result = allocate(byte_length);
// 成功則返回
if (result) return result;
// 失敗則進(jìn)行 GC 后再次執(zhí)行
if (!always_allocate()) {
for (int i = 0; i < 2; i++) {
CollectGarbage(OLD_SPACE,
GarbageCollectionReason::kExternalMemoryPressure);
result = allocate(byte_length);
if (result) return result;
}
isolate()->counters()->gc_last_resort_from_handles()->Increment();
CollectAllAvailableGarbage(
GarbageCollectionReason::kExternalMemoryPressure);
}
return allocate(byte_length);
}

AllocateExternalBackingStore 主要是為了在分配內(nèi)存失敗時,進(jìn)行 GC 嘗試騰出一些內(nèi)存。分配完內(nèi)存后,就把這塊內(nèi)存交給 BackingStore 管理。BackingStore 就不進(jìn)行分析了,主要是記錄了內(nèi)存的一些信息,比如開始和結(jié)束地址。拿到一塊內(nèi)存后就會創(chuàng)建一個 JSArrayBuffer 對象進(jìn)行關(guān)聯(lián)。JSArrayBuffer 是 ArrayBuffer 在 V8 中的具體實(shí)現(xiàn)。接著看。

auto array_buffer = Handle::cast(NewJSObjectFromMap(map, allocation));

NewJSObjectFromMap 根據(jù) map 在 allocation 指示的地方分配一個內(nèi)存用來存儲 JSArrayBuffer 對象。map 表示對象的類型,allocation 表示在哪個 space 分配這塊內(nèi)存,默認(rèn)是新生代。來看下 NewJSObjectFromMap。

Handle Factory::NewJSObjectFromMap(
Handle map, AllocationType allocation,
Handle allocation_site) {

JSObject js_obj = JSObject::cast(AllocateRawWithAllocationSite(map, allocation, allocation_site));

InitializeJSObjectFromMap(js_obj, *empty_fixed_array(), *map);

return handle(js_obj, isolate());
}

AllocateRawWithAllocationSite 最終調(diào)用 allocator()->AllocateRawWith 在新生代分配了一塊內(nèi)存,大小是一個 JSArrayBuffer 的內(nèi)存,因?yàn)?JSArrayBuffer 是 JSObject 的子類,所以上面可以轉(zhuǎn)成 JSObject 進(jìn)行一些操作,完成之后我們就拿到了一個 JSArrayBuffer 對象。接著看最后一步。

array_buffer->Setup(SharedFlag::kNotShared, ResizableFlag::kNotResizable, std::move(backing_store));

Setup 是把申請的 BackingStore 對象和 JSArrayBuffer 對象關(guān)聯(lián)起來,JSArrayBuffer 對象不涉及存儲數(shù)據(jù)的內(nèi)存,它只是保存了一些元信息,比如內(nèi)存大小。具體存儲數(shù)據(jù)的內(nèi)存由 BackingStore 管理??纯?Setup 的實(shí)現(xiàn)。

void JSArrayBuffer::Setup(SharedFlag shared, ResizableFlag resizable,
std::shared_ptr backing_store) {
clear_padding();
set_bit_field(0);
set_is_shared(shared == SharedFlag::kShared);
set_is_resizable(resizable == ResizableFlag::kResizable);
set_is_detachable(shared != SharedFlag::kShared);
for (int i = 0; i < v8::ArrayBuffer::kEmbedderFieldCount; i++) {
SetEmbedderField(i, Smi::zero());
}
set_extension(nullptr);
Attach(std::move(backing_store));
}

做了一些初始化處理,然后調(diào)用 Attach。

void JSArrayBuffer::Attach(std::shared_ptr backing_store) {

Isolate* isolate = GetIsolate();
set_backing_store(isolate, backing_store->buffer_start());
set_byte_length(backing_store->byte_length());
set_max_byte_length(backing_store->max_byte_length());
// 創(chuàng)建 ArrayBufferExtension 對象
ArrayBufferExtension* extension = EnsureExtension();
// 內(nèi)存大小
size_t bytes = backing_store->PerIsolateAccountingLength();
// 關(guān)聯(lián)起來
extension->set_accounting_length(bytes);
extension->set_backing_store(std::move(backing_store));
// 注冊到管理 GC 的對象中
isolate->heap()->AppendArrayBufferExtension(*this, extension);
}

Attach 是最重要的邏輯,首先把 BackingStore 對象保存到 JSArrayBuffer 對象中,然后通過 EnsureExtension 創(chuàng)建了一個 ArrayBufferExtension 對象,ArrayBufferExtension 是為了 GC 管理。

ArrayBufferExtension* JSArrayBuffer::EnsureExtension() {
ArrayBufferExtension* extension = this->extension();
if (extension != nullptr) return extension;
extension = new ArrayBufferExtension(std::shared_ptr());
set_extension(extension);
return extension;
}

ArrayBufferExtension 對象保存了內(nèi)存的大小和其管理對象 BackingStore。最終形成的關(guān)系如下。

對象關(guān)聯(lián)完畢后,通過 isolate->heap()->AppendArrayBufferExtension(*this, extension); 把 ArrayBufferExtension 對象注冊到負(fù)責(zé)管理 GC 的對象中。

void Heap::AppendArrayBufferExtension(JSArrayBuffer object,
ArrayBufferExtension* extension) {
array_buffer_sweeper_->Append(object, extension);
}

array_buffer_sweeper_ 是 ArrayBufferSweeper 對象,該對象在 V8 初始化時創(chuàng)建,看一下它的 Append 函數(shù)。

void ArrayBufferSweeper::Append(JSArrayBuffer object,
ArrayBufferExtension* extension) {
size_t bytes = extension->accounting_length();
if (Heap::InYoungGeneration(object)) {
young_.Append(extension);
} else {
old_.Append(extension);
}
// 通知 V8 堆外內(nèi)存的大小增加 bytes 字節(jié)
IncrementExternalMemoryCounters(bytes);
}

ArrayBufferSweeper 維護(hù)了新生代和老生代兩個隊(duì)列,根據(jù) JSArrayBuffer 對象在哪個 space 來決定插入哪個隊(duì)列,剛出分析過,JSArrayBuffer 默認(rèn)在新生代創(chuàng)建。

void ArrayBufferList::Append(ArrayBufferExtension* extension) {
if (head_ == nullptr) {
head_ = tail_ = extension;
} else {
tail_->set_next(extension);
tail_ = extension;
}

const size_t accounting_length = extension->accounting_length();
bytes_ += accounting_length;
extension->set_next(nullptr);
}

Append 就是把對象插入隊(duì)列,并且更新已經(jīng)分配的內(nèi)存大小。這樣就完成了一個 ArrayBuffer 對象的創(chuàng)建。

2.ArrayBuffer GC 的實(shí)現(xiàn)

接著看 GC 的邏輯,具體在 RequestSweep 函數(shù),該函數(shù)在幾個地方被調(diào)用,比如新生代進(jìn)行 GC 時。

void ScavengerCollector::SweepArrayBufferExtensions() {
heap_->array_buffer_sweeper()->RequestSweep(
ArrayBufferSweeper::SweepingType::kYoung);
}

看一下這個函數(shù)的功能。

void ArrayBufferSweeper::RequestSweep(SweepingType type) {

if (young_.IsEmpty() && (old_.IsEmpty() || type == SweepingType::kYoung))
return;
// 做一些準(zhǔn)備工作
Prepare(type);
auto task = MakeCancelableTask(heap_->isolate(), [this, type] {
base::MutexGuard guard(&sweeping_mutex_);
job_->Sweep();
job_finished_.NotifyAll();
});
job_->id_ = task->id();
V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
}

首先看 Prepare。

void ArrayBufferSweeper::Prepare(SweepingType type) {
switch (type) {
case SweepingType::kYoung: {
job_ = std::make_unique(std::move(young_), ArrayBufferList(),
type);
young_ = ArrayBufferList();
} break;
case SweepingType::kFull: {
job_ = std::make_unique(std::move(young_), std::move(old_),
type);
young_ = ArrayBufferList();
old_ = ArrayBufferList();
} break;
}
}

這里根據(jù) GC 類型創(chuàng)建一個 SweepingJob 任務(wù)和重置 young_ 隊(duì)列(已經(jīng)交給 SweepingJob 處理了),準(zhǔn)備好之后,然后提交一個 task 給線程池。當(dāng)線程池調(diào)度該任務(wù)執(zhí)行時,就會執(zhí)行 job_->Sweep()。

void ArrayBufferSweeper::SweepingJob::Sweep() {
switch (type_) {
case SweepingType::kYoung:
SweepYoung();
break;
case SweepingType::kFull:
SweepFull();
break;
}
state_ = SweepingState::kDone;
}

根據(jù) GC 類型進(jìn)行處理,這里是新生代。

void ArrayBufferSweeper::SweepingJob::SweepYoung() {
// 新生代當(dāng)前待處理的隊(duì)列
ArrayBufferExtension* current = young_.head_;

ArrayBufferList new_young;
ArrayBufferList new_old;
// 遍歷對象
while (current) {
ArrayBufferExtension* next = current->next();
// 可以被 GC 了則直接刪除
if (!current->IsYoungMarked()) {
size_t bytes = current->accounting_length();
delete current;
if (bytes) freed_bytes_.fetch_add(bytes, std::memory_order_relaxed);
} else if (current->IsYoungPromoted()) { // 晉升到老生代,則把它重新放到老生代
current->YoungUnmark();
new_old.Append(current);
} else { // 否則放回新生代
current->YoungUnmark();
new_young.Append(current);
}

current = next;
}
// GC 更新當(dāng)前隊(duì)列
old_ = new_old;
young_ = new_young;
}

遍歷對象的過程中,V8 會把可以 GC 的對象直接刪除,因?yàn)?ArrayBufferExtension 中是使用 std::shared_ptr 對 BackingStore 進(jìn)行管理,所以 ArrayBufferExtension 被刪除后,BackingStore 也會被刪除,來看看 BackingStore 的析構(gòu)函數(shù)。

BackingStore::~BackingStore() {
// 是否需要在析構(gòu)函數(shù)中銷毀管理的內(nèi)存,通常是需要
if (free_on_destruct_) {
// 拿到內(nèi)存分配器,然后釋放之前申請的內(nèi)存,通常是 free 函數(shù)
auto allocator = get_v8_api_array_buffer_allocator();
allocator->Free(buffer_start_, byte_length_);
}
// 重置字段
Clear();
}

至此,就完成了 ArrayBuffer 的 GC 過程的分析。


網(wǎng)站題目:V8堆外內(nèi)存ArrayBuffer垃圾回收的實(shí)現(xiàn)
本文網(wǎng)址:http://m.5511xx.com/article/cddojsp.html