成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何追蹤 JS 對象是否被 GC

開發 前端
我們通常會使用 V8 自帶的堆快照來判斷某些變量的內存是否沒有得到正確的回收,這是一種非常有效的手段,因為我們在堆快照中可以實時看到當前所有 JS 對象的存活情況。

在自帶垃圾回收的語言中,開發者往往不需要過多地關注內存管理。但是不代表我們可以完全忽略它。因為語言引擎的垃圾回收是有一定的判斷規則的,如果我們的變量所引用的內存沒有符合這個規則,那么引擎無無法對這些內存進行自動回收。所以如何追蹤變量的內存是否被回收也變得非常重要,尤其在 Node.js 中。

因為 Node.js 通常以服務器的角色長期提供服務,一旦服務發生內存泄露,就意味著我們的服務遲早會掛掉,盡管服務可以被自動重啟,但是這并不能從根本上解決問題。所以如何檢測內存泄露,就變得非常重要。

我們通常會使用 V8 自帶的堆快照來判斷某些變量的內存是否沒有得到正確的回收,這是一種非常有效的手段,因為我們在堆快照中可以實時看到當前所有 JS 對象的存活情況。但是快照是一種非常重的操作,因為它不僅會阻塞線程的執行,而且會導致內存的暴漲,前者導致我們的服務出現短暫的不可用,具體時間取決于進程的堆大小,堆內存過大時,采集堆快照所引起的內存暴漲可能會導致進程直接掛掉。下面介紹一種輕量級的內存泄露檢測方式,雖然它不像堆快照那么強大,但是在某些場景下是有用的。

當我們想知道一個對象有沒有被回收時,有幾種方式,第一種就是通過引擎提供的快照能力,直接查看對象的存活情況,第二種則是注冊對象被 GC 時的回調,下面是介紹的第二種能力。引擎沒有直接提供當對象被 GC 時回調的能力,但是我們可以通過引擎提供的弱引用技術來實現這個功能(可參考 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);
}

接著分析下代碼的實現,主要是利用了 WeakMap 和 async_hooks 實現了這個功能。當我們需要追蹤一個對象是否被 GC 時,我們只需要傳入這個對象和一個回調,然后調用 trackGC。trackGC 首先會針對一個被追蹤的對象生成一個關聯的 AsyncResource 對象。并且記錄 AsyncResource id 和 回調的對應關系,然后把再通過 WeakMap 把被追蹤的對象和 AsyncResource 對象關聯起來。那么當被追蹤的對象失去所有引用時,它關聯的 AsyncResource 對象就會被回收,從而 async_hooks 的 destroy 鉤子被回調,這時候執行開發者注冊的回調通知開發者該對象已經被 GC。接下來看看 如何使用。

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`);

在上面的例子中,首先打印出初始化的進程內存,接著分配一塊大的內存,注冊對象的 GC 回調,把變量賦值為 null 使得它的關聯的對象失去唯一的強引用,從而被 GC,最后進行顯式 GC 并輸出這時候的內存。下面是我電腦上的輸出。

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

可以看到注冊的 GC 回調被執行了,并且內存的確被回收了。

最后分析一下這個實現。這里主要是利用了 async_hooks 模塊的能力,因為 WeakMap 是沒有提供回調機制的。來看一下 AsyncResource 的實現,只列出核心代碼。

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

當創建一個 AsyncResource 對象時,會調用 registerDestroyHook。

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


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

RegisterDestroyHook 首先創建了一個 DestroyParam 對象保存一些上下文,然后利用 V8 的弱引入對象可以注冊回調的機制設置需要追蹤的對象的 GC 回調。那么當對象失去所有強引用被 GC 時,回調就會被執行。

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


std::unique_ptr<DestroyParam> p{info.GetParameter()};
Local<Object> prop_bag = PersistentToLocal::Default(info.GetIsolate(),
p->propBag);
Local<Value> 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);
}
}

最終通過 EmitDestroy 回調 JS 層執行 destroy 鉤子。這樣就實現了追蹤 JS 對象是否被 GC 的能力。具體可以參考 https://github.com/theanarkh/gc-tracker。

責任編輯:武曉燕 來源: 編程雜技
相關推薦

2021-01-28 07:52:39

JS數組變量

2011-05-25 10:46:39

Javascript

2022-06-24 09:36:47

Python對象調用

2023-10-04 20:03:46

GOkeyvalue

2011-06-08 10:11:25

JavaScript

2009-10-22 17:39:34

CLR內存管理

2011-08-09 16:58:33

windows7Windows7是否被

2024-05-13 08:05:26

JVMJava逃逸分析

2019-10-25 22:06:38

服務器開發工具

2017-12-06 19:00:53

2009-08-27 10:06:49

2024-04-15 11:24:32

庫存跟蹤技術NFC藍牙

2010-09-13 11:26:26

2020-06-04 17:14:03

資產跟蹤物聯網RFID

2009-11-10 11:36:46

2013-10-29 16:24:10

FirefoxLightbeam

2022-02-15 22:32:19

GC虛擬機對象

2023-12-05 07:59:08

JS小技巧數組對象去重

2023-06-20 06:44:14

Node.jsCPU 負載

2018-01-10 10:45:37

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精品久久久久久久不卡四虎 | 国产一在线 | 91色啪 | 国产999精品久久久久久 | 一区网站 | 国产欧美一区二区三区日本久久久 | 久久极品| 中文字幕视频在线 | 天堂一区二区三区 | 国产色网 | 亚洲午夜小视频 | 亚洲成人免费观看 | 国产日韩一区二区 | 欧美v在线观看 | 黄网站在线播放 | 欧美一区二区三区久久精品 | 日韩在线欧美 | 欧美视频在线看 | 久国久产久精永久网页 | 欧美激情久久久 | 国产成人精品免高潮在线观看 | 97日韩精品| 精品一区二区三区在线视频 | 久久久久国产精品 | 91看片在线 | 国产精品a一区二区三区网址 | 国产精品一区久久久 | 欧美大片在线观看 | 波多野结衣电影一区 | 亚洲h视频 | 天天天天操 | 亚洲国产aⅴ精品 | 欧美一级欧美一级在线播放 | 国产乱人伦 | 日韩高清在线观看 | av网站在线免费观看 | 亚洲一区亚洲二区 | 亚洲一区二区成人 | av手机免费在线观看 | 日韩综合色 | 亚洲精品视频一区 |