这一小节是第五章的又一个重头戏,朴灵作者深入讲解了Node.js中处理二进制数据的核心——Buffer。Buffer是Node区别于浏览器JS的关键特性之一,让Node能高效处理文件、网络流、图片、加密等二进制场景。
5.4.1 Buffer的内存分配
Buffer的内存不在V8堆中,而在Node的C++层直接分配(外部内存,external)。
关键机制:Slab动态分配(书中有经典图)。
Slab有三种状态:
- Full(已满):完全分配给某个Buffer。
- Partial(部分):部分分配给多个小Buffer,剩余空闲。
- Empty(空闲):全新8KB块。
分配过程:
- 小Buffer(<8KB):从Partial Slab借,或新建Empty Slab转为Partial。
- 大Buffer(>=8KB):直接分配Full Slab(大对象,直接用slowBuffer)。
- 释放时:小Buffer归还Slab,大Buffer直接释放。
优势:
- 频繁小Buffer创建/销毁极快(复用Slab)。
- 避免V8 GC频繁触发(Buffer不在堆里)。
示例:
let buf1 = Buffer.alloc(1024); // 小,从Partial Slab
let buf2 = Buffer.alloc(10 * 1024 * 1024); // 大,直接Full
5.4.2 Buffer的使用注意
作者列出常见坑和最佳实践:
-
创建方式
- Buffer.alloc(size[, fill]):安全,初始化为0或fill(推荐)。
- Buffer.allocUnsafe(size):不初始化,快但可能含旧数据(需手动fill(0))。
- Buffer.from(array/string):从数据创建。
-
拼接问题
- 频繁buf.concat会导致大量临时Buffer,内存峰值高。
- 解决:预估大小,用alloc,或用Stream流式处理。
-
编码转换
- string.toBuffer() / buf.toString('utf8') 注意编码。
- 中文等多字节字符小心截断。
-
性能注意
- 避免频繁创建小Buffer(用pool复用)。
- 大文件用Stream + Buffer分块读写。