目錄
一、為什麼需要GC
二、GC發展
3.1.1 新生代
3.1.2 老生代
3.2 大物件空間 large object space
四、V8 新老分区大小
4.1 老生代分区大小
4.2 新生代分区大小
五、 内存分析相关API
5.1 v8.getHeapStatistics()
5.2 process.memoryUsage
5.3 开启打印GC事件
5.4 内存快照
六、利用内存快照分析内存泄漏
洩漏解決和最佳化
六、 最後
首頁 web前端 js教程 圖文詳解Node V8引擎的記憶體和GC

圖文詳解Node V8引擎的記憶體和GC

Mar 29, 2023 pm 06:02 PM
node.js 後端 v8

這篇文章帶大家深入了解NodeJS V8引擎的記憶體和垃圾回收器(GC),希望對大家有幫助!

圖文詳解Node V8引擎的記憶體和GC

一、為什麼需要GC

#程式運行需要使用內存,其中記憶體的兩個分區是我們常常會討論的概念:棧區和堆區。

堆疊區是線性的佇列,隨著函數運行結束自動釋放的,而堆區是自由的動態記憶體空間、堆疊記憶體是手動分配釋放或垃圾回收程式(Garbage Collection,後文都簡稱GC)自動分配釋放的。

軟體發展早期或某些語言對於堆記憶體都是手動操作分配和釋放,例如 CC 。雖然能精準操作內存,達到盡可能的最優內存使用,但是開發效率卻非常低,也容易出現內存操作不當。 【相關教學推薦:nodejs影片教學程式設計教學

#隨著科技發展,高階語言(例如Java Node )都不需要開發者手動操作內存,程式語言自動會分配和釋放空間。同時也誕生了 GC(Garbage Collection)垃圾回收器,幫助釋放和整理記憶體。開發者大部分情況不需要關心記憶體本身,可以專注業務開發。後文主要是討論堆記憶體和 GC

二、GC發展

GC運行會消耗CPU資源,GC運行的過程會觸發STW(stop-the-world)暫停業務代碼線程,為什麼會STW 呢?是為了確保在 GC 的過程中,不會和新建立的物件起衝突。

GC主要是伴隨記憶體大小增加而發展演化。大致分為3個大的代表性階段:

  • 階段一單執行緒GC(代表:serial)

單執行緒GC,在它進行垃圾收集時,必須完全暫停其他所有的工作執行緒 ,它是最初階段的GC,效能也是最糟糕的

    ##階段二並行多執行緒GC(代表:Parallel Scavenge, ParNew)
在多CPU 環境中利用多條GC 執行緒同時並行運行,從而垃圾回收的時間減少、用戶執行緒停頓的時間也減少,這個演算法也會STW,

完全暫停其他所有的工作線程

    階段三多線程並發concurrent GC(代表:CMS (Concurrent Mark Sweep) G1)
這裡的並發是指:GC多執行緒執行可以和業務程式碼並發運行。

在前面的兩個發展階段的 GC 演算法都會完全 STW,而在 concurrent GC 中,有部分階段 GC 執行緒可以和業務程式碼並發運行,保證了更短的 STW 時間。但這個模式就會存在標記錯誤,因為GC 過程中可能有新物件進來,當然演算法本身會修正並解決這個問題

上面的三個階段並不代表GC 一定是上面描述三種的其中一種。不同程式語言的 GC 根據不同需求採用多種演算法組合實作。

三、v8 記憶體分區與GC

堆記憶體設計與GC設計是緊密相關的。 V8 把堆記憶體分為幾大區域,採用分代策略。

盜圖:

圖文詳解Node V8引擎的記憶體和GC

  • 新生代(new-space 或young-generation):空間小,分成了兩個半空間(semi-space),其中的資料存活期短。
  • 老生代(old-space 或old-generation):空間大,可增量,其中的資料存活期長
  • 大物件空間( large-object-space):預設超過256K的物件會在此空間下,下文解釋
  • 程式碼空間(code-space):即時編譯器(JIT)在這裡儲存已編譯的程式碼
  • 元空間(cell space):這個空間用來儲存小的、固定大小的JavaScript對象,像是數字和布林值。
  • 屬性元空間(property cell space):這個空間用來儲存特殊的JavaScript對象,例如存取器屬性和某些內部物件。
  • Map Space:這個空間用來儲存用於JavaScript物件的元資訊和其他內部資料結構,例如Map和Set物件。

3.1 分代策略:新生代與老生代

圖文詳解Node V8引擎的記憶體和GC

