Sunday, May 26, 2019

JavaScript string memory leak

If you create the following HTML file
<html>
  <body>
    <script>
      function createString() {
        return "0".repeat(25 * 1024 * 1024).substring(0, 12);
      }

      var arr = [];

      setInterval(function() {
         let str = createString();
         arr.push(str);
      }, 500);
    </script>
  </body>
</html>
run it in your browser and open the task manager you could notice that the browser takes some limited amount of memory ~200Mb. But what if we update one single digit in our code
return "0".repeat(25 * 1024 * 1024).substring(0, 13);
i.e. replace 12 with 13, run it into a browser and have a look at the process memory. It grows unstoppable and makes the browser crash in a half a minute! Why does it happen?

Every time we call String.prototype.substring function it returns new string instance that keeps .. a link to the original string! That's how V8 organizes memory for strings for permamance reason. Exception is when a new instance string length is less than 13 - in that case no link to original string preserved. So in our example arr contains no just short strings(13), but actually the huge ones (25 * 1024 * 1024). How to avoid it? Unfortunatelly, there is no documemnted method to 'cut' parents from strings, that's because we can do the following (update line 11):
<html>
  <body>
    <script>
      function createString() {
        return "0".repeat(25 * 1024 * 1024).substring(0, 12);
      }

      var arr = [];

      setInterval(function() {
         let str = createString().split('').join('');
         arr.push(str);
      }, 500);
    </script>
  </body>
</html>
That manoeuvre breaks the link with the original string and prevents memory leak.

No comments:

Post a Comment