Nodejs源码解析之Buffer

今天源码分析一下Nodejs的核心模块Buffer, 官方文档https://nodejs.org/api/buffer.html
在分析源码前,以下问题需要说明:

  • Buffer可以看作是原始数据的数组集合,需要特别说明的是,其支持不同形式的编码,而且是在V8 heap之外,可以被GC回收的堆内存。
  • 理解大字节序与小字节序的区别,buffer的方法很多区分了大字节序和小序。 注意Buffer的内存是用malloc分配的堆内存,还是从低到高的排列的

分析nodejs 的API和源码后,准备从下面这个方面进行分析:

  • Buffer的缓存优化以及内存的分配
  • Buffer的构造
  • Buffer的类方法
  • Buffer的对象属性方法
  • Buffer的读与写方法

Buffer的缓存优化和内存分配

源码如下:

// 绑定一个C/C++的模块,用于添加buffer的方法,详情可以查看node_buffer.h node_buffer.cc
var buffer = process.binding('buffer');
// 绑定一个C/C++的模块 用于申请内存,详情可以查看smalloc.h,smalloc.cc
var smalloc = process.binding('smalloc');
// 帮助类
var util = require('util');
// 申请内存的方法,可以简单的理解为C/C++的new malloc
var alloc = smalloc.alloc;
var truncate = smalloc.truncate;
var sliceOnto = smalloc.sliceOnto;
// 可以分配的最长的buffer,值为 0x3fffffff, 也就是1G的大小
var kMaxLength = smalloc.kMaxLength;
// 这个下面用于将C/C++ buffer中的方法,用于Javascript中J。。详情请参考node_buffer.cc
var internal = {};
// 导出模块的类 buffer
exports.Buffer = Buffer;
// 导出模块的类 SlowBuffer
exports.SlowBuffer = SlowBuffer;
exports.INSPECT_MAX_BYTES = 50;
// Buffer缓存的大小为8k
Buffer.poolSize = 8 * 1024;
var poolSize, poolOffset, allocPool;

// 创建缓存库8k的大小缓存
function createPool() {
   
  // 当前的缓存的大小
  poolSize = Buffer.poolSize;
  // 缓存的内存地址
  allocPool = alloc({}, poolSize);
  // 可以使用的缓存内存偏移
  poolOffset = 0;
}
// 可以理解为系统启动的时候就创建了一个8k大小的buffer缓存
createPool();

从上面的源码可以看出。

  • Buffer利用一个8k大小的空间做缓存,在创建一个Buffer的时候可能不会新申请内存,这样可以提高效率。
  • Buffer的最大内存为1G
  • 系统启动时,已经分配了8k大小的buffer缓存

Buffer的构造

源码如下:

// Buffer的创建最多有两个参数,subject(不同种类型,主要说明buffer的大小与初始值)
// 和encoding(buffer的编码格式) 
function Buffer(subject, encoding) {
   
  // 检查,如果不是buffer类型,直接递归调用。生成新Buffer对象
  if (!util.isBuffer(this))
    return new Buffer(subject, encoding);
   // 如果subject参数为数字,表示的Buffer长度
  if (util.isNumber(subject)) {
    this.length = +subject;
  }// 如果 subject参数为字符串的情况
  else if (util.isString(subject)) {
    //处理第二个参数,说明Buffer默认为 'utf8'编码格式
    if (!util.isString(encoding) || encoding.length === 0)
      encoding = 'utf8';
      // 获取当前Buffer的长度
    this.length = Buffer.byteLength(subject, encoding);

  // Handle Arrays, Buffers, Uint8Arrays or JSON.
  } else if (util.isObject(subject)) {
    // 处理是队列或者Buffer JSON格式的情况
    if (subject.type === 'Buffer' && util.isArray(subject.data))
      subject = subject.data;
     //获取的是队列或者buffer的长度 
    this.length = +subject.length;

  } else {
     // 如果不是数字,字符串,队列,Buffer等情况,直接抛出异常。 
     //这说明Buffer的构造只接受subject参数为几种情况
    throw new TypeError('must start with number, buffer, array or string');
  }
   // 检查,如果申请的大小大于1G,直接抛出异常
  if (this.length > kMaxLength) {
    throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
                         'size: 0x' + kMaxLength.toString(16) + ' bytes');
  }
  // 如果为负数,直接清0
  if (this.length < 0)
    this.length = 0;
  else
    this.length >>>= 0;  // Coerce to uint32.

  this.parent = undefined;
  // 这里需要特别注意,如果Buffer申请的大小为4k(Buffer.poolSize >>> 1)
  if (this.length <= (Buffer.poolSize >>> 1) && this.length > 0) {
    // 如果需要的内存已经超过缓存了,直接重新申请
    if (this.length > poolSize - poolOffset)
      createPool();
    // 
    this.parent = sliceOnto(allocPool,
                            this,
                            poolOffset,
                            poolOffset + this.length);
    // 将空闲的内存给新的Buffer
    poolOffset += this.length;

    // Ensure aligned slices
    // 处理字节对齐
    if (poolOffset & 0x7) {
      poolOffset |= 0x7;
      poolOffset++;
    }
  } else {
    // 也就是说,大于4k的情况下,会直接重新分配内存
    alloc(this, this.length);
  }
  // 下面处理的是已经申请的Buffer内存初始化问题

  // step1: 如果是数字,无需初始化,直接返回就好。
  if (util.isNumber(subject)) {
    return;
  }

  // step1: 如果是字符串,需要用字符串来初始化Buffer空间。
  if (util.isString(subject)) {
    // In the case of base64 it's possible that the size of the buffer
    // allocated was slightly too large. In this case we need to rewrite
    // the length to the actual length written.
    // 直接将string按照编码格式写入到Buffer内存中,进行初始化。
    // 这里需要注意的是base64编码的情况下,可能内存不一样
    var len = this.write(subject, encoding);
    // Buffer was truncated after decode, realloc internal ExternalArray
    
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值