在node中通过javascript只能使用部分内存,64位系统下约为1.4GB,32位系统下约为0.7GB。
这与V8的垃圾回收机制有关。
在node启动时可以传递参数来调整内在限制的大小(注意:只能在启动时指定),如下:
--max-old-space-size //老生代内存空间的最大值
--max-new-space-size //新生代内存空间的最大值

新生代回收算法:Scavenge;
老生代回收算法:Mark-Sweep & Mark-Compact

解决”全停顿“(stop-the-world)问题方案: 增量标记法(incremental marking),垃圾回收最大停顿时间可以减少到原本的1/6左右。后续还引入了延迟清理(lazy sweeping)与增量式整理(incremental compaction)。计划引入并行标记与并行清理,利用多核性能。

查看垃圾回收日志的方式,在启动时添加参数 --trace_gc 。
启动时使用 --prof 参数,可以得到V8执行时的性能分析数据。

V8提供了linux-tick-processor工具,用于统计日志信息。windows下为 windows-tick-processor.bat 。

不通过var声明或定义在global变量上的为全局变量。
可以通过delete操作或重新赋值来使旧的对象脱离引用关系,global.foo=nudefined; // or null
建议用重新赋值的方法来解除引用,因为在V8中通过delete删除对象的属性有可能干扰V8的优化。

查看内存使用情况:
process.memoryUsage(); // node进程的内存占用情况
os模块中有:totalmem() 和 freemem() //操作系统内存使用情况
rss (resident set size),即进程的常驻内存部分。进程的内存总共有几部分,一部分是rss,其余部分在交换区(swap)或者文件系统(filesystem)中。

堆外内存
Buffer对象不通过V8分配,没有堆内存大小限制,利用堆外内存可以突破内存限制问题。

内存泄漏的可能原因:
1、缓存----在Node中,任何试图拿内存当缓存的行为都应当被限制。
缓存限制策略:Isaac Z. Schlueter--LRU算法,https://github.com/isaacs/node-lru-cache (memoize)https://github.com/medikoo/memoize 。另:由于模块缓存机制,模块是常驻老生代的,其在编译执行后形成的作用域不会被释放。不要让模块调用导致局部变量无限制增长。
缓存的解决方案:采用进程外的缓存,优点:一是减少常驻内存的对象,让垃圾回收更高效;二是进程之间可以共享缓存。
Redis: https://github.com/mranney/node_redis
Memcached: https://github.com/3rd-Eden/node-memcached
2、队列消费不及时----解决方法:监控队列长度;异步调用应包含超时机制。Bagpipe(https://github.com/JacksonTian/bagpipe)实现方案:超时模式+拒绝模式。
3、作用域未释放

内存泄漏排查工具:node-heapdump、node-memwatch
node-heapdump:安装,npm install heapdump;在测试代码第一行添加,var heapdump=require('heapdump'); ;命令,kill -USR2 ,抓拍堆内存快照,存为 heapdump-..heapsnapshot 。
node-memwatch: 使用方式不同,详见 P132 。

大内存应用:使用stream模块。
var reader = fs.createReadStream('in.txt');
var writer = fs.createWriteStream('out.txt');
reader.pipe(writer);
可读流提供了管理方法pipe(),封装了data事件和写入操作,这种流的方式不会受到V8内存限制的影响。
如果不需要进行字符串层面的操作,则不需要借助V8来处理,可以尝试进行纯粹的Buffer操作。

var oneSecond = 1000 * 1; // one second = 1000 x 1 ms setTimeout 类同

setInterval(function() {

               document.write('<p>Hello there.</p>');

}, oneSecond);

Buffer相关:
slab分配机制----一种动态内存管理机制。
Node以8KB为界限一区分Buffer是大对象还是小对象,8KB也是每个slab的大小,在javascript层面,以它作为单位单元进行内存分配。
真正的内存在Node的C++层面提供,javascript层面只是使用它。当进行小而频繁的Buffer操作时,采用slab机制进行预先申请和事后分配;对于大块的Buffer,则直接使用C++层面提供的内存(SlowBuffer)。

 Buffer是二进制数据,字符串与Buffer之间存在编码关系。

性能测试工具: ab ---- apache自带,常用参数 -n 和 -c 。
-n requests Number of requests to perform
-c concurrency Number of multiple requests to make
例如:
./ab -c 1000 -n 100 url
这个表示同时处理1000个请求并运行100次index.php文件。

curl
curl 是一个利用URL语法在命令行方式下工作的文件传输工具。它支持很多协议:FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以及 LDAP。

下载单个文件,默认将输出打印到标准输出中(STDOUT)中
curl http://www.centos.org

将文件下载到本地并命名为mygettext.html
curl -o mygettext.html http://www.gnu.org/software/gettext/manual/gettext.html
将文件保存到本地并命名为gettext.html
curl -O http://www.gnu.org/software/gettext/manual/gettext.html

通过使用-C选项可对大文件使用断点续传功能,如:
curl -C -O http://www.gnu.org/software/gettext/manual/gettext.html

通过--limit-rate选项对CURL的最大网络使用进行限制
curl --limit-rate 1000B -O http://www.gnu.org/software/gettext/manual/gettext.html

下载指定时间内修改过的文件,如:
若yy.html文件在2011/12/21之后有过更新才会进行下载
curl -z 21-Dec-11 http://www.example.com/yy.html

CURL授权
在访问需要授权的页面时,可通过-u选项提供用户名和密码进行授权
curl -u username:password URL
通常的做法是在命令行只输入用户名,之后会提示输入密码,这样可以保证在查看历史记录时不会将密码泄露
curl -u username URL

从FTP服务器下载文件
列出public_html下的所有文件夹和文件
curl -u ftpuser:ftppass -O ftp://ftp_server/public_html/
下载xss.php文件
curl -u ftpuser:ftppass -O ftp://ftp_server/public_html/xss.php

上传文件到FTP服务器
通过 -T 选项可将指定的本地文件上传到FTP服务器上,如:
将myfile.txt文件上传到服务器
curl -u ftpuser:ftppass -T myfile.txt ftp://ftp.testserver.com

从标准输入获取内容保存到服务器指定的文件中
curl -u ftpuser:ftppass -T - ftp://ftp.testserver.com/myfile_1.txt

注意:TCP针对网络中的小数据包有一定的优化策略:Nagle算法。
这种优化虽然使网络带宽被有效地使用,但是数据有可能被延迟发送。
在Node中,由于TCP默认启用了Nagle算法,可以调用socket.setNoDelay(true)去掉Nagle算法,使得write()可以立即发送数据到网络中。
尽管在网络的一端调用write()会触发另一端的data事件,但是并不意味着每次write()都会触发一次data事件,在关闭掉Nagle算法后,另一端可能会将接收到的多个小数据包合并,然后只触发一次data事件。


zhoutk
2.6k 声望1.2k 粉丝

自由程序员,技术路线c,delphi,c++,c#,java,php,node.js,python,golang,typescript;超喜欢react.js的设计思路,全栈开发成为我的终极目标。开发机器macbook pro,或装ubuntu、fedora的机器,编程用vim...