前言

JavaScript 对于字符串的操作十分便捷,无论是单字节字符还是宽字节字符,都会认为是一个字符。对字符串的简单操作和DOM操作基本上已经可以满足前端工程需求,但Node很多时候需要处理文件和网络I/O,就需要处理大量的二进制数据。所以,在Node中就定义了一个Buffer类,该类用来创建一个专门存放二进制数据的缓存区。本文是《深入浅出nodejs》相关内容的一个总结,是一篇读书笔记,仅供以后查阅。

在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。

1.Buffer结构

Buffer是一个类似与Array的对象,但它主要是用于字节操作。也拥有一些类似于Array具有的方法,可以使用这些方法进行字节处理。

1.1模块结构

Buffer是一个JavaScript与C++结合的模块,它将性能相关部分使用C++实现,将非性能相关的部分用JavaScript实现。

Buffer所占用的内存不是通过V8分配的,属于堆外内存。由于V8垃圾回收性能的影响,将常用的操作对象用更高效和专用的内存分配回收策略来管理是个不错的思路。

Buffer是Node的一个核心模块,在进程启动时已经加载,并放置在全局对象中,使用Buffer时,不需require()引入,可直接使用。

1.2Buffer对象

Buffer对象类似于数组,它的元素为16进制的两位数(如:1F,0~255)。示例如下:

不同编码的字符串占用的元素个数各不相同,ASCII编码的字符占用一个元素,汉字占用两个元素。

当新建一个Buffer,而没有写入数据时,访问其中的元素,会得到一个0~255的随机值。

我们对Buffer中的一个元素赋值的话,它会默认的转为一个0~255的值,它的转换规则如下:

  • 1.给元素赋值小于0,就将该值逐次加256,直到得到一个0~255的整数;
  • 2.给元素赋值大于255,就逐次减256,直到得到一个0~255的整数;
  • 3.给元素赋值是小数,舍弃小数部分,只保留整数部分。

1.3Buffer的内存分配

处理大量的字节数据不能采用需要一点内存就向系统申请一点内存的方式,这会造成很大的内存申请的系统调用,对操作系统有一定压力。为此Node在内存的使用上应用的是在C++层面申请,在JavaScript中分配的策略。

为了高效的使用申请来的内存,Node采用了slab分配机制。slab就是一个申请好的固定大小的内存区域,有以下三种状态:

  • full:完全分配状态;
  • partial:部分分配状态;
  • empty:没有分配状态。

在node中,需要一个Buffer对象,可以通过以下方式指定对象的大小:

new Buffer(size)

Node以8KB为分界来区分Buffer是大对象还是小对象。在JavaScript层面,以它作为单位单元进行内存的分配。

分配小Buffer对象

如果指定Buffer的大小少于8KB,Node会按照小对象的方式进行分配。Buffer的分配过程中主要使用一个局部变量pool作为中间处理对象,处于分配状态的slab单元都指向它。以下是分配一个全新的slab单元的操作,它会将新申请的SlowBuffer对象指向它:

var pool;

function allocPool() {
pool = new SlowBuffer(Buffer.poolSize);
pool.used = 0;
}

slab处于empty状态。

构造小Buffer对象的代码如下:

new Buffer(1024);

这次构造将会去检查pool对象,如果pool没有被创建,将会创建一个新的slab单元指向它:

if(!pool || pool.length - pool.used < this.length) {
allocPool();
}

同时当前Buffer对象的parent属性指向该slab,并记录下是从这个slab的哪个位置(offset)开始使用的,slab对象自身也记录被使用了多少字节,代码如下:

this,parent = pool;
this.offset = pool.used;
pool.used += this.length;
if(pool.used & 7) {
pool.used = (pool.used + 8) & ~7;
}

这时候的slab状态为partial。当再次创建一个Buffer对象时,构造过程中将会判断这个slab的剩余空间是否足够。如果足够就使用剩余空间,并更新slab的分配状态。如果剩余空间不足,将会重新构造slab,原slab中剩余的空间会造成浪费。

这就带来了一个问题,一个slab可能分配给多个Buffer对象使用,只有这些Buffer对象在作用域释放并都可以回收时,slab的8KB空间才会被回收。

分配大Buffer对象

如果需要超过8KB的Buffer对象,将会直接分配一个SlowBuffer对象作为slab单元,这个单元将会被这个大Buffer对象所独占。

上面提到的Buffer对象都是JavaScript层面上的,能够被V8的垃圾回收标记回收。但其内部的parent属性指向的SlowBuffer对象却来自于Node自身C++中的定义,是C++层面上的Buffer对象,所以内存不在V8的堆中。

2.Buffer的转换

Buffer对象可以直接与字符串之间相互转换,目前支持的字符串编码类型有以下几种:

  • ASCII
  • UTF-8
  • UTF-16LE/UCS-2
  • Base64
  • Binary
  • Hex

字符串转Buffer:

new Buffer(str, [encoding]);

当encoding不传入时,默认为utf-8。一个Buffer对象可以存储不同编码类型的字符串转码的值,调用write方法可以实现它:

buf.write(string, [offset], [length], [encoding]);

需要特别注意的是,不同编码所用的字节长度不同,将Buffer反转回来字符串时需要谨慎处理。

