2011-05-29 5 views
7

Tengo un lector de RSS/MongoDB/raspador que se ejecuta a través de un conjunto de datos más grande que mi sistema tiene memoria para. A medida que recorro los datos, el sistema se ralentiza. Estoy razonablemente seguro de que es porque me estoy quedando sin memoria.¿Cómo puedo decodificar el significado de los datos de memoria en node.js y depurar la pérdida de memoria?

He agregado algo de información de depuración y he realizado algunos cambios, pero no sé cómo leer la información proporcionada en la salida de depuración.

He aquí una muestra de salida de depuración (desde antes de que sea mortal):

100 items 
Memory: { rss: 11104256,  // what is RSS? 
      vsize: 57507840,  // what is VSIZE? 
      heapTotal: 4732352, // heapTotal? 
      heapUsed: 3407624 } // heapUsed? 
200 items 
Memory: { rss: 12533760, 
      vsize: 57880576, 
      heapTotal: 6136320, 
      heapUsed: 3541984 } 
           // what key numbers do I watch for? 
           // when do I reach 'situation critical'? 
           // how do I free up memory to prevent problems? 

Además, si ayuda y para una mejor ilustración, he incluido una muestra del código. Un cambio que ya hice es mover todas las instrucciones de solicitud fuera de la función de GrabRss.

var http = require('http'); 
var sys  = require('sys'); 
var xml2js = require('xml2js'); 
var util = require('util'); 
var Db  = require('../lib/mongodb').Db, 
    Conn = require('../lib/mongodb').Connection, 
    Server = require('../lib/mongodb').Server, 
    // BSON = require('../lib/mongodb').BSONPure; 
    BSON = require('../lib/mongodb').BSONNative; 

GrabRss = function(grab, start) {   
    var options = { 
     host: 'www.example.com', 
     port: 80, 
     path: '/rss/'+grab+'/'+start 
    }; 

    var data; 
    var items; 
    var checked = 0; 
    var len = 0; 

    GotResponse = function(res) { 
     var ResponseBody = ""; 
     res.on('data', DoChunk); 
     res.on('end', EndResponse); 

     function DoChunk(chunk){ 
      ResponseBody += chunk; 
     } 
     function EndResponse() { 
      //console.log(ResponseBody); 
      var parser = new xml2js.Parser(); 
      parser.addListener('end', GotRSSObject); 
      parser.parseString(ResponseBody); 
     } 
    } 

    GotError = function(e) { 
     console.log("Got error: " + e.message); 
    } 

    GotRSSObject = function(r){ 
     items = r.item; 
     //console.log(sys.inspect(r)); 

     var db = new Db('rss', new Server('localhost', 27017, {}), {native_parser:false}); 
     db.open(function(err, db){ 
      db.collection('items', function(err, col) { 
       len = items.length; 
       if (len === 0) { 
        process.exit(0); 
       } 
       for (i in items) { 
        SaveItem(item[i], col); 
       } 
      }); 
     }); 
    } 

    SaveMovie = function(i, c) { 
     c.update({'id': i.id}, {$set: i}, {upsert: true, safe: true}, function(err){ 
      if (err) console.warn(err.message); 
      if (++checked >= len) { 
       if (checked < 5000) { 
         delete data; // added since asking 
         delete items; // added since asking 

        console.log(start+checked); 
        console.log('Memory: '+util.inspect(process.memoryUsage())); 
        GrabRss(50, start+checked); 
       } else { 
        console.log(len); 
        process.exit(0); 
       } 
      } else if (checked % 10 == 0) { 
       console.log(start+checked); 
      } 
     }); 
    } 
    http.get(options, GotResponse).on('error', GotError); 

} 
GrabRss(50, 0); 

Respuesta

8

Después de leer este código, yo veo que items en GotRSSObject se declara como global, porque no hay var anteponiendo a él.

Aparte de eso, no veo otras pérdidas de memoria evidentes. Una buena técnica básica es agregar algunas instrucciones de impresión para ver dónde se está asignando la memoria y luego verificar dónde se espera que se limpie la memoria al afirmar que las variables == null.

El problema con la memoria con node.js y v8 es que no se garantiza que se recolecte basura en ningún momento y afaik, no se puede forzar la recolección de basura. Querrá limitar la cantidad de datos con los que está trabajando para encajar fácilmente en la memoria y proporcionar algún tipo de manejo de errores (quizás con setTimeout o process.nextTick) para esperar hasta que la memoria se haya limpiado.

Un consejo con nextTick: es una llamada muy, muy rápida. Node.js tiene un único subproceso en un bucle de eventos, como todos saben. El uso de nextTick ejecutará literalmente esa función en el próximo ciclo, asegúrate de no llamar con mucha frecuencia, de lo contrario te encontrarás perdiendo ciclos.

Y con respecto a rss, vsize, heapTotal, heapUsed ... vsize es el tamaño de la totalidad de memoria que su proceso está utilizando y rss es cuánto de lo que hay en la memoria RAM física real y no de intercambio. heaptotal y heapUsed se refieren al almacenamiento subyacente de v8 del que no tiene control. En su mayoría, le interesará vsize, pero también puede obtener información más detallada con top o Activity Monitor en OS X (¿alguien sabe de las buenas herramientas de visualización de procesos en los sistemas * nix?).

+0

gracias. La siguiente marca parece haber suavizado realmente el procesamiento. ¿Alguna idea de lo que significan los valores de memoria (rss, vsize, heaptotal, heapused)? La documentación aquí http://nodejs.org/docs/v0.4.8/api/all.html#process.memoryUsage no entra en detalles. –

+0

Un consejo con nextTick: es una llamada muy, muy rápida. Node.js tiene un único subproceso en un bucle de eventos, como todos saben. El uso de nextTick ejecutará literalmente esa función en el próximo ciclo, asegúrate de no llamar con mucha frecuencia, de lo contrario te encontrarás perdiendo ciclos. – tjarratt

+0

Y con respecto a rss, vsize, heaptotal, heapused ... vsize es el tamaño completo de la memoria que está usando su proceso y rss es la cantidad de eso en la RAM física real y no en el intercambio. Teaptotal y heapused se refieren al almacenamiento subyacente de v8 del que usted no tiene control. – tjarratt

Cuestiones relacionadas