#在Node.js 中,GC 採用分代策略,分為新、老生代區,記憶體資料大都在這兩個區域。

3.1.1 新生代

新生代是一個小的、儲存年齡小的物件、快速的記憶體池,分成了兩個半空間(semi-space),一半的空間是空閒的(稱為to空間),另一半的空間是儲存了資料(稱為from空間)。

當物件首次建立時,它們被分配到新生代 from 半空間中,它的年齡為1。當from 空間不足或超過一定大小數量之後,會觸發Minor GC(採用複製演算法Scavenge),此時,GC 會暫停應用程式的執行(STW,stop-the-world),標記(from空間)中所有活動對象,然後將它們整理連續移動到新生代的另一個空閒空間(to空間)。最後原本的from 空間的記憶體會被全部釋放而變成空閒空間,兩個空間就完成fromto 的對換,複製演算法是犧牲了空間換取時間的演算法。

新生代的空間更小,所以此空間會更頻繁的觸發 GC。同時掃描的空間更小,GC效能消耗也更小、它的 GC 執行時間也更短。

每當一次 Minor GC 完成存活的物件年齡就 1,經歷過多次Minor GC還存活的物件(年齡大於N),它們將被移到老生代記憶體池中。

3.1.2 老生代

老生代是一個大的記憶體池,用來儲存較長壽命的物件。老生代記憶體採用 標記清除(Mark-Sweep)標記壓縮演算法(Mark-Compact)。它的一次執行叫做 Mayor GC。當老生代中的物件佔滿一定比例時,即存活物件與總物件的比例超過一定的閾值,就會觸發一次 標記清除標記壓縮

因為它的空間更大,它的GC執行時間也更長,頻率相對新生代更低。如果老生代完成 GC 回收之後空間還是不足,V8 就會從系統中申請更多記憶體。

可以手動執行 global.gc() 方法,設定不同參數,主動觸發GC。 但是要注意的是,預設情況下,Node.js 是禁用了此方法。如果要啟用,可以透過啟動Node.js 應用程式時新增--expose-gc 參數來開啟,例如:

node --expose-gc app.js
登入後複製

V8 在老生代中主要採用了Mark -SweepMark-Compact 相結合的方式進行垃圾回收。

Mark-Sweep 是標記清除的意思,它分成兩個階段,標記和清除。 Mark-Sweep 在標記階段遍歷堆中的所有對象,並標記活著的對象,在隨後的清除階段中,只清除未被標記的對象。

Mark-Sweep 最大的問題是在進行一次標記清除回收後,記憶體空間會出現不連續的狀態。這種記憶體碎片會對後續的記憶體分配造成問題,因為很可能出現需要分配一個大物件的情況,這時所有的碎片空間都無法完成此次分配,就會提前觸發垃圾回收,而這次回收是不必要的。

為了解決 Mark-Sweep 的記憶體碎片問題,Mark-Compact 被提出來。 Mark-Compact 是標記整理的意思,是在 Mark-Sweep 的基礎上演進而來的。它們的差異在於物件在標記為死亡後,在整理過程中,將活著的物件往一端移動,移動完成後,直接清除邊界外的記憶體。 V8 也會根據某個邏輯,釋放一定空閒的記憶體還給系統。

3.2 大物件空間 large object space

大物件會直接在大物件空間創建,並且不會移動到其它空間。那麼到底多大的物件會直接在大物件空間創建,而不是在新生代 from 區中創建呢?查閱資料和原始碼終於找到了答案。預設是 256KV8 似乎並沒有暴露修改指令,原始碼中的 v8_enable_hugepage 設定應該是打包的時候設定的。

chromium.googlesource.com/v8/v8.git/ …

 // There is a separate large object space for objects larger than
 // Page::kMaxRegularHeapObjectSize, so that they do not have to move during
 // collection. The large object space is paged. Pages in large object space
 // may be larger than the page size.
登入後複製

source.chromium.org/ chromium/ch…

圖文詳解Node V8引擎的記憶體和GC

#

圖文詳解Node V8引擎的記憶體和GC

(1 << (18 - 1)) 的结果 256K
(1 << (19 - 1)) 的结果 256K
(1 << (21 - 1)) 的结果 1M(如果开启了hugPage)
登入後複製

四、V8 新老分区大小

4.1 老生代分区大小

在v12.x 之前:

为了保证 GC 的执行时间保持在一定范围内,V8 限制了最大内存空间,设置了一个默认老生代内存最大值,64位系统中为大约1.4G,32位为大约700M,超出会导致应用崩溃。