Buffer转字符串:

buf.toString([encoding], [start], [end]);

当encoding不传入时,默认为utf-8。可以通过设置encoding,start,end这3个参数实现整体或局部的转换。

3.Buffer的拼接

Buffer在使用时,通常是一段一段的方式传输。以下是常见的从输入流中读取内容的示例代码:

var fs = require('fs');

var rs = fs.createReadStream('test.md');
var data = '';
rs.on('data', function (chunk){
data += chunk;
});
rs.on('end', function (){
console.log(data);
});

Buffer学习笔记.的更多相关文章

  1. Protocol Buffer学习笔记

    Protocol Buffer Protobuf基础概念 Protobuf是google开发的数据结构描述语言,能够将结构化数据序列化与反序列化,取代json和xml,常用于服务器通信协议.RPC系统 ...

  2. MySQL学习笔记-cache 与 buffer

    Cache和Buffer是两个不同的概念,简单的说,Cache是加速"读",而 buffer是缓冲"写",前者解决读的问题,保存从磁盘上读出的数据,后者是解决写 ...

  3. Java NIO 学习笔记(一)----概述,Channel/Buffer

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  4. Java IO学习笔记一:为什么带Buffer的比不带Buffer的快

    作者:Grey 原文地址:Java IO学习笔记一:为什么带Buffer的比不带Buffer的快 Java中为什么BufferedReader,BufferedWriter要比FileReader 和 ...

  5. DirectX Graphics Infrastructure(DXGI):最佳范例 学习笔记

    今天要学习的这篇文章写的算是比较早的了,大概在DX11时代就写好了,当时龙书11版看得很潦草,并没有注意这篇文章,现在看12,觉得是跳不过去的一篇文章,地址如下: https://msdn.micro ...

  6. JSP学习笔记

    JSP学习笔记 Jsp网页主要分为Elements与Template Data两部分. Template Data:JSP Container不处理的部分,例如HTML内容 Elements:必须经由 ...

  7. linux驱动开发之块设备学习笔记

    我的博客主要用来存放我的学习笔记,如有侵权,请与我练习,我会立刻删除.学习参考:http://www.cnblogs.com/yuanfang/archive/2010/12/24/1916231.h ...

  8. Linux 学习笔记

    Linux学习笔记 请切换web视图查看,表格比较大,方法:视图>>web板式视图 博客园不能粘贴图片吗 http://wenku.baidu.com/view/bda1c3067fd53 ...

  9. SQLite学习笔记(七)&&事务处理

    说到事务一定会提到ACID,所谓事务的原子性,一致性,隔离性和持久性.对于一个数据库而言,通常通过并发控制和故障恢复手段来保证事务在正常和异常情况下的ACID特性.sqlite也不例外,虽然简单,依然 ...

随机推荐

  1. CISCO知识扫盲

    cisco知识扫盲 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.vlan简介 1.什么是VLAN 简称虚拟局域网.至于英语怎么写自行百度吧. VLAN的优势: 1>.广 ...

  2. 函数和常用模块【day05】:迭代器(六)

    本节内容 1.简书 2.可迭代对象 3.迭代器 4.rang方法 5.总结 一.简述 我们经常使用for循环去遍历一些序列数据,但是我们有的时间发现for循环的效率很低,而且很占用了大量的硬件资源,但 ...

  3. Swift学习笔记7--访问控制

    在Swift语言中,访问修饰符有三种,分别为private,internal和public.同时,Swift对于访问权限的控制,不是基于类的,而是基于文件的.其区别如下: 1,private priv ...

  4. Hbase记录-Hbase Web管理工具

    1.Hmaster的Web接口-端口参数:hbase.master.info.port  默认为16010 http://hbase_master_server:16010 可查看hbase的版本信息 ...

  5. HTML中       等6种空白空格的区别

    HTML提供了5种空格实体(space entity),它们拥有不同的宽度,非断行空格( )是常规空格的宽度,可运行于所有主流浏览器.其他几种空格(       ‌‍)在不同浏览器中宽度各异.   它 ...

  6. VS中空项目、win32项目、控制台程序的区别(转)

    空项目,大多数想单纯创建c++工程的新同学,打开vs后很可能不知道选择创建什么工程,这时候请相信我,空项目是你最好的选择.因为空工程不包含任何的源代码文件,接下来你只需要在相应的源代码文件夹和头文件文 ...

  7. python 数据类型详解

    python数据类型详解 参考网址:http://www.cnblogs.com/linjiqin/p/3608541.html 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8 ...

  8. input框的输入限制

    1.输入数字 <input onKeyUp="value=value.replace(/[^\d|chun]/g,'')"> 2.只输入中文 <input typ ...

  9. 阿里云apache服务器外网无法访问(配置安全组,添加80服务)

    CentOS的系统 ,已经安装好了 apache php mysql 常规排错过程(ps:没耐心的童鞋请直接看最后一步,学习在阿里云控制台配置 安全组,允许 http服务) 第一步:检查apache ...

  10. Docker安装Zookeeper

    ⒈下载 docker pull zookeeper ⒉运行 docker run --name zk -p 2181:2181 -p 2888:2888 -p 3888:3888 --restart ...