【问题标题】:Node.js memory usage remains high even after destroying sockets即使在销毁套接字之后,Node.js 的内存使用率仍然很高
【发布时间】:2015-11-20 14:54:37
【问题描述】:

我创建了一个简单的 nodejs 服务器和客户端。它们通过 15000 个 tcp 套接字相互交互。

客户端代码:

'use strict';

const net = require('net');

for (let i = 0; i < 15000; ++i) {
    let socket = new net.Socket();
    socket.connect(6000, '127.0.0.1', () => {
        console.log('Connected');
        socket.write('data to server');
    });

    socket.on('data', data => {
        console.log(data);
    });

    socket.on('close', () => {
        console.log('Connection closed');
    });
}

服务器代码:

'use strict';

const net = require('net');

let sockets = [];

let server = net.createServer(socket => {
    socket.write('blabla from server');
    socket.on('data', data => {
        console.log(data);
    });
    sockets.push(socket);
    if (sockets.length >= 15000) {
        setTimeout(() => {
            console.log('cleanup start');
            for (let socket of sockets) {
                socket.end();
                socket.destroy();
                socket.unref();
            }
            console.log('cleaned up and ready');
        }, 80000);
    }
});

if (global.gc) {
    setInterval(() => {
        global.gc();
    }, 15000);
}

setInterval(() => {
    console.log(process.memoryUsage());
}, 5000);

server.listen(6000, '127.0.0.1');

他们发送和接收消息。在创建套接字期间,内存使用率变高。但在销毁套接字后,我预计内存使用率会变低,这不会发生。

【问题讨论】:

    标签: node.js sockets


    【解决方案1】:

    你有一个充满空值的数组。

    sockets.push(socket);
    
    if (sockets.length >= 15000) {   // The length is never decreasing
        setTimeout(() => {
            for (let socket of sockets) {
                [...]
                socket = null;    // Because you just null the value but the array has still the same length.
            }
    
        }, 80000);
    }
    

    看看这个例子:

    var arr=[];
    
    setInterval(function(){
        arr.push(null);
    }, 1);
    
    setInterval(() => {
        console.log(process.memoryUsage());
        console.log(arr.length);  // You will see the array is growing continously
    }, 5000);
    

    因此解决方案是选择不同的迭代数字和切片,它们键释放数组索引,而不是用另一个属性覆盖它。

    但是,无论这些连接是否正在使用,您为什么要删除所有连接。您可以在客户端断开连接时更好地销毁套接字。

    sockets.slice(index,1);
    

    【讨论】:

    • 我不会在区间内将值推送到数组,请参阅我的代码。无论如何,即使 15000 个空值的数组驻留在服务器上,因为在 setTimeout 完成后引用将丢失并且 GC 将删除它们,它也不应该占用 20 MB 的 RAM。在这种情况下它会做什么。
    • @AraYeressian 间隔只是为了演示空值数组需要内存。当您删除引用并计算数组长度时,即使在 global.gc() 调用之后它也会减少吗?由于 v8 的全面行为,使用 global.gc 是不好的做法。 jayconrod.com/posts/55/a-tour-of-v8-garbage-collectionstackoverflow.com/a/30654451/1165289
    • 我不打算在生产环境中使用 global.gc。我在这里使用它来进行测试。正如我所说,对整个数组的引用在 setTimeout 之后会丢失,因此是否将其元素设置为 null 并不重要。无论如何,您可以自己检查一下,只需删除 socket = null;从代码中查看结果。
    【解决方案2】:

    好的,看来我找到了解决方案。 Nodejs 缓存 Socket 对象以供将来使用。这是更新后的代码。

    服务器代码:

    'use strict';
    
    const net = require('net');
    
    let sockets = [];
    
    let numberOfSockets = 2000;
    let server = net.createServer(socket => {
        sockets.push(socket);
        if (sockets.length >= numberOfSockets) {
            console.log(process.memoryUsage());
            console.log('cleanup start');
    
            for (let socket of sockets) {
                socket.end();
                socket.destroy();
                socket.unref();
            }
            sockets = [];
            global.gc();
            setTimeout(() => {
                console.log(process.memoryUsage());
            }, 1000);
    
            console.log('cleaned up and ready');
        }
    });
    
    if (global.gc) {
        setInterval(() => {
            global.gc();
        }, 15000);
    }
    
    server.listen(6000, '127.0.0.1');
    

    客户端代码:

    'use strict';
    
    const net = require('net');
    
    let numberOfClosedSockets = 0;
    let numberOfSockets = 2000;
    
    function test() {
        for (let i = 0; i < numberOfSockets; ++i) {
            let socket = new net.Socket();
            socket.connect(6000, '127.0.0.1', () => {
                console.log('Connected');
            });
    
            socket.on('data', data => {
                console.log(data);
            });
    
            socket.on('close', () => {
                console.log('Connection closed ' + numberOfClosedSockets);
                numberOfClosedSockets++;
                if (numberOfClosedSockets >= numberOfSockets) {
                    numberOfClosedSockets = 0;
                    setTimeout(() => {
                        test();
                    }, 2000);
    
                }
                socket.destroy();
                socket.unref();
            });
        }
    }
    test();
    

    在本例中,客户端在第一组套接字被销毁后创建新的套接字组。它一遍又一遍地做。如果您查看内存使用情况,它不会增加,因此没有内存泄漏。

    github问题link

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-06
      • 1970-01-01
      • 2014-05-07
      • 2021-07-09
      • 1970-01-01
      • 2016-06-22
      • 1970-01-01
      相关资源
      最近更新 更多