如果想加大内存,可以使用 --max-old-space-size 设置最大内存(单位:MB)

node --max_old_space_size=
登入後複製

在v12以后:

V8 将根据可用内存分配老生代大小,也可以说是堆内存大小,所以并没有限制堆内存大小。以前的限制逻辑,其实不合理,限制了 V8 的能力,总不能因为 GC 过程消耗的时间更长,就不让我继续运行程序吧,后续的版本也对 GC 做了更多优化,内存越来越大也是发展需要。

如果想要做限制,依然可以使用 --max-old-space-size 配置, v12 以后它的默认值是0,代表不限制。

参考文档:nodejs.medium.com/introducing…

4.2 新生代分区大小

新生代中的一个 semi-space 大小 64位系统的默认值是16M,32位系统是8M,因为有2个 semi-space,所以总大小是32M、16M。

--max-semi-space-size

--max-semi-space-size 设置新生代 semi-space 最大值,单位为MB。

此空间不是越大越好,空间越大扫描的时间就越长。这个分区大部分情况下是不需要做修改的,除非针对具体的业务场景做优化,谨慎使用。

--max-new-space-size

--max-new-space-size 设置新生代空间最大值,单位为KB(不存在)

有很多文章说到此功能,我翻了下 nodejs.org 网页中 v4 v6 v7 v8 v10的文档都没有看到有这个配置,使用 node --v8-options 也没有查到,也许以前的某些老版本有,而现在都应该使用 --max-semi-space-size

五、 内存分析相关API

5.1 v8.getHeapStatistics()

执行 v8.getHeapStatistics(),查看 v8 堆内存信息,查询最大堆内存 heap_size_limit,当然这里包含了新、老生代、大对象空间等。我的电脑硬件内存是 8G,Node版本16x,查看到 heap_size_limit 是4G。

{
  total_heap_size: 6799360,
  total_heap_size_executable: 524288,
  total_physical_size: 5523584,
  total_available_size: 4340165392,
  used_heap_size: 4877928,
  heap_size_limit: 4345298944,
  malloced_memory: 254120,
  peak_malloced_memory: 585824,
  does_zap_garbage: 0,
  number_of_native_contexts: 2,
  number_of_detached_contexts: 0
}
登入後複製

k8s 容器中查询 NodeJs 应用,分别查看了v12 v14 v16版本,如下表。看起来是本身系统当前的最大内存的一半。128M 的时候,为啥是 256M,因为容器中还有交换内存,容器内存实际最大内存限制是内存限制值 x2,有同等的交换内存。

所以结论是大部分情况下 heap_size_limit 的默认值是系统内存的一半。但是如果超过这个值且系统空间足够,V8 还是会申请更多空间。当然这个结论也不是一个最准确的结论。而且随着内存使用的增多,如果系统内存还足够,这里的最大内存还会增长。

容器最大内存heap_size_limit
4G2G
2G1G
1G0.5G
1.5G0.7G
256M256M
128M256M

5.2 process.memoryUsage

process.memoryUsage()
{
  rss: 35438592,
  heapTotal: 6799360,
  heapUsed: 4892976,
  external: 939130,
  arrayBuffers: 11170
}
登入後複製

通过它可以查看当前进程的内存占用和使用情况 heapTotalheapUsed,可以定时获取此接口,然后绘画出折线图帮助分析内存占用情况。以下是 Easy-Monitor 提供的功能:

圖文詳解Node V8引擎的記憶體和GC

建议本地开发环境使用,开启后,尝试大量请求,会看到内存曲线增长,到请求结束之后,GC触发后会看到内存曲线下降,然后再尝试多次发送大量请求,这样往复下来,如果发现内存一直在增长低谷值越来越高,就可能是发生了内存泄漏。

5.3 开启打印GC事件

使用方法

node --trace_gc app.js
// 或者
v8.setFlagsFromString(&#39;--trace_gc&#39;);
登入後複製
  • --trace_gc
[40807:0x148008000]   235490 ms: Scavenge 247.5 (259.5) -> 244.7 (260.0) MB, 0.8 / 0.0 ms  (average mu = 0.971, current mu = 0.908) task 
[40807:0x148008000]   235521 ms: Scavenge 248.2 (260.0) -> 245.2 (268.0) MB, 1.2 / 0.0 ms  (average mu = 0.971, current mu = 0.908) allocation failure 
[40807:0x148008000]   235616 ms: Scavenge 251.5 (268.0) -> 245.9 (268.8) MB, 1.9 / 0.0 ms  (average mu = 0.971, current mu = 0.908) task 
[40807:0x148008000]   235681 ms: Mark-sweep 249.7 (268.8) -> 232.4 (268.0) MB, 7.1 / 0.0 ms  (+ 46.7 ms in 170 steps since start of marking, biggest step 4.2 ms, walltime since start of marking 159 ms) (average mu = 1.000, current mu = 1.000) finalize incremental marking via task GC in old space requested
登入後複製
GCType <heapUsed before> (<heapTotal before>) -> <heapUsed after> (<heapTotal after>) MB
登入後複製

上面的 ScavengeMark-sweep 代表GC类型,Scavenge 是新生代中的清除事件,Mark-sweep 是老生代中的标记清除事件。箭头符号前是事件发生前的实际使用内存大小,箭头符号后是事件结束后的实际使用内存大小,括号内是内存空间总值。可以看到新生代中事件发生的频率很高,而后触发的老生代事件会释放总内存空间。

  • --trace_gc_verbose

展示堆空间的详细情况

v8.setFlagsFromString(&#39;--trace_gc_verbose&#39;);

[44729:0x130008000] Fast promotion mode: false survival rate: 19%
[44729:0x130008000]    97120 ms: [HeapController] factor 1.1 based on mu=0.970, speed_ratio=1000 (gc=433889, mutator=434)
[44729:0x130008000]    97120 ms: [HeapController] Limit: old size: 296701 KB, new limit: 342482 KB (1.1)
[44729:0x130008000]    97120 ms: [GlobalMemoryController] Limit: old size: 296701 KB, new limit: 342482 KB (1.1)
[44729:0x130008000]    97120 ms: Scavenge 302.3 (329.9) -> 290.2 (330.4) MB, 8.4 / 0.0 ms  (average mu = 0.998, current mu = 0.999) task 
[44729:0x130008000] Memory allocator,       used: 338288 KB, available: 3905168 KB
[44729:0x130008000] Read-only space,        used:    166 KB, available:      0 KB, committed:    176 KB
[44729:0x130008000] New space,              used:    444 KB, available:  15666 KB, committed:  32768 KB
[44729:0x130008000] New large object space, used:      0 KB, available:  16110 KB, committed:      0 KB
[44729:0x130008000] Old space,              used: 253556 KB, available:   1129 KB, committed: 259232 KB
[44729:0x130008000] Code space,             used:  10376 KB, available:    119 KB, committed:  12944 KB
[44729:0x130008000] Map space,              used:   2780 KB, available:      0 KB, committed:   2832 KB
[44729:0x130008000] Large object space,     used:  29987 KB, available:      0 KB, committed:  30336 KB
[44729:0x130008000] Code large object space,     used:      0 KB, available:      0 KB, committed:      0 KB
[44729:0x130008000] All spaces,             used: 297312 KB, available: 3938193 KB, committed: 338288 KB
[44729:0x130008000] Unmapper buffering 0 chunks of committed:      0 KB
[44729:0x130008000] External memory reported:  20440 KB
[44729:0x130008000] Backing store memory:  22084 KB
[44729:0x130008000] External memory global 0 KB
[44729:0x130008000] Total time spent in GC  : 199.1 ms
登入後複製
  • --trace_gc_nvp

每次GC事件的详细信息,GC类型,各种时间消耗,内存变化等

v8.setFlagsFromString(&#39;--trace_gc_nvp&#39;);

[45469:0x150008000]  8918123 ms: pause=0.4 mutator=83.3 gc=s reduce_memory=0 time_to_safepoint=0.00 heap.prologue=0.00 heap.epilogue=0.00 heap.epilogue.reduce_new_space=0.00 heap.external.prologue=0.00 heap.external.epilogue=0.00 heap.external_weak_global_handles=0.00 fast_promote=0.00 complete.sweep_array_buffers=0.00 scavenge=0.38 scavenge.free_remembered_set=0.00 scavenge.roots=0.00 scavenge.weak=0.00 scavenge.weak_global_handles.identify=0.00 scavenge.weak_global_handles.process=0.00 scavenge.parallel=0.08 scavenge.update_refs=0.00 scavenge.sweep_array_buffers=0.00 background.scavenge.parallel=0.00 background.unmapper=0.04 unmapper=0.00 incremental.steps_count=0 incremental.steps_took=0.0 scavenge_throughput=1752382 total_size_before=261011920 total_size_after=260180920 holes_size_before=838480 holes_size_after=838480 allocated=831000 promoted=0 semi_space_copied=4136 nodes_died_in_new=0 nodes_copied_in_new=0 nodes_promoted=0 promotion_ratio=0.0% average_survival_ratio=0.5% promotion_rate=0.0% semi_space_copy_rate=0.5% new_space_allocation_throughput=887.4 unmapper_chunks=124
[45469:0x150008000]  8918234 ms: pause=0.6 mutator=110.9 gc=s reduce_memory=0 time_to_safepoint=0.00 heap.prologue=0.00 heap.epilogue=0.00 heap.epilogue.reduce_new_space=0.04 heap.external.prologue=0.00 heap.external.epilogue=0.00 heap.external_weak_global_handles=0.00 fast_promote=0.00 complete.sweep_array_buffers=0.00 scavenge=0.50 scavenge.free_remembered_set=0.00 scavenge.roots=0.08 scavenge.weak=0.00 scavenge.weak_global_handles.identify=0.00 scavenge.weak_global_handles.process=0.00 scavenge.parallel=0.08 scavenge.update_refs=0.00 scavenge.sweep_array_buffers=0.00 background.scavenge.parallel=0.00 background.unmapper=0.04 unmapper=0.00 incremental.steps_count=0 incremental.steps_took=0.0 scavenge_throughput=1766409 total_size_before=261207856 total_size_after=260209776 holes_size_before=838480 holes_size_after=838480 allocated=1026936 promoted=0 semi_space_copied=3008 nodes_died_in_new=0 nodes_copied_in_new=0 nodes_promoted=0 promotion_ratio=0.0% average_survival_ratio=0.5% promotion_rate=0.0% semi_space_copy_rate=0.3% new_space_allocation_throughput=888.1 unmapper_chunks=124
登入後複製

5.4 内存快照

const { writeHeapSnapshot } = require(&#39;node:v8&#39;);
v8.writeHeapSnapshot()
登入後複製

打印快照,将会STW,服务停止响应,内存占用越大,时间越长。此方法本身就比较费时间,所以生成的过程预期不要太高,耐心等待。

注意:生成内存快照的过程,会STW(程序将暂停)几乎无任何响应,如果容器使用了健康检测,这时无法响应的话,容器可能被重启,导致无法获取快照,如果需要生成快照、建议先关闭健康检测。

兼容性问题:此 API arm64 架构不支持,执行就会卡住进程 生成空快照文件 再无响应, 如果使用库 heapdump,会直接报错:

(mach-o file, but is an incompatible architecture (have (arm64), need (x86_64))

API 会生成一个 .heapsnapshot 后缀快照文件,可以使用 Chrome 调试器的“内存”功能,导入快照文件,查看堆内存具体的对象数和大小,以及到GC根结点的距离等。也可以对比两个不同时间快照文件的区别,可以看到它们之间的数据量变化。

六、利用内存快照分析内存泄漏

一个 Node 应用因为内存超过容器限制经常发生重启,通过容器监控后台看到应用内存的曲线是一直上升的,那应该是发生了内存泄漏。

使用 Chrome 调试器对比了不同时间的快照。发现对象增量最多的是闭包函数,继而展开查看整个列表,发现数据量较多的是 mongo 文档对象,其实就是闭包函数内的数据没有被释放,再通过查看 Object 列表,发现同样很多对象,最外层的详情显示的是 MongooseConnection 对象。

圖文詳解Node V8引擎的記憶體和GC

圖文詳解Node V8引擎的記憶體和GC

到此为止,已经大概定位到一个类的 mongo 数据存储逻辑附近有内存泄漏。

再看到 Timeout 对象也比较多,从 GC 根节点距离来看,这些对象距离非常深。点开详情,看到这一层层的嵌套就定位到了代码中准确的位置。因为那个类中有个定时任务使用 setInterval 定时器去分批处理一些不紧急任务,当一个 setInterval 把事情做完之后就会被 clearInterval 清除。

圖文詳解Node V8引擎的記憶體和GC圖文詳解Node V8引擎的記憶體和GC

洩漏解決和最佳化

透過程式碼邏輯分析,最終找到了問題所在,是clearInterval 的觸發條件有問題,導致計時器沒有被清除一直循環下去。定時器一直執行,這段程式碼和其中的資料還在閉包之中,無法被 GC 回收,所以記憶體會越來越大直到達到上限崩潰。

這裡使用setInterval 的方式並不合理,順便改成了利用for await 隊列順序執行,從而達到避免同時間大量並發的效果,代碼也要清晰許多。由於這塊程式碼比較久遠,就不考慮為啥當初使用 setInterval 了。

發布新版本之後,觀察了十多天,內存平均保持在100M出頭,GC 正常回收臨時增長的內存,呈現為波浪曲線,沒有再出現洩漏。

圖文詳解Node V8引擎的記憶體和GC

至此利用記憶體快照,分析並解決了記憶體洩漏。當然實際分析的時候要曲折一點,這個記憶體快照的內容並不好理解、沒那麼直接。快照資料的展示是類型聚合的,需要透過看不同的建構函數,以及內部的資料詳情,結合自己的程式碼綜合分析,才能找到一些線索。 例如從當時我得到的記憶體快照看,有大量資料是閉包、string、mongo model類別、Timeout、Object等,其實這些增量的資料都是來自於那段有問題的程式碼,並且無法被GC 回收。

六、 最後

不同的語言GC 實作都不一樣,例如JavaGo

Java:了解JVM (對應Node V8)的知道,Java 也採用分代策略,它的新生代中還存在一個eden 區,新生的物件都在這個區域創建。而 V8 新生代沒有 eden 區。

Go:採用標記清除,三色標記演算法

不同的語言的 GC 實作不同,但本質上都是採用不同演算法組合實作。在效能上,不同的組合,帶來的各方面效能效率都不一樣,但都是此消彼長,只是偏向不同的應用場景而已。

更多node相關知識,請造訪:nodejs 教學

以上是圖文詳解Node V8引擎的記憶體和GC的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

一文聊聊Node中的記憶體控制 一文聊聊Node中的記憶體控制 Apr 26, 2023 pm 05:37 PM

基於無阻塞、事件驅動建立的Node服務,具有記憶體消耗低的優點,非常適合處理海量的網路請求。在海量請求的前提下,就需要考慮「記憶體控制」的相關問題了。 1. V8的垃圾回收機制與記憶體限制 Js由垃圾回收機

圖文詳解Node V8引擎的記憶體和GC 圖文詳解Node V8引擎的記憶體和GC Mar 29, 2023 pm 06:02 PM

這篇文章帶大家深入了解NodeJS V8引擎的記憶體和垃圾回收器(GC),希望對大家有幫助!

深入聊聊Node中的File模組 深入聊聊Node中的File模組 Apr 24, 2023 pm 05:49 PM

文件模組是對底層文件操作的封裝,例如文件讀寫/打開關閉/刪除添加等等文件模組最大的特點就是所有的方法都提供的**同步**和**異步**兩個版本,具有sync 字尾的方法都是同步方法,沒有的都是異

分享介面設計文件的12個注意點 分享介面設計文件的12個注意點 Apr 24, 2023 am 10:58 AM

最近在做介面文件評審的時候,發現一個小夥伴定義的出參是個枚舉值,但是介面文件沒有給出對應具體的枚舉值。其實,如何寫好介面文檔,真的很重要。今天田螺哥,帶給你介面設計文件的12個注意點~

深入了解golang中的泛型(Generic) 深入了解golang中的泛型(Generic) Apr 11, 2023 pm 07:20 PM

這篇文章帶給大家的內容是介紹深入理解golang中的泛型?泛型怎麼使用?有一定的參考價值,有需要的朋友可以參考一下,希望對你們有幫助。

一起聊聊Node中的事件循環 一起聊聊Node中的事件循環 Apr 11, 2023 pm 07:08 PM

事件循環是 Node.js 的基本組成部分,透過確保主執行緒不被阻塞來實現非同步編程,了解事件循環對建立高效應用程式至關重要。以下這篇文章就來帶大家深入了解Node中的事件循環 ,希望對大家有幫助!

深入了解Node中的Buffer 深入了解Node中的Buffer Apr 25, 2023 pm 07:49 PM

一開始的時候 JS 只在瀏覽器端運行,對於 Unicode 編碼的字串容易處理,但對於二進位和非 Unicode 編碼的字串處理困難。並且二進制是電腦最底層的資料格式,視訊/音訊/程式/網路包

了解一下Golang中的unsafe包 了解一下Golang中的unsafe包 Apr 02, 2023 am 08:30 AM

在一些底層的庫中, 經常會看到使用 unsafe 套件的地方。這篇文章就來帶大家了解Golang中的unsafe包,介紹一下unsafe 包的功能和Pointer的使用方式,希望對大家有幫助!

See all articles