Memory leak in a Node.js extension written in Rust

439 views Asked by At

I'm writing a Node.js extension in Rust. I called this toy library from C, checked it with Valgrind and no memory leak was found.

I called the same library from Python and Ruby, running in an infinite loop, and see no sign of a memory leak. (I can not post the picture due to insufficient reputation here).

When the same library is called from Node.js, the memory usage seems to increase with time: The memory usage of the Node.js module

The unit of time is the cycle of loop, not a real time.

Here is the Rust code:

#[repr(C)]
pub struct Matrix {
    m: Vec<Vec<f64>>,
}

#[no_mangle]
pub extern "C" fn matrix_new(nrow: usize, ncol: usize) -> *mut Matrix {
    let mut m = Vec::new();
    for _ in 0..(nrow) {
        let mut n = Vec::new();

        for _ in 0..(ncol) {
            n.push(0.0);
        }

        m.push(n);
    }

    Box::into_raw(Box::new(Matrix { m: m }))
}

#[no_mangle]
pub extern "C" fn matrix_free(matrix: *mut Matrix) {
    if matrix.is_null() {
        return
    }

    unsafe { Box::from_raw(matrix); }
}

Here is the original Node.js code:

var ref = require('ref');
var ffi = require('ffi');

var c_matrix = ref.types.void;
var c_matrix_ptr = ref.refType(c_matrix);

var libmatrix = ffi.Library('./target/release/libmatrix.so', {
    'matrix_new': [ c_matrix_ptr, ['size_t', 'size_t']],
    'matrix_get': [ 'double', [ c_matrix_ptr, 'size_t', 'size_t']],
    'matrix_set': [ 'double', [ c_matrix_ptr, 'size_t', 'size_t', 'double']],
    'matrix_free': [ 'void', [ c_matrix_ptr ]]
});

var matrix = function(nrow, ncol) {
    "use strict";

    var matrix = libmatrix.matrix_new(nrow, ncol);

    Object.defineProperty(this, '_matrix', {
        value: matrix,
        writeable: false
    });

    return this;
};

matrix.prototype.get = function(row, col) {
    "use strict";

    return libmatrix.matrix_get(this._matrix, row, col);
};

matrix.prototype.set = function(row, col, value) {
    "use strict";

    libmatrix.matrix_set(this._matrix, row, col, value);
};

matrix.prototype.free = function() {
    "use strict";

    libmatrix.matrix_free(this._matrix);
};

module.exports = matrix;

if (!module.parent) {
    while (true) {
        var m = new matrix(3, 3);

        m.free();
        m = null;
        delete global.m;

        global.gc();
        console.log(process.memoryUsage().rss);
    }
}

Some information:

  • OS: Debian GNU/Linux (Jessie)
  • Node.js: 7.2.0
  • node-gyp: 7.2.0
  • ffi: 2.2.0
  • ref: 1.3.3
  • Rust: 1.13.0

I re-wrote the same library in C:

typedef struct Matrix {
    size_t nrow;
    size_t ncol;
    double** mtx;
} Matrix;

void* matrix_new(size_t row, size_t col) {
    Matrix* m = malloc(sizeof(Matrix));
    m->nrow = row;
    m->ncol = col;

    m->mtx = malloc(m->nrow * sizeof(double*));

    for (int i = 0; i < m->nrow; i++) {
        *((m->mtx)+i) = (double*) malloc(m->ncol * sizeof(double));
    }

    for (int i = 0; i < m->nrow; i++) {
        for (int j = 0; j < m->ncol; j++) {
            m->mtx[i][j] = 0.0;
        }
    }

    return (void*) m;
}

void matrix_free(void* m) {
    if (m == NULL) {
        return;
    }

    double** ptr = ((Matrix*) m)->mtx;
    for (int i = 0; i < ((Matrix*) m)->nrow; i++) {
        free((void*) (ptr[i]));
    }

   free((void*) ((Matrix*) m)->mtx);
   free((void*) m);
}

I also changed the version of Node from 7.2.0 to 7.3.0. Here is the memory usage of the Node.js module with both C and Rust implementation:

The memory usage of the Node.js module from Rust and C

I tried a no-op library without changing the Node.js code, finding something surprising myself:

The memory usage of the Node.js module from Rust, C, and no-op

0

There are 0 answers