v8/Node 在函数调用期间是否真的进行了垃圾收集? - 或者这是一个 sailsJS 内存泄漏
Does v8/Node actually garbage collect during function calls? - Or is this a sailsJS memory leak
我正在创建一个带有后台任务的 sailsJS 网络服务器,需要连续 运行(如果服务器空闲)。 - 这是一项将数据库与一些外部数据和 pre-cache 数据同步以加快请求速度的任务。
我使用的是 sails 1.0 版。适配器是postgresql (adapter: 'sails-postgresql'
),适配器版本:1.0.0-12
现在,在 运行 运行此应用程序时,我注意到一个主要问题:一段时间后,应用程序似乎莫名其妙地崩溃并出现堆内存不足错误。 (我什至无法捕捉到这一点,节点进程刚刚退出)。
当我试图寻找内存泄漏时,我尝试了很多不同的方法,最终我可以将我的代码缩减为以下函数:
async DoRun(runCount=0, maxCount=undefined) {
while (maxCount === undefined || runCount < maxCount) {
this.count += 1;
runCount += 1;
console.log(`total run count: ${this.count}`);
let taskList;
try {
this.active = true;
taskList = await Task.find({}).populate('relatedTasks').populate('notBefore');
//taskList = await this.makeload();
} catch (err) {
console.error(err);
this.active = false;
return;
}
}
}
为了做到这一点 "testable" 我减小了应用程序允许使用的堆大小:--max-old-space-size=100
;使用这个堆大小,它总是在 2000 运行 秒左右崩溃。然而,即使使用 "unlimited" 堆,它也会在几(万)千 运行 秒后崩溃。
现在为了进一步测试,我注释掉了 Task.find()
命令并添加了一个创建 "same" 结果的虚拟对象。
async makeload() {
const promise = new Promise(resolve => {
setTimeout(resolve, 10, this);
});
await promise;
const ret = [];
for (let i = 0; i < 10000; i++) {
ret.push({
relatedTasks: [],
notBefore: [],
id: 1,
orderId: 1,
queueStatus: 'new',
jobType: 'test',
result: 'success',
argData: 'test',
detail: 'blah',
lastActive: new Date(),
updatedAt: Date.now(),
priority: 2 });
}
return ret;
}
这 运行s(到目前为止)即使在 20000 次调用之后也很好,分配了 90 MB 的堆。
在第一种情况下我做错了什么?这让我相信帆有内存泄漏?或者节点无法以某种方式释放数据库连接?
我这里好像看不出有什么明目张胆的"leaking"?正如我在日志中看到的那样,this.count
不是字符串,因此它甚至没有泄漏(运行Count 也是如此)。
从这一点我该如何进步?
编辑
还有一些 clarifications/summary:
- 我 运行 在节点 8.9.0
- Sails 版本 1.0
- 使用 sails-postgresql 适配器 (1.0.0-12)(测试版,因为其他版本不适用于 sails 1.0)
我运行 带有标志:--max-old-space-size=100
环境变量:node_env=production
它在生产环境中大约 2000-2500 运行 秒后崩溃(在调试模式下为 500)。
我创建了一个 github 存储库,其中包含一个可行的代码示例;
here。再次查看代码 "soon" 设置标志 --max-old-space-size=80
(或类似的东西)
我对sailsJS一窍不通,但是我可以回答标题前半部分的问题:
Does V8/Node actually garbage collect during function calls?
是的,绝对是。细节很复杂(大多数垃圾 collection 工作是在小的增量块中完成的,并且尽可能多地在后台完成)并且随着垃圾收集器的改进而不断变化。基本原则之一是分配触发 GC 工作块。
垃圾收集器不关心函数调用或事件循环。
我正在创建一个带有后台任务的 sailsJS 网络服务器,需要连续 运行(如果服务器空闲)。 - 这是一项将数据库与一些外部数据和 pre-cache 数据同步以加快请求速度的任务。
我使用的是 sails 1.0 版。适配器是postgresql (adapter: 'sails-postgresql'
),适配器版本:1.0.0-12
现在,在 运行 运行此应用程序时,我注意到一个主要问题:一段时间后,应用程序似乎莫名其妙地崩溃并出现堆内存不足错误。 (我什至无法捕捉到这一点,节点进程刚刚退出)。
当我试图寻找内存泄漏时,我尝试了很多不同的方法,最终我可以将我的代码缩减为以下函数:
async DoRun(runCount=0, maxCount=undefined) {
while (maxCount === undefined || runCount < maxCount) {
this.count += 1;
runCount += 1;
console.log(`total run count: ${this.count}`);
let taskList;
try {
this.active = true;
taskList = await Task.find({}).populate('relatedTasks').populate('notBefore');
//taskList = await this.makeload();
} catch (err) {
console.error(err);
this.active = false;
return;
}
}
}
为了做到这一点 "testable" 我减小了应用程序允许使用的堆大小:--max-old-space-size=100
;使用这个堆大小,它总是在 2000 运行 秒左右崩溃。然而,即使使用 "unlimited" 堆,它也会在几(万)千 运行 秒后崩溃。
现在为了进一步测试,我注释掉了 Task.find()
命令并添加了一个创建 "same" 结果的虚拟对象。
async makeload() {
const promise = new Promise(resolve => {
setTimeout(resolve, 10, this);
});
await promise;
const ret = [];
for (let i = 0; i < 10000; i++) {
ret.push({
relatedTasks: [],
notBefore: [],
id: 1,
orderId: 1,
queueStatus: 'new',
jobType: 'test',
result: 'success',
argData: 'test',
detail: 'blah',
lastActive: new Date(),
updatedAt: Date.now(),
priority: 2 });
}
return ret;
}
这 运行s(到目前为止)即使在 20000 次调用之后也很好,分配了 90 MB 的堆。 在第一种情况下我做错了什么?这让我相信帆有内存泄漏?或者节点无法以某种方式释放数据库连接?
我这里好像看不出有什么明目张胆的"leaking"?正如我在日志中看到的那样,this.count
不是字符串,因此它甚至没有泄漏(运行Count 也是如此)。
从这一点我该如何进步?
编辑 还有一些 clarifications/summary:
- 我 运行 在节点 8.9.0
- Sails 版本 1.0
- 使用 sails-postgresql 适配器 (1.0.0-12)(测试版,因为其他版本不适用于 sails 1.0)
我运行 带有标志:--max-old-space-size=100
环境变量:node_env=production
它在生产环境中大约 2000-2500 运行 秒后崩溃(在调试模式下为 500)。
我创建了一个 github 存储库,其中包含一个可行的代码示例;
here。再次查看代码 "soon" 设置标志 --max-old-space-size=80
(或类似的东西)
我对sailsJS一窍不通,但是我可以回答标题前半部分的问题:
Does V8/Node actually garbage collect during function calls?
是的,绝对是。细节很复杂(大多数垃圾 collection 工作是在小的增量块中完成的,并且尽可能多地在后台完成)并且随着垃圾收集器的改进而不断变化。基本原则之一是分配触发 GC 工作块。
垃圾收集器不关心函数调用或事件循环。