自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Node.js ObjectWrap 的弱引用問題

開發(fā) 前端
最近在寫 Node.js Addon 的過程中,遇到了一個問題,然后發(fā)現(xiàn)是 ObjectWrap 弱引用導(dǎo)致的,本文介紹一下具體的問題和排查過程,以及 ObjectWrap 的使用問題。

前言:最近在寫 Node.js Addon 的過程中,遇到了一個問題,然后發(fā)現(xiàn)是 ObjectWrap 弱引用導(dǎo)致的,本文介紹一下具體的問題和排查過程,以及 ObjectWrap 的使用問題。

ObjectWrap 用于寫 Addon 的時候?qū)С?C++ 對象給 JS 層使用,大致用法如下。首先定義一個 C++ 類。

  1. class Demo: public node::ObjectWrap { 
  2.      public
  3.          static void create(const FunctionCallbackInfo<Value>& args) { 
  4.                     new Demo(args.This()); 
  5.          } 
  6.          Demo(Local<Object> object): node::ObjectWrap() {} 
  7.      private: 
  8.         uv_timer_t timer; 
  9.  
  10. }; 

然后導(dǎo)出這個類到 JS。

  1. void Initialize( 
  2.   Local<Object> exports, 
  3.   Local<Value> module, 
  4.   Local<Context> context 
  5. ) { 
  6.   Isolate *isolate = context->GetIsolate(); 
  7.   Local<FunctionTemplate> demo = FunctionTemplate::New(isolate, Demo::create); 
  8.   char * str = "Demo"
  9.   Local<String> name = String::NewFromUtf8(isolate, str, NewStringType::kNormal, strlen(str)).ToLocalChecked(); 
  10.   demo->InstanceTemplate()->SetInternalFieldCount(1); 
  11.   exports->Set(context, name, demo->GetFunction(context).ToLocalChecked()).Check(); 
  12.  
  13.  
  14. NODE_MODULE_CONTEXT_AWARE(NODE_GYP_MODULE_NAME, Initialize) 

然后在 JS 通過以下方式調(diào)用。

 

  1. const { Demo } = require('demo.node'); 
  2.  
  3. const demo = new Demo(); 

可以看到 C++ Demo 類中有一個 uv_timer_t 成員。主要用來定時去抓取 V8 堆快照,所以把它注冊到 Libuv 中。

 

  1. uv_timer_init(loop, &timer); 
  2.  
  3. uv_timer_start(&timer, timer_cb, 1000, 1000); 

然后使用的過程中我們發(fā)現(xiàn),定時器隨機觸發(fā)了幾次后,就不觸發(fā)了。經(jīng)過多種測試無果后,我不得不編譯一個 debug 版本的 Node.js 進行單步調(diào)試,然后就發(fā)現(xiàn)了有意思的事情。第一次進入 poll io 階段時,一切正常,1 秒后超時。

 

 

 

 

但是后面再次進入 poll io 階段時,詭異的事情發(fā)生了。

 

 

 

 

超時時間變成了一個很大的數(shù)字,正常來說,我設(shè)置的每隔一秒超時一次,這里應(yīng)該是 1才對,為什么會出現(xiàn)一個詭異的數(shù)字呢。思考了一下,猜想是這塊內(nèi)存被釋放了,然后里面保存了一些臟數(shù)據(jù),接著我給 Demo 類加了個析構(gòu)函數(shù)。

 

  1. ~Demo() { 
  2.   LOG("dead"); 
  3.  

然后發(fā)現(xiàn),這個類對象居然被析構(gòu)了。通過棧追蹤發(fā)現(xiàn)邏輯來自于 ObjectWrap 的 WeakCallback。

 

 

 

 

WeakCallback 的代碼如下。

  1. static void WeakCallback(const v8::WeakCallbackInfo<ObjectWrap>& data) { 
  2.    ObjectWrap* wrap = data.GetParameter(); 
  3.    wrap->handle_.Reset(); 
  4.    delete wrap; 
  5.  

delete wrap 就是 delete 了 Demo 對象。而這個 WeakCallback 的源頭來自 ObjectWrap 的 MakeWeak。

  1. inline void MakeWeak() { 
  2.     persistent().SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); 

這個 MakeWeak 又來源于 Wrap。

  1. inline void Wrap(v8::Local<v8::Object> handle) { 
  2.     // 關(guān)聯(lián) C++ 對象和 Demo 對象 
  3.     handle->SetAlignedPointerInInternalField(0, this); 
  4.     persistent().Reset(v8::Isolate::GetCurrent(), handle); 
  5.     MakeWeak(); 
  6.  

Wrap 是創(chuàng)建 Demo 對象時調(diào)用的函數(shù)。用于關(guān)聯(lián) JS 層對象和 C++ 對象,關(guān)系如下。

 

 

 

 

所以 JS 創(chuàng)建一個 Demo 對象的時候,就會指向一個 C++ 對象,然后 Demo 對象也有個持久句柄指向這個 C++ 對象。但是它默認情況下調(diào)用了 MakeWeak,也就是弱引用。而 JS 層在創(chuàng)建完 Demo 對象后就離開了作用域,因為 JS 模塊是被函數(shù)包裹起來的,執(zhí)行完變量就被 gc了,除非通過 module.exports 或全局變量保持對 C++ 對象的引用。所以就導(dǎo)致了 C++ 對象最終被 Demo 對象以弱引用的方式引用著,等待 gc 的時候被回收。這里又引出了另一個問題,當我把抓取快照的代碼改成一些簡單的代碼時,并不容易觸發(fā)這個問題,原因在于它沒有觸發(fā) gc。后來我嘗試在 JS 層分配一些內(nèi)存,最終也成功觸發(fā)了這個問題,因為下面的代碼會導(dǎo)致 gc。而 gc 的時候就把 C++ 對象回收了。

 

  1. setInterval(() => { 
  2.     Buffer.from('x'.repeat('10')) 
  3.  
  4. },3000) 

這個問題的解決方式就是調(diào)用 ObjectWrap 的 Ref 函數(shù)消除弱引用(或者在 JS 層保持對這個對象的引用)。

 

  1. virtual void Ref() { 
  2.     persistent().ClearWeak(); 
  3.     refs_++; 

回過頭來看看 Node.js 中另一個類似功能的類 BaseObject。

  1. BaseObject::BaseObject(Environment* env, v8::Local<v8::Object> object) 
  2.     : persistent_handle_(env->isolate(), object), env_(env) { 
  3.   object->SetAlignedPointerInInternalField(BaseObject::kSlot, static_cast<void*>(this)); 
  4.  

它并沒有設(shè)置弱引用的邏輯。所以在 Node.js 的 C++ 模塊里,我們也看不到主動調(diào)用 Ref 的代碼。這或許是使用 ObjectWrap 時需要注意的問題。

 

 

 

總結(jié):大致分析了 ObjectWrap 相關(guān)的這個問題,但是其實排查過程比描述的繁瑣和困難,主要是一開始沒有用 debug 版本的 Node.js 進行調(diào)試,把排查聚焦在打快照的地方了,因為那里涉及了多線程操作同一個 isolate,所以以為是 V8 API 使用方式的問題??偟膩碚f,如果碰到 Node.js 詭異的一些問題,不妨打個 debug 版本的 Node.js 進行調(diào)試,可能會更快地找到問題,從中也能學(xué)到很多東西。

 

責(zé)任編輯:姜華 來源: 編程雜技
相關(guān)推薦

2017-04-10 13:28:32

Node.jsJavaScript

2023-06-30 23:25:46

HTTP模塊內(nèi)存

2022-06-23 06:34:56

Node.js子線程

2013-11-01 09:34:56

Node.js技術(shù)

2015-03-10 10:59:18

Node.js開發(fā)指南基礎(chǔ)介紹

2025-01-08 08:47:44

Node.js內(nèi)存泄露定時器

2020-05-29 15:33:28

Node.js框架JavaScript

2021-12-25 22:29:57

Node.js 微任務(wù)處理事件循環(huán)

2012-02-03 09:25:39

Node.js

2011-09-09 14:23:13

Node.js

2011-11-01 10:30:36

Node.js

2011-09-08 13:46:14

node.js

2011-09-02 14:47:48

Node

2012-10-24 14:56:30

IBMdw

2011-11-10 08:55:00

Node.js

2011-11-02 09:04:15

Node.js

2021-09-26 05:06:04

Node.js模塊機制

2021-11-06 18:40:27

js底層模塊

2019-07-09 14:50:15

Node.js前端工具

2015-06-23 15:27:53

HproseNode.js
點贊
收藏

51CTO技術(shù)棧公眾號