精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

N-API的JS堆對象生命周期管理

jf_wN0SrCdH ? 來源:Rust語言中文社區 ? 2023-12-29 09:41 ? 次閱讀

N-API的JS堆對象生命周期管理

N-API是Node API的簡寫,同時也是nodejs的JS VM(鏈)接入原生模塊.node文件的應用程序二進制接口(i.e. ABI)。借助N-API引入的抽象隔離,升級nodejs運行時(虛擬機)

【編譯】不要求對原生擴展模塊重新編譯— 為nodejs的不同版本分別準備不同的原生模塊build真的好麻煩。

【運行】不導致原生模塊程序崩潰— 精讀每一版changelogs清單和微調原生模塊源碼更耗時費力。

N-API開放接口在nodejs 10+后才逐步穩定,和成為nodejs c-addon的主流編程標準。 不久前,我有機會在工程實踐中獨立完成“給node-webkit容器編寫原生擴展模塊的”程序開發任務。雖然擴展模塊自身的業務處理邏輯很簡單 — 餒餒的“膠水”代碼,但其涉及到了跨越多個FFI接口調用的JS對象緩存處理。初版程序緩存不住JS堆內存中的變量值,因為JS VM的GC總是在FFI接口調用的間隙回收由原生模塊緩存的JS對象和導致程序崩潰。由此,我特意“死磕”C/C++ addons with Node-API廠方文檔,在解決工程難題的同時匯總實踐收獲寫下此文。 文章以名詞解釋統一術語理解開篇,以對比不同版本ABI標準引題,以技術細節展開討論為依據,最后向讀者圖文并茂地描述我個人創新的實踐方案。

名詞解釋

nodejs c-addon

nodejs原生擴展模塊。所謂“原生”是相對JS模塊而言的。它必須由【系統編程語言C / Cpp / Rust】編寫,并經由nodejs開放接口N-API,

接入nodejs的JS VM,并

與nodejs交換數據·互操作。

為了文字簡練,下文也將其記作為addon。 nodejs c-addon與Commonjs Module在科技樹上處于相同的生態位,和對“上游”調用端的JS業務代碼呈現一致的調用方式。

JS堆對象

它既包括由JS程序自身構造的對象實例,也包含由系統程序從addon內調用N-API接口(比如,napi_create_object())實例化的JS對象。它們都

被保存在JS VM的內存中,和

被Rust內存中的napi_value可修改原始指針引用。

N-API引用計數

它是指向JS堆對象的“FFI引用計數”智能指針(后文有圖,應該會更直觀些)。其

被保存于JS VM的內存中,和

被Rust內存中的napi_ref可修改原始指針引用。即,addon端Rust程序拿到的是指向了“智能指針”的“指針”。

被用于阻止JS VM的GC回收正活躍于addon端的JS堆對象。這就賦予了 @Rustacean 從JS VM外部干預JS對象生命周期的能力。React Native可都做不到這一點。

WASM墊片程序

它既包括由wasm-bindgen-cli生成的JS墊片程序文件,也包含由wasm-bindgen crate導出的Rust開發框架。正是js <-> Rust兩端墊片程序的協同配合,JS堆對象才幾乎被“投影為”Rust所有權(棧)變量。比如,JS堆對象的wasm_bindgen::JsValue(智能指針)結構體就比nj_sys::napi_value可修改原始指針更能發揮Rust類型系統與Borrow / Drop Checker對程序正確性的保障力。沒有“黑魔法”,滿眼都是對墊片程序開發迭代的工作量

WASM vs. N-API堆對象生命周期管理策略

簡單地講,生命周期策略的差異取決于【墊片程序】的“薄/厚”。因為WASM應用場景多(包括但不限于:網頁、nodejs,wasm-runtime獨立虛擬機),社區關注度高,wasm-bindgen工具鏈迭代速度快,所以,wasm <-> js墊片程序就“厚”。JS堆對象向Rust的“投影”就更像【智能指針】,而不是“裸奔的”原始指針。WebAssembly工作組甚至規劃將墊片程序逐步“固化”至wasm-runtime內(比如,TC39弱引用提案與引用類型提案等)以完備核心功能。工作量到位自然對接平滑!這不是黑魔法,而是真金白銀的血汗努力。 相反,nodejs c-addon的應用場景就要少得多了。所以,技術社區鮮有熱情面向N-API開放接口編寫功能豐富的addon <-> js墊片程序。于是,@Rustacean 不得不直面

“裸奔的”原始指針

簡陋的Rust Bindings— 與C頭文件概念對等的Rust語言項

“安慰劑”式的編程工具。因為缺乏了js墊片程序的協同呼應,幾個Rust宏也只是杯水車薪,能“糖”的內容很少。

轉移更多精力從【業務邏輯實現】至【FFI編程】,并與各種FFI技術細節做“斗爭”。趕快補課內存布局理論知識去吧!

具體地講,在Rust - WASM程序上下文中,披上了“智能指針”馬甲的JS堆對象幾乎完全“銹化”了。@Rustacean 可忽視JS VM垃圾收集器的干擾和:

static全局緩存JS堆對象。而不必擔心活躍于addon的JS堆對象會被JS VM的GC回收。

相對FFI函數的單次調用執行周期,延長JS堆對象的生命周期。

{ .. }塊作用域限定JS堆對象,按需釋放不再訪問的變量值,提高內存利用效率。就有局部變量的函數而言,這可明顯地降低JS堆內存占用的瞬時峰值。

相對FFI函數的單次調用執行周期,縮短JS堆對象的生命周期

另一方面,N-API沒有功能面面俱到的墊片程序。所以,@Rustacean 做不到僅憑Rust基本語法項就對FFI另一端的JS堆對象執行【全局緩存】或【塊作用域】按需回收的程序處理。甚至(重點來了),即便JS端代碼刻意保留了已FFI導出堆對象的引用,addon端(棧內存)所持有的原始指針依舊會,在FFI函數執行之后,丟失其原本指向的值和成為“野”指針。我懷疑JS VM就算沒有回收也至少挪動了被導出JS堆對象的內存位置。由此,@Rustacean 需要在addon業務代碼中額外實現部分本該由墊片程序完成的“公共服務”功能,包括但不限于:

徒手維護N-API引用計數智能指針,以“鎖住”JS堆對象不被JS VM的GC回收 —延長JS堆對象的生命周期。

調用N-API程序接口構造可層疊嵌套的作用域【塊】 —縮短JS堆對象的生命周期。

這的確是一次接觸底層“自己動手豐衣足食”的機會,但絕對不是什么令人愉快的開發體驗。千言萬語匯聚一張圖(左側WASM,右側nodejs c-addon)促成讀者思緒的豁然開朗:

051c66a8-a585-11ee-8b88-92fbcf53809c.png

N-API JS堆對象生命周期管理的技術細節

addon對JS堆對象生命周期的管理分為如下三種情況(看圖吧,一圖抵千詞):

053d16a0-a585-11ee-8b88-92fbcf53809c.png

由上圖可見,真實數據被保存于JS端(堆)內存中。Rust端(棧)內存僅持有隨時可能失效的原始指針。所以,@Rustacean 需要調用特定的N-API接口,遠程操控JS堆對象的活躍周期。但是,N-API接口并不易用。這表現為...

N-API引用計數智能指針不智能

沒有RAII Guard對活躍引用數量的自動跟蹤。@Rustacean 還需書面編寫N-API接口調用和人工增減引用個數跟蹤引用復本數量 — 這是傳統的缺陷產出“大戶”。

引用數量意味著GC回收。@Rustacean 還需顯式地析構掉N-API【引用計數】智能指針實例,才能促使被“持久化于內存”的JS堆對象接受GC回收。否則,內存泄漏!具體作法請參見如下偽碼


use ::{napi_delete_reference, napi_reference_unref}; use ::napi_call_result; let result = Box::into_raw(Box::new(u32::MAX)); // 1. 將引用計數值減一 napi_call_result!(napi_reference_unref( , , result // 引用計數減一之后的結果數值 )).unwrap(); let result = unsafe { Box::from_raw(result) }; // 2. 判斷減一后的最新引用計數值是否已經歸零。 if *result == 0 { // 當且僅當不再有任何 N-API 引用復本還指向該 JS 堆對象時, // 3. 顯式地釋放引用計數智能指針實例。 napi_call_result!(napi_delete_reference( // 這一步是必須的。要不然,內存就漏了! ,  )).unwrap(); }

只有四類JS堆對象支持N-API引用計數。它們分別是

napi_object—ECMAScript規范中的Object

napi_function—ECMAScript規范中的Function

napi_symbol—ECMAScript規范中的Symbol

napi_external— 類似于ECMAScript中的Blob,專門引用進程外的某種“黑盒opaque”資源。

若多個N-API引用計數指針實例(注:不是引用復本)都指向同一個JS堆對象,那么只有當全部N-API引用計數指針實例都被napi_delete_reference()處理后,“持久化于內存”的JS堆對象才被允許GC回收。

可逃逸作用域與作用域提升不實用

在上圖中的(普通)作用域napi_handle_scope禁止其內部的JS堆對象溢出作用域,和向外傳值。即,普通作用域是“多入無出”的。 【可逃逸作用域napi_escapable_handle_scope】有限松綁了這條限制。它允許作用域像函數一樣向外輸出一個且僅一個值,而輸出形式不是Rust塊表達式【返回值】,而是JS堆對象【作用域·提升handle promoting】。類比JS動態語言的【變量提升variable hoisting】,

相同點:塊內聲明的變量可從塊外引用和訪問

不同點:【可逃逸作用域】有且只有一個塊內聲明的變量可從塊外被訪問。否則,程序崩潰。

所以,可逃逸作用域是“多入單出”的面向實用有限放開。再看圖吧,一圖抵千詞!

055e7b7e-a585-11ee-8b88-92fbcf53809c.png

在作用域層疊嵌套的場景下,這絕對是“盛產”缺陷的泥沼。@Rustacean 需要從程序設計之初就努力避免從Rust端遠程管理JS變量的作用域。最好從產品架構上,多用addon構建【業務組件】,少封裝【功能模塊】,從根本上規避Rust <-> JS復雜互操作出現

智能化N-API引用計數 — “二段式”引用計數優化法

相比于最低也需要【過程宏】作為抽象工具才能描述清楚的JS堆對象作用域,N-API引用計數智能化改造還是有捷徑可走的。 簡單地講,將對引用復本數量變化的跟蹤任務委托給遵循RAII with Guard設計模式的智能指針std::Rc處理。然后,addon業務實現代碼僅需負責

【始】調用napi_create_reference()接口,構造一個單復本引用計數指針實例,鎖住JS堆對象不被GC回收。

【末】調用napi_reference_unref()與napi_delete_reference()接口,清空引用復本與析構唯一的引用計數指針實例,解鎖GC回收JS堆對象。

接著看圖,依舊一圖抵千詞!

0577ae78-a585-11ee-8b88-92fbcf53809c.png

于是,整個設計方案的“難點”就聚焦于:

監聽智能指針std::Rc的引用復本清空事件,并

在事件處理函數內,調用napi_reference_unref()與napi_delete_reference()接口通知VM GC回收JS堆對象。

難點不難,因為Newtypes設計模式允許 @Rustacean

對std::Rc做AOP編程。以

“攔截+重寫”std::Rc的析構函數::drop(&mut self)。于是,

在每個引用復本的析構處理后,都重新統計剩余引用復本的數量。最后,

沒有剩余引用復本了,就立即調用N-API接口napi_reference_unref()與napi_delete_reference()。

文章寫得再自恰也不如呈現一段既注釋豐富又可獨立運行的參考實現[例程]來得清晰明白。整個例程由四個部分組成:

模塊nj_sys模擬nj_sys crate的部分導出項,因為nj_sys crate并沒有入選playground.org的top 100熱門依賴包榜單。

模塊napi_rc包含了對智能指針std::Rc的AOP封裝。

函數napi_export_method()模仿nodejs c-addon的FFI導出函數。

入口函數main()模仿JS程序調用Rust-FFI函數napi_export_method()。

“二段式”引用計數優化方案的裨益

【程序性能】將FFI調用次數減少至一個常量3。

【代碼健壯性】將引用復本的數量跟蹤任務從易錯的人工完成轉為機器自動完成。addon業務代碼僅需關注引用復本的個數歸零事件。

結束語

關于nodejs c-addon技術方向,我這次僅準備了上述偏【編程】內容與大家分享。其實,交叉編譯與動態庫鏈接也是一項可以聊出些許深度的話題。比如,如何做到“從一個工程,一個分支,一套Rust程序同時編譯出三版.node鏈接庫文件,以分別適用于nodejs / nwjs / electron三款應用程序容器”的呢?。哎!無處不是“黑科技” — 從條件編譯,至編譯時修改鏈接目標。在我輸出下一篇相關主題的文章前,感興趣的讀者不防率先品鑒我的另一個github工程request-window-attention尋找答案,和給我的工程點個star! 創作不易,值得(文章)點贊,(github工程)點star,和(兩者都)轉發。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 接口
    +關注

    關注

    33

    文章

    8496

    瀏覽量

    150834
  • API
    API
    +關注

    關注

    2

    文章

    1484

    瀏覽量

    61810
  • 編程
    +關注

    關注

    88

    文章

    3591

    瀏覽量

    93592
  • 應用程序
    +關注

    關注

    37

    文章

    3240

    瀏覽量

    57600

原文標題:堆對象生命周期管理

文章出處:【微信號:Rust語言中文社區,微信公眾號:Rust語言中文社區】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    NAPI 類對象導出及其生命周期管理(下)

    NAPI 類對象導出及其生命周期管理(下)
    的頭像 發表于 05-16 10:25 ?2135次閱讀
    NAPI 類<b class='flag-5'>對象</b>導出及其<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>(下)

    鴻蒙開發接口公共事件與通知:【FFI能力】 N-API在Android、iOS平臺應用的使用指導

    N-API接口可以實現ArkTS/TS/JS與C/C++(Native)之間的交互,ArkUI-X中支持的N-API接口情況和使用場景請見[FFI能力(N-API)]。本文檔以[Ark
    的頭像 發表于 05-25 16:33 ?1835次閱讀
    鴻蒙開發接口公共事件與通知:【FFI能力】 <b class='flag-5'>N-API</b>在Android、iOS平臺應用的使用指導

    鴻蒙實戰開發-如何安全和高效的使用N-API開發Native模塊

    基礎能力的介紹,同時,方舟 ArkTS 運行時提供的 N-API 接口,封裝了方舟引擎的能力,在功能上與 Node.js 社區保持一致,這里不再贅述。 本文將結合應用開發場景,分別從對象生命周期
    發表于 05-09 15:55

    AutoScaling 生命周期掛鉤功能

    摘要: AutoScaling 伸縮組實例管理功能全面升級,新上線生命周期掛鉤(LifecycleHook)功能,方便用戶更加靈活地管理伸縮組內實例。使用生命周期掛鉤可以在伸縮組發生伸
    發表于 06-27 17:13

    理解數據生命周期管理思路

    數據生命周期管理的思考
    發表于 03-17 10:49

    HarmonyOS應用開發-PageAbility生命周期

    pageAbility的生命周期如下圖所示:在代碼中通過調用下列方法實現生命周期操作:onShow() :Ability由后臺不可見狀態切換到前臺可見狀態調用onShow方法,此時用戶在屏幕可以看到
    發表于 10-17 11:11

    觸覺智能RK3568使用體驗—NAPI 類對象導出及其生命周期管理(上)

    /n-api.html#n_api_environment_life_cycle_apis3. 關于本文提供的樣例工程本文提供了一個IDE開發的NAPI工程用來學習NAPI 類對象導出和對象生命周期
    發表于 02-08 17:10

    在S32G2 RM中有“生命周期”,生命周期的完整含義是什么?

    在S32G2 RM中,有“生命周期”。生命周期的完整含義是什么,我們應該如何使用它?
    發表于 04-23 10:37

    Synopsys 啟動硅生命周期管理計劃

    Synopsis 的數據分析驅動的硅生命周期管理計劃解決了 IC 生命周期中的質量、可靠性和安全挑戰。
    發表于 08-18 15:37 ?825次閱讀
    Synopsys 啟動硅<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>計劃

    生命周期管理:COTS視角

    全面的生命周期管理策略是保護程序和緩解與長期任務關鍵型系統中部署的 COTS 技術相關的挑戰的關鍵。除了降低風險外,生命周期管理服務還通過確保及時購買和儲存報廢 (EOL) 組件并大大
    的頭像 發表于 11-08 14:18 ?1059次閱讀
    <b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>:COTS視角

    Vue入門Vue的生命周期

    .生命周期 4.1生命周期是什么 Vue的生命周期, 就是Vue實例從創建到銷毀的過程.
    的頭像 發表于 02-06 16:16 ?842次閱讀
    Vue入門Vue的<b class='flag-5'>生命周期</b>

    觸覺智能RK3568使用體驗:NAPI 類對象導出及其生命周期管理(上)

    寫在開頭: OpenHarmony 中的 ?N-API組件定義了由ArkTS (JS/ETS)語言編寫的代碼和 native 代碼(使用 C/C++ 編寫)交互的方式,由 Node.js
    的頭像 發表于 02-17 09:10 ?853次閱讀

    恭喜!華為云通過中國信通院《API生命周期管理能力評估》

    互聯互通。為助力企業高質量管理 API生命周期的發展目標,中國信通院牽頭制定了《API生命周期
    的頭像 發表于 10-26 09:16 ?737次閱讀
    恭喜!華為云通過中國信通院《<b class='flag-5'>API</b> 全<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>能力評估》

    鴻蒙開發組件:DataAbility的生命周期

    應用開發者可以根據業務場景實現data.js/data.ets中的生命周期相關接口。DataAbility生命周期接口說明見下表。
    的頭像 發表于 06-20 09:39 ?393次閱讀

    什么是PLM產品生命周期管理系統?

    在當今競爭激烈的制造業環境中,企業不僅要關注產品的設計和生產,還需要對產品的整個生命周期進行全面管理。這包括了從產品概念構思、設計開發、生產制造、銷售分發,到最終報廢處理的每一個環節。為了高效、系統
    的頭像 發表于 11-23 16:14 ?71次閱讀
    什么是PLM產品<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>系統?