I appear to have a memory leak with my Node.js application. I built it quickly, and my JavaScript isn't too strong, so this might be easy.
I've done some heap dumps on it, and it's the String object
? leaking memory, at the rate of about 1MB every 5 minutes. I expanded String, and it's actually String.Array?
Heap stack:
#!/usr/local/bin/node var port = 8081; var io = require('socket.io').listen(port), sys = require('sys'), daemon = require('daemon'), mysql = require('mysql-libmysqlclient'); var updateq = "SELECT 1=1"; var countq = "SELECT 2=2"; io.set('log level', 2); process.on('uncaughtException', function(err) { console.log(err); }); var connections = 0; var conn = mysql.createConnectionSync(); dbconnect(); io.sockets.on('connection', function(client){ connections++; client.on('disconnect', function(){ connections--; }) }); process.on('exit', function () { console.log('Exiting'); dbdisconnect(); }); function dbdisconnect() { conn.closeSync(); } function dbconnect() { conn.connectSync('leet.hacker.org','user','password'); } function update() { if (connections == 0) return; conn.query(updateq, function (err, res) { if (err) { dbdisconnect(); dbconnect(); return; } res.fetchAll(function (err, rows) { if (err) { throw err; } io.sockets.json.send(rows); }); }); } function totals() { if (connections == 0) return; conn.query(countq, function (err, res) { if (err) { // Chances are that the server has just disconnected, lets try reconnecting dbdisconnect(); dbconnect(); throw err; } res.fetchAll(function (err, rows) { if (err) { throw err; } io.sockets.json.send(rows); }); }); } setInterval(update, 250); setInterval(totals,1000); setInterval(function() { console.log("Number of connections: " + connections); },1800000); daemon.daemonize('/var/log/epiclog.log', '/var/run/mything.pid', function (err, pid) { // We are now in the daemon process if (err) return sys.puts('Error starting daemon: ' + err); sys.puts('Daemon started successfully with pid: ' + pid); });
Current version
function totals() { if (connections > 0) { var q = "SELECT query FROM table"; db.query(q, function (err, results, fields) { if (err) { console.error(err); return false; } for (var row in results) { io.sockets.send("{ ID: '" + results[row].ID + "', event: '" + results[row].event + "', free: '" + results[row].free + "', total: '" + results[row].total + "', state: '" + results[row]$ row = null; } results = null; fields = null; err = null; q = null; }); } }
Still leaking memory, but it seems only on these conditions:
Basic usage of socket.io causes incremental memory usage (about +4mb every second). Even when nothing is transmitting.
A quick way to fix Node. js memory leaks in the short term is to restart the app. Make sure to do this first and then dedicate the time to seek out the root cause of the memory leak.
They are absolutely NOT SAFE.
As of v3, socket.io has a default message limit of 1 MB. If a message is larger than that, the connection will be killed.
Do yourself a favour and use node-mysql, it's a pure javascript mysql client and it's fast. Other than that, you should be using asynchronous code to stop IO being blocked whilst you're working. Using the async library will help you here. It has code for waterfall callback passing among other things.
As for your memory leaking, it probably isn't socket.io, although I haven't used it in a few months, I have had many thousands of concurrent connections and not leaked memory, and my code wasn't the best either.
Two things, however. Firstly your code is faily unreadable. I suggest looking into properly formatting your code (I use two spaces for every indentation but some people use four). Secondly, printing the number of connections every half an hour seems a little silly, when you could do something like:
setInterval(function() { process.stdout.write('Current connections: ' + connections + ' \r'); }, 1000);
The \r
will cause the line to be read back to the start of the line and overwrite the characters there, which will replace the line and not create a huge amount of scrollback. This will help with debugging if you choose to put debugging details in your logging.
You can also use process.memoryUsage()
for quickly checking the memory usage (or how much node thinks you're using).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With