{"id":1165,"date":"2015-11-18T03:34:44","date_gmt":"2015-11-17T21:34:44","guid":{"rendered":"http:\/\/promincproductions.com\/blog\/?p=1165"},"modified":"2023-03-03T20:47:33","modified_gmt":"2023-03-04T02:47:33","slug":"node-js-fabric-js-memory-leaks-prevent-enomem","status":"publish","type":"post","link":"https:\/\/promincproductions.com\/blog\/node-js-fabric-js-memory-leaks-prevent-enomem\/","title":{"rendered":"Node JS Fabric JS Memory Leaks &#8211; Prevent ENOMEM"},"content":{"rendered":"<p>I&#8217;ve been working on tracking down a memory leak in my Fabric JS app running on Node JS hosted on a <a title=\"Rackspace cloud server\" href=\"https:\/\/promincproductions.com\/blog\/recommends\/rackspace-cloud-hosting\/\" target=\"_blank\" rel=\"nofollow noopener\" data-lasso-id=\"539\">Rackspace cloud server<\/a>.<\/p>\n<h2>The&nbsp;Problem<\/h2>\n<p>This server would never free up it&#8217;s used memory. &nbsp;I watch the memory climb and climb on the <a href=\"http:\/\/www.rackspace.com\/knowledge_center\/article\/install-and-configure-the-cloud-monitoring-agent\" target=\"_blank\" rel=\"noopener\" data-lasso-id=\"540\">server monitor<\/a> until it hits the dreaded drop off point. &nbsp;The server runs out of memory, it throws an ENOMEM error, and <a href=\"https:\/\/www.npmjs.com\/package\/forever\" target=\"_blank\" rel=\"noopener\" data-lasso-id=\"541\">forever<\/a> restarts the server.<\/p>\n<h2>Previous Attempts To Fix This Issue<\/h2>\n<p>I&#8217;ve looked into this issue before and discussed it in&nbsp;a&nbsp;<a href=\"https:\/\/promincproductions.com\/blog\/nodejs-handling-memory-issues-enomem-garbage-collection\/\" target=\"_blank\" rel=\"noopener\" data-lasso-id=\"542\">post about garbage collection in NodeJS<\/a>, however that sadly didn&#8217;t solve all of the issues.<\/p>\n<p>The other solution I&#8217;ve had in place for some time is utilizing a CRON job that will restart the server once a day in the off hours. &nbsp;This will reset the memory at that time. &nbsp;This hack works, but as usage has increased enough that it&#8217;s crashing before we ever get to that point.<\/p>\n<h2>Memory Leak Debugging Process<\/h2>\n<h3>Strip Out Code<\/h3>\n<p>Today I essentially stripped out code line by line to try an figure out where there could be an issue. &nbsp;In your code, take out function after function, line after line until you start to notice some differences in your memory monitor.<\/p>\n<p>This is a manual process. &nbsp;Take out some code, restart the server to get back to a baseline, run your process, and compare what&nbsp;the final memory usage is to what it was from your previous test. &nbsp;You&#8217;ll want to grab a paper and pencil to write some numbers down. &nbsp;If you think you&#8217;ve found something that made a change, undo the code change, retest, and prove that what you think you saw did happen.<\/p>\n<h3>Memory Monitoring<\/h3>\n<p>I&#8217;m utilizing <a href=\"http:\/\/hisham.hm\/htop\/\" target=\"_blank\" rel=\"noopener\" data-lasso-id=\"543\">htop<\/a> to monitor the servers memory usage on a dev server. &nbsp;(I changed my setup settings from the defaults so if the images below look different than you see that is why.)<\/p>\n<h3>Today&#8217;s Culprit &#8211; FabricJS canvas.clear()<\/h3>\n<p>I found something that made a difference! &nbsp;Sadly I think there still are some lingering issues out there, but this is a sizable enough change that I&#8217;m happy to have found a big issue here.<\/p>\n<p>It appears the canvas object isn&#8217;t being cleared properly. &nbsp;I had been clearing the canvas manually via Javascript, but apparently that either isn&#8217;t the correct method or sufficient enough. &nbsp;What I was doing is trying to set the canvas to null and then forcing the garbage collector to run.<\/p>\n<pre><code>\/\/ Create canvas\ncanvas = fabric.createCanvasForNode( width, height );\n\n\/\/ Process the canvas with some code here\n\n\/\/ Cleanup the canvas\ncanvas = null;\nglobal.gc();<\/code><\/pre>\n<p>What I found today though in the <a href=\"https:\/\/github.com\/kangax\/fabric.js\/issues\/1997\" target=\"_blank\" rel=\"noopener\" data-lasso-id=\"544\">FabricJS&nbsp;Github Issue #1997<\/a> is that there is a FabricJS method that will better cleanup these memory issues.<\/p>\n<pre><code>canvas.clear();\ncanvas.dispose();<\/code><\/pre>\n<p>According to the source code,&nbsp;<a href=\"http:\/\/fabricjs.com\/docs\/fabric.js.html#line7109\" target=\"_blank\" rel=\"noopener\" data-lasso-id=\"545\">canvas.dispose()<\/a> calls canvas.clear() and thus technically you would only need canvas.dispose();<\/p>\n<p>For me, I found that canvas.dispose(); didn&#8217;t exist. &nbsp;I am running an older version so it must have been added somewhere along the way.<\/p>\n<p>So for me, the solution I found is:<\/p>\n<pre><code>\/\/ Create canvas\ncanvas = fabric.createCanvasForNode( width, height );\n\n\/\/ Process the canvas with some code here\n\n\/\/ Cleanup the canvas\ncanvas.clear();\ncanvas.interactive &amp;&amp; canvas.removeListeners();\n<\/code><\/pre>\n<h4>Memory Usage Results<\/h4>\n<figure id=\"attachment_1167\" aria-describedby=\"caption-attachment-1167\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-baseline.jpg\" rel=\"attachment wp-att-67\" data-lasso-id=\"546\" data-rel=\"lightbox-gallery-WSi629J2\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img decoding=\"async\" class=\"size-medium wp-image-1167\" src=\"https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-baseline-500x214.jpg\" alt=\"Baseline memory usage\" width=\"500\" height=\"214\" srcset=\"https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-baseline-500x214.jpg 500w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-baseline-768x328.jpg 768w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-baseline-150x64.jpg 150w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-baseline-600x256.jpg 600w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-baseline.jpg 822w\" sizes=\"(max-width: 500px) 100vw, 500px\" \/><\/a><figcaption id=\"caption-attachment-1167\" class=\"wp-caption-text\">This is the memory usage just after a server reboot.<\/figcaption><\/figure>\n<figure id=\"attachment_1169\" aria-describedby=\"caption-attachment-1169\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-without-clear.jpg\" rel=\"attachment wp-att-69\" data-lasso-id=\"547\" data-rel=\"lightbox-gallery-WSi629J2\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img decoding=\"async\" class=\"size-medium wp-image-1169\" src=\"https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-without-clear-500x212.jpg\" alt=\"Memory usage with FabricJS memory leak\" width=\"500\" height=\"212\" srcset=\"https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-without-clear-500x212.jpg 500w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-without-clear-768x326.jpg 768w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-without-clear-150x64.jpg 150w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-without-clear-600x255.jpg 600w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-without-clear.jpg 827w\" sizes=\"(max-width: 500px) 100vw, 500px\" \/><\/a><figcaption id=\"caption-attachment-1169\" class=\"wp-caption-text\">After running the server process, you can see there is a memory leak. The server memory goes up and stays up. As pointed out above, this is due to not clearing the canvas created by FabricJS properly.<\/figcaption><\/figure>\n<figure id=\"attachment_1168\" aria-describedby=\"caption-attachment-1168\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-with-clear.jpg\" rel=\"attachment wp-att-68\" data-lasso-id=\"548\" data-rel=\"lightbox-gallery-WSi629J2\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img decoding=\"async\" class=\"size-medium wp-image-1168\" src=\"https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-with-clear-500x214.jpg\" alt=\"Memory usage after clearing FabricJS canvas\" width=\"500\" height=\"214\" srcset=\"https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-with-clear-500x214.jpg 500w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-with-clear-768x328.jpg 768w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-with-clear-150x64.jpg 150w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-with-clear-600x256.jpg 600w, https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-with-clear.jpg 833w\" sizes=\"(max-width: 500px) 100vw, 500px\" \/><\/a><figcaption id=\"caption-attachment-1168\" class=\"wp-caption-text\">I restarted the server to get back to the baseline usage above, and then ran the process &#8211; this time using canvas.clear(); to clear the FabricJS canvas from the NodeJS server memory. As you can see there is a significant drop in the retained memory. As you can also see it didn&#8217;t go back to what the baseline was and thus there still is another leak to find. But this was a big step in the right direction!<\/figcaption><\/figure>\n<p>&nbsp;<\/p>\n<h2>Other Useful Information<\/h2>\n<h3>NodeJS Streams<\/h3>\n<p>I didn&#8217;t find this to be the golden ticket in my debugging, but I did see plenty of discussion by other developers that using streams in NodeJS to be a better practice in regards to memory usage as opposed to reading\/writing directly from the disk. &nbsp;I&#8217;m not going to try and act like an expert on this topic however &#8211; there are already great resources out there including:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/substack\/stream-handbook\" target=\"_blank\" rel=\"noopener\" data-lasso-id=\"549\">stream-handbook<\/a><\/li>\n<li><a href=\"https:\/\/nodejs.org\/api\/stream.html#stream_event_drain\" target=\"_blank\" rel=\"noopener\" data-lasso-id=\"550\">Nodejs.org Stream Documentation<\/a><\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h3>Garbage Collection<\/h3>\n<p>I&#8217;ve written <a href=\"https:\/\/promincproductions.com\/blog\/nodejs-handling-memory-issues-enomem-garbage-collection\/\" target=\"_blank\" rel=\"noopener\" data-lasso-id=\"551\">garbage collection in NodeJS<\/a> in&nbsp;the past, but wanted to add a few points to the topic here.<\/p>\n<ul>\n<li>Garbage Collection can be a server intensive process. &nbsp;Improvements have been made in the backend code that have improved this, but it&#8217;s still not recommended to run this more than necessary. &nbsp;Many developers say you should never manually run it as the V8 engine that NodeJS runs on is&nbsp;good enough. &nbsp;I&#8217;m still not sure what my verdict on the topic is however.<\/li>\n<li>Garbage collection is a very complex topic. &nbsp;I&#8217;m not saying I fully understand it. &nbsp;But this article by J<a href=\"http:\/\/jayconrod.com\/posts\/55\/a-tour-of-v8-garbage-collection\" target=\"_blank\" rel=\"noopener\" data-lasso-id=\"552\">ay Conrod about V8 Garbage Collection<\/a> is a start for helping to understand how it operates.<\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been working on tracking down a memory leak in my Fabric JS app running on Node JS [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1167,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"wprm-recipe-roundup-name":"","wprm-recipe-roundup-description":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[51,12,52,5],"tags":[],"class_list":["post-1165","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-fabricjs","category-javascript-jquery","category-nodejs","category-website-development"],"jetpack_featured_media_url":"https:\/\/promincproductions.com\/blog\/wp-content\/uploads\/2015\/11\/nodejs-fabricjs-memory-leak-baseline.jpg","jetpack_shortlink":"https:\/\/wp.me\/p4BbcR-iN","jetpack_sharing_enabled":true,"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/promincproductions.com\/blog\/wp-json\/wp\/v2\/posts\/1165","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/promincproductions.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/promincproductions.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/promincproductions.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/promincproductions.com\/blog\/wp-json\/wp\/v2\/comments?post=1165"}],"version-history":[{"count":4,"href":"https:\/\/promincproductions.com\/blog\/wp-json\/wp\/v2\/posts\/1165\/revisions"}],"predecessor-version":[{"id":3269,"href":"https:\/\/promincproductions.com\/blog\/wp-json\/wp\/v2\/posts\/1165\/revisions\/3269"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/promincproductions.com\/blog\/wp-json\/wp\/v2\/media\/1167"}],"wp:attachment":[{"href":"https:\/\/promincproductions.com\/blog\/wp-json\/wp\/v2\/media?parent=1165"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/promincproductions.com\/blog\/wp-json\/wp\/v2\/categories?post=1165"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/promincproductions.com\/blog\/wp-json\/wp\/v2\/tags?post=1165"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}