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

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

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
如何追蹤JS對(duì)象是否被GC

在自帶垃圾回收的語(yǔ)言中,開(kāi)發(fā)者往往不需要過(guò)多地關(guān)注內(nèi)存管理。但是不代表我們可以完全忽略它。因?yàn)檎Z(yǔ)言引擎的垃圾回收是有一定的判斷規(guī)則的,如果我們的變量所引用的內(nèi)存沒(méi)有符合這個(gè)規(guī)則,那么引擎無(wú)無(wú)法對(duì)這些內(nèi)存進(jìn)行自動(dòng)回收。所以如何追蹤變量的內(nèi)存是否被回收也變得非常重要,尤其在 Node.js 中。

創(chuàng)新互聯(lián)主要從事網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)烏當(dāng),十余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):18980820575

因?yàn)?Node.js 通常以服務(wù)器的角色長(zhǎng)期提供服務(wù),一旦服務(wù)發(fā)生內(nèi)存泄露,就意味著我們的服務(wù)遲早會(huì)掛掉,盡管服務(wù)可以被自動(dòng)重啟,但是這并不能從根本上解決問(wèn)題。所以如何檢測(cè)內(nèi)存泄露,就變得非常重要。

我們通常會(huì)使用 V8 自帶的堆快照來(lái)判斷某些變量的內(nèi)存是否沒(méi)有得到正確的回收,這是一種非常有效的手段,因?yàn)槲覀冊(cè)诙芽煺罩锌梢詫?shí)時(shí)看到當(dāng)前所有 JS 對(duì)象的存活情況。但是快照是一種非常重的操作,因?yàn)樗粌H會(huì)阻塞線程的執(zhí)行,而且會(huì)導(dǎo)致內(nèi)存的暴漲,前者導(dǎo)致我們的服務(wù)出現(xiàn)短暫的不可用,具體時(shí)間取決于進(jìn)程的堆大小,堆內(nèi)存過(guò)大時(shí),采集堆快照所引起的內(nèi)存暴漲可能會(huì)導(dǎo)致進(jìn)程直接掛掉。下面介紹一種輕量級(jí)的內(nèi)存泄露檢測(cè)方式,雖然它不像堆快照那么強(qiáng)大,但是在某些場(chǎng)景下是有用的。

當(dāng)我們想知道一個(gè)對(duì)象有沒(méi)有被回收時(shí),有幾種方式,第一種就是通過(guò)引擎提供的快照能力,直接查看對(duì)象的存活情況,第二種則是注冊(cè)對(duì)象被 GC 時(shí)的回調(diào),下面是介紹的第二種能力。引擎沒(méi)有直接提供當(dāng)對(duì)象被 GC 時(shí)回調(diào)的能力,但是我們可以通過(guò)引擎提供的弱引用技術(shù)來(lái)實(shí)現(xiàn)這個(gè)功能(可參考 Node.js 的源碼)。

const { createHook, AsyncResource } = require('async_hooks');
const weakMap = new WeakMap();


let gcCallbackContext = {};
let hooks;


function trackGC(obj, gcCallback) {
if (!hooks) {
hooks = createHook({
destroy(id) {
if (gcCallbackContext[id]) {
gcCallbackContext[id]();
delete gcCallbackContext[id];
}
}
}).enable();
}
const gcTracker = new AsyncResource('none');
gcCallbackContext[gcTracker.asyncId()] = gcCallback;
weakMap.set(obj, gcTracker);
}

接著分析下代碼的實(shí)現(xiàn),主要是利用了 WeakMap 和 async_hooks 實(shí)現(xiàn)了這個(gè)功能。當(dāng)我們需要追蹤一個(gè)對(duì)象是否被 GC 時(shí),我們只需要傳入這個(gè)對(duì)象和一個(gè)回調(diào),然后調(diào)用 trackGC。trackGC 首先會(huì)針對(duì)一個(gè)被追蹤的對(duì)象生成一個(gè)關(guān)聯(lián)的 AsyncResource 對(duì)象。并且記錄 AsyncResource id 和 回調(diào)的對(duì)應(yīng)關(guān)系,然后把再通過(guò) WeakMap 把被追蹤的對(duì)象和 AsyncResource 對(duì)象關(guān)聯(lián)起來(lái)。那么當(dāng)被追蹤的對(duì)象失去所有引用時(shí),它關(guān)聯(lián)的 AsyncResource 對(duì)象就會(huì)被回收,從而 async_hooks 的 destroy 鉤子被回調(diào),這時(shí)候執(zhí)行開(kāi)發(fā)者注冊(cè)的回調(diào)通知開(kāi)發(fā)者該對(duì)象已經(jīng)被 GC。接下來(lái)看看 如何使用。

const { trackGC } = require('../index');
function memory() {
return ~~(process.memoryUsage().heapUsed / 1024 / 1024);
}


console.log(`before new Array: ${memory()} MB`);


let key = {
a: new Array(1024 * 1024 * 10)
};


let key2 = {
a: new Array(1024 * 1024 * 10)
};


console.log(`after new Array: ${memory()} MB`);
trackGC(key, () => {
console.log("key gc");
});


trackGC(key2, () => {
console.log("key2 gc");
});


global.gc();


key = null;
key2 = null;


global.gc();


console.log(`after gc: ${memory()} MB`);

在上面的例子中,首先打印出初始化的進(jìn)程內(nèi)存,接著分配一塊大的內(nèi)存,注冊(cè)對(duì)象的 GC 回調(diào),把變量賦值為 null 使得它的關(guān)聯(lián)的對(duì)象失去唯一的強(qiáng)引用,從而被 GC,最后進(jìn)行顯式 GC 并輸出這時(shí)候的內(nèi)存。下面是我電腦上的輸出。

before new Array: 3 MB
after new Array: 163 MB
after gc: 2 MB
key gc
key2 gc

可以看到注冊(cè)的 GC 回調(diào)被執(zhí)行了,并且內(nèi)存的確被回收了。

最后分析一下這個(gè)實(shí)現(xiàn)。這里主要是利用了 async_hooks 模塊的能力,因?yàn)?WeakMap 是沒(méi)有提供回調(diào)機(jī)制的。來(lái)看一下 AsyncResource 的實(shí)現(xiàn),只列出核心代碼。

constructor(type, opts = kEmptyObject) {
const asyncId = newAsyncId();
this[async_id_symbol] = asyncId;
this[trigger_async_id_symbol] = triggerAsyncId;
registerDestroyHook(this, asyncId, ...);
}

當(dāng)創(chuàng)建一個(gè) AsyncResource 對(duì)象時(shí),會(huì)調(diào)用 registerDestroyHook。

class DestroyParam {
public:
double asyncId;
Environment* env;
Global target;
Global propBag;};static void RegisterDestroyHook(const FunctionCallbackInfo& args) {


Isolate* isolate = args.GetIsolate();
DestroyParam* p = new DestroyParam();
p->asyncId = args[1].As()->Value();
p->env = Environment::GetCurrent(args);
p->target.Reset(isolate, args[0].As());
p->target.SetWeak(p, AsyncWrap::WeakCallback, WeakCallbackType::kParameter);
p->env->AddCleanupHook(DestroyParamCleanupHook, p);
}

RegisterDestroyHook 首先創(chuàng)建了一個(gè) DestroyParam 對(duì)象保存一些上下文,然后利用 V8 的弱引入對(duì)象可以注冊(cè)回調(diào)的機(jī)制設(shè)置需要追蹤的對(duì)象的 GC 回調(diào)。那么當(dāng)對(duì)象失去所有強(qiáng)引用被 GC 時(shí),回調(diào)就會(huì)被執(zhí)行。

void AsyncWrap::WeakCallback(const WeakCallbackInfo& info) {
HandleScope scope(info.GetIsolate());


std::unique_ptr p{info.GetParameter()};
Local prop_bag = PersistentToLocal::Default(info.GetIsolate(),
p->propBag);
Local val;


p->env->RemoveCleanupHook(DestroyParamCleanupHook, p.get());


if (!prop_bag.IsEmpty() &&
!prop_bag->Get(p->env->context(), p->env->destroyed_string())
.ToLocal(&val)) {
return;
}


if (val.IsEmpty() || val->IsFalse()) {
AsyncWrap::EmitDestroy(p->env, p->asyncId);
}
}

最終通過(guò) EmitDestroy 回調(diào) JS 層執(zhí)行 destroy 鉤子。這樣就實(shí)現(xiàn)了追蹤 JS 對(duì)象是否被 GC 的能力。具體可以參考 https://github.com/theanarkh/gc-tracker。


本文標(biāo)題:如何追蹤JS對(duì)象是否被GC
當(dāng)前URL:http://www.5511xx.com/article/cocdgee.html