Understanding Memory Behavior on NodeJS

Understanding Memory Behavior on NodeJS

The idea is to talk about memory in nodejs (V8 behavior), describing common and more complex leaks. Presenting how to debug, detect and solve problems with memory.

Understanding concepts like heap/stack, mark-and-sweep, garbage, memory allocation, cyclic reference, memory life cycle, V8's stop-the-world and more. In addition to showing real cases of memory leaks from globo.com.

Ed35943d3199ea37b1b60c39615e8163?s=128

Raphael Amorim

September 29, 2017
Tweet

Transcript

  1. 2.

    Raphael Amorim This guy seems like a nice person, but

    he doesn’t. Seriously. He likes topics related to JavaScript, Python, Clojure, WebGL, Algorithms and sometimes force some git push. 
 Working most part of his time in useless globo.com • js foundation/jQuery member • Mozillian • Metido a besta • @raphamundi This guy seems like a he doesn’t. Seriously. related to JavaScript, WebGL, Algorithms and some git push. 
 Working most part of h open source projects. some git push. 
 Working most part of his open source projects.
  2. 7.

    Developers (not all) don’t considers (or just doesn’t know) how

    tricky can be memory management in Nodejs.
  3. 8.
  4. 13.
  5. 14.
  6. 17.
  7. 25.

    In C and C++, this is accomplished by malloc() and

    free() Every program that consumes memory requires a mechanism for reserving and freeing space.
  8. 26.

    char * buffer; buffer = (char*) malloc (42); // Do

    something with buffer free (buffer);
  9. 27.

    char * buffer; buffer = (char*) malloc (42); // Do

    something with buffer free (buffer); What this means?
  10. 28.

    In this case (C++) the programmer is responsible for freeing

    heap memory that is no longer required.
  11. 30.

    /* Allocate 30 bytes to house a string */ char*

    str = new char [30]; /* Clear those 30 bytes and make str point nowhere */ delete [] str;
  12. 31.

    /* Allocate 30 bytes to house a string */ char*

    str = new char [30]; /* Give str another memory address with the first one gone forever */ str = new char [60]; delete [] str;
  13. 33.

    When a portion of memory allocated for certain operation is

    not released when it's no longer needed.
  14. 34.

    Fail to remove data that can not be referenced. When

    a portion of memory allocated for certain operation is not released when it is no longer needed. It’s a 
 Memory Leak
  15. 39.

    Performance of garbage collected languages is not strictly better or

    worse than languages without managed memory!
  16. 41.

    Resident Set ~ V8 uses a scheme similar to the

    Java Virtual Machine and divides the memory into segments ~
  17. 54.

    console.log(process.memoryUsage()); { rss: 4935680, heapTotal: 1826816, heapUsed: 650472, external: 49879

    } external refers to the memory usage of C++ objects bound to JavaScript objects managed by V8.
  18. 56.
  19. 58.

    // allocation for Number var num = 42; // allocation

    for String var str = “my string”;
  20. 59.

    // allocation for Number var num = 42; // allocation

    for String var str = “my string”; Rvalue Lvalue
  21. 61.

    // each element is a not named reference var arr

    = [1, null, "ABC"] *In-Object slack tracking
  22. 63.

    // each element is a not named reference var arr

    = [1, null, "ABC"] *In-Object slack tracking *overflow array
  23. 70.

    var myObject = {
 person: { name: "raphael", age: 21,

    }
 } > var anotherObject = myObject
  24. 71.

    var myObject = {
 person: { name: "raphael", age: 21,

    }
 } var anotherObject = myObject > myObject = 1
  25. 72.

    > var yetAnotherObject = anotherObject.person person: { name: "raphael", age:

    21, }
 } var anotherObject = myObject myObject = 1
  26. 74.

    > yetAnotherObject = null anotherObject = “some-random-string” var yetAnotherObject =

    anotherObject.person var anotherObject = myObject myObject = 1
  27. 79.

    function LeakingClass() { // do-nothing } var leaks = []

    setInterval(function() { for (var i = 0; i < 100; i++) { leaks.push(new LeakingClass) } console.error('Leaks: %d', leaks.length) }, 1000)
  28. 81.

    const developer = { name: “amorim”, company: “globocom” } doWork(()

    => { const data = developer.name // doing a bunch of work })
  29. 82.
  30. 83.

    const http = require(‘http') const server = http.createServer((req, res) =>

    { for (var i=0; i<1000; i++) { server.on('request', function LeakFunc() {}) } res.end(‘Hello, The Conf!’) }).listen(8585, ‘127.0.0.1') server.setMaxListeners(0) console.log(`Server running at 127.0.0.1:8585/. Process PID: ${process.pid}`)
  31. 86.

    function LeakingClass() { // do-nothing } var leaks = []

    setInterval(function() { for (var i = 0; i < 100; i++) { leaks.push(new LeakingClass) } console.error('Leaks: %d', leaks.length) }, 1000) leak.js
  32. 88.
  33. 89.
  34. 90.
  35. 91.

    Hint: You can also send the SIGUSR1 signal to an

    existing node process to enable the inspect mode as you need it.
  36. 92.

    Hint2: It's important to use named constructors, otherwise the heap

    snapshots will not produce useful outputs for you.
  37. 93.

    const http = require(‘http') const server = http.createServer((req, res) =>

    { for (var i=0; i<1000; i++) { server.on('request', function LeakFunc() {}) } res.end(‘Hello, The Conf!’) }).listen(8585, ‘127.0.0.1') server.setMaxListeners(0) console.log(`Server running at 127.0.0.1:8585/. Process PID: ${process.pid}`) server-leak.js
  38. 95.
  39. 97.
  40. 98.
  41. 99.

    There’s a lot of tools to help you! - node-memwatch

    - node-heapdump - node-inspector - nodetime - v8-profiler - memwatch-next - ...
  42. 100.

    const developer = { name: “amorim”, company: “globocom” } doWork(()

    => { const data = developer.name // doing a bunch of work }) closure-leak.js
  43. 101.

    const developer = { name: “amorim”, company: “globocom” } doWork(()

    => { const data = developer.name // doing a bunch of work developer = null })