<div dir="ltr"><div>Hi Adam,</div><div><br></div><div>You are welcome! I am happy to say that your patch also fixes the memory leak. I believe there is another memory leak in the JavaScript runtime (see the end of the previous message). I am gonna look into it.</div><div><br></div><div>Sincerely,</div><div>Saulo</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Aug 24, 2016 at 1:17 PM, Adam Chlipala <span dir="ltr"><<a href="mailto:adamc@csail.mit.edu" target="_blank">adamc@csail.mit.edu</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
  
    
  
  <div bgcolor="#FFFFFF" text="#000000">
    <p>Thanks for spotting this problem.  I've pushed a different fix
      that makes dynamic input elements create <script> tags, so
      that the original dyn() code finds them to garbage-collect.  Does
      this change also seem to fix the problem?</p>
    <p>(I'd like to avoid using createNodeIterator(), which only became
      widely deployed about 5 years ago.  I think Ur/Web client-side
      reactive code today will still work on browsers from about the
      year 2000, and I'd like to keep it that way unless there's a very
      compelling reason to the contrary.)<br>
    </p><div><div class="h5">
    <br>
    <div>On 08/12/2016 09:55 PM, Saulo Araujo
      wrote:<br>
    </div>
    </div></div><blockquote type="cite"><div><div class="h5">
      <div dir="ltr">
        <div>Hi,</div>
        <div><br>
        </div>
        <div>I believe there is a substantial memory leak in the
          JavaScript runtime of Ur/Web. To see it happening:</div>
        <div><br>
        </div>
        <div>1) visit <a href="http://timesheet-ur.sauloaraujo.com:8080/TimeSheet/application" target="_blank">http://timesheet-ur.<wbr>sauloaraujo.com:8080/<wbr>TimeSheet/application</a>
          with Google Chrome</div>
        <div>2) open the developer tools by pressing f12</div>
        <div>3) click in the "Profiles" tab</div>
        <div>4) click in the "Take Heap Snapshot" radio button</div>
        <div>5) click in the "Take Snapshot" button</div>
        <div>6) type Detached in the "Class filter" text box (you will
          see that the heap has about 7MB and Detached DOM trees are
          retaining 0% of the memory - <a href="https://snag.gy/2nF9fz.jpg" target="_blank">https://snag.gy/2nF9fz.jpg</a>)</div>
        <div><br>
        </div>
        <div>7) click 10 times in the foward icon (the one on the right
          of the "Date" header in the time sheet application)</div>
        <div>9) click in the "Take Snapshot" button</div>
        <div>10) type Detached in the "Class filter" text box (you will
          see that the heap has about 43MB and Detached DOM trees are
          retaining 16% of the memory - <a href="https://snag.gy/Crc3Vg.jpg" target="_blank">https://snag.gy/Crc3Vg.jpg</a>)</div>
        <div><br>
        </div>
        <div>11) click 10 times in the foward icon (the one on the right
          of the "Date" header in the time sheet application)</div>
        <div>12) click in the "Take Snapshot" button</div>
        <div>13) type Detached in the "Class filter" text box (you will
          see that the heap has about 78MB and Detached DOM trees are
          retaining 17% of the memory - <a href="https://snag.gy/P5BLhq.jpg" target="_blank">https://snag.gy/P5BLhq.jpg</a>)</div>
        <div><br>
        </div>
        <div>I have spent quite some time investigating this memory leak
          and I believe the problem is in the function recreate inside
          the function dyn:</div>
        <div><br>
        </div>
        <div>function dyn(pnode, s) {</div>
        <div>    if (suspendScripts)</div>
        <div>        return;</div>
        <div><br>
        </div>
        <div>    var x = document.createElement("<wbr>script");</div>
        <div>    x.dead = false;</div>
        <div>    x.signal = s;</div>
        <div>    x.sources = null;</div>
        <div>    x.closures = null;</div>
        <div><br>
        </div>
        <div>    var firstChild = null;</div>
        <div><br>
        </div>
        <div>    x.recreate = function(v) {</div>
        <div>        for (var ls = x.closures; ls; ls = ls.next)</div>
        <div>            freeClosure(ls.data);</div>
        <div><br>
        </div>
        <div>        var next;</div>
        <div>        for (var child = firstChild; child && child
          != x; child = next) {</div>
        <div>            next = child.nextSibling;</div>
        <div><br>
        </div>
        <div>            killScript(child);</div>
        <div>            if (child.getElementsByTagName) {</div>
        <div>                var arr =
          child.getElementsByTagName("<wbr>script");</div>
        <div>                for (var i = 0; i < arr.length; ++i)</div>
        <div>                    killScript(arr[i]);</div>
        <div>            }</div>
        <div>...</div>
        <div><br>
        </div>
        <div>Note that recreate only kills <script> nodes .
          Therefore, <input>, <textarea> and <select>
          nodes created through <ctextbox>, <ctextarea> and
          <cselect> will continue to be in the dyns lists of its
          sources. To fix this memory leak, I propose changing the
          function dyn to</div>
        <div><br>
        </div>
        <div>function dyn(pnode, s) {</div>
        <div>    if (suspendScripts)</div>
        <div>        return;</div>
        <div><br>
        </div>
        <div>    var x = document.createElement("<wbr>script");</div>
        <div>    x.dead = false;</div>
        <div>    x.signal = s;</div>
        <div>    x.sources = null;</div>
        <div>    x.closures = null;</div>
        <div><br>
        </div>
        <div>    var firstChild = null;</div>
        <div><br>
        </div>
        <div>    x.recreate = function(v) {</div>
        <div>        for (var ls = x.closures; ls; ls = ls.next)</div>
        <div>            freeClosure(ls.data);</div>
        <div><br>
        </div>
        <div>        var next;</div>
        <div>        for (var child = firstChild; child && child
          != x; child = next) {</div>
        <div>            next = child.nextSibling;</div>
        <div><br>
        </div>
        <div>            killDyns(child)</div>
        <div>...</div>
        <div><br>
        </div>
        <div>Below is the function killDyns.</div>
        <div><br>
        </div>
        <div>function killDyns(node) {</div>
        <div>    var nodeIterator = document.createNodeIterator(<wbr>node,
          NodeFilter.SHOW_ELEMENT, function (node) {</div>
        <div>        return node.dead !== undefined && node.dead
          === false</div>
        <div>    })</div>
        <div>    var node = nodeIterator.nextNode();</div>
        <div>    while (node) {</div>
        <div>        killScript(node);</div>
        <div>        node = nodeIterator.nextNode();</div>
        <div>    }</div>
        <div>}</div>
        <div><br>
        </div>
        <div>killDyns traverses all descendants of a node that have a
          dead attribute equal to false and kills them with killScript.
          Therefore, killDyns may be less performant than the code it
          substitutes, but I believe killDyns is less prone to memory
          leaks than the original code. There is another small change to
          be done in urweb.js. You can see all changes in <a href="https://github.com/saulo2/urweb/commit/dcd280c85595ceee60a4fb78a3bfaf413a9eb7b8#diff-867e8dfbbc36419eefc0dfdf9db32883" target="_blank">https://github.com/saulo2/<wbr>urweb/commit/<wbr>dcd280c85595ceee60a4fb78a3bfaf<wbr>413a9eb7b8#diff-<wbr>867e8dfbbc36419eefc0dfdf9db328<wbr>83</a></div>
        <div><br>
        </div>
        <div>I did not make a pull request yet because I would like to
          know the opinion of the Ur/Web comitters about this fix. Maybe
          there is something that can be improved. Maybe there is a new
          bug in it :) In any case, repeating the previous steps with
          the proposed changes, I get the following results:</div>
        <div><br>
        </div>
        <div>6) type Detached in the "Class filter" text box (you will
          see that the heap has about 7MB and Detached DOM trees are
          retaining 0% of the memory - <a href="https://snag.gy/NicJow.jpg" target="_blank">https://snag.gy/NicJow.jpg</a>)</div>
        <div>10) type Detached in the "Class filter" text box (you will
          see that the heap has about 15MB and Detached DOM trees are
          retaining 0% of the memory - <a href="https://snag.gy/nuVfhg.jpg" target="_blank">https://snag.gy/nuVfhg.jpg</a>)</div>
        <div>13) type Detached in the "Class filter" text box (you will
          see that the heap has about 21MB and Detached DOM trees are
          retaining 0% of the memory - <a href="https://snag.gy/aPeUuK.jpg" target="_blank">https://snag.gy/aPeUuK.jpg</a>)</div>
        <div><br>
        </div>
        <div>The new results suggest that there is another memory leak
          in the JavaScript Runtime. However, I have not looked into it
          yet.</div>
        <div><br>
        </div>
        <div>Sincerely,</div>
        <div>Saulo</div>
      </div>
      <br>
      <fieldset></fieldset>
      <br>
      </div></div><pre>______________________________<wbr>_________________
Ur mailing list
<a href="mailto:Ur@impredicative.com" target="_blank">Ur@impredicative.com</a>
<a href="http://www.impredicative.com/cgi-bin/mailman/listinfo/ur" target="_blank">http://www.impredicative.com/<wbr>cgi-bin/mailman/listinfo/ur</a>
</pre>
    </blockquote>
    <br>
  </div>

<br>______________________________<wbr>_________________<br>
Ur mailing list<br>
<a href="mailto:Ur@impredicative.com">Ur@impredicative.com</a><br>
<a href="http://www.impredicative.com/cgi-bin/mailman/listinfo/ur" rel="noreferrer" target="_blank">http://www.impredicative.com/<wbr>cgi-bin/mailman/listinfo/ur</a><br>
<br></blockquote></div><br></div>