<html>
  <head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  </head>
  <body 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>
    <br>
    <div class="moz-cite-prefix">On 08/12/2016 09:55 PM, Saulo Araujo
      wrote:<br>
    </div>
    <blockquote
cite="mid:CA+ckhoTcGRN=uhb5a7Ed5J5BGRRZUHWT5_yV7+yonEjVu=unrg@mail.gmail.com"
      type="cite">
      <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 moz-do-not-send="true"
            href="http://timesheet-ur.sauloaraujo.com:8080/TimeSheet/application">http://timesheet-ur.sauloaraujo.com:8080/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 moz-do-not-send="true"
            href="https://snag.gy/2nF9fz.jpg">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 moz-do-not-send="true"
            href="https://snag.gy/Crc3Vg.jpg">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 moz-do-not-send="true"
            href="https://snag.gy/P5BLhq.jpg">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("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("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("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(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
            moz-do-not-send="true"
href="https://github.com/saulo2/urweb/commit/dcd280c85595ceee60a4fb78a3bfaf413a9eb7b8#diff-867e8dfbbc36419eefc0dfdf9db32883">https://github.com/saulo2/urweb/commit/dcd280c85595ceee60a4fb78a3bfaf413a9eb7b8#diff-867e8dfbbc36419eefc0dfdf9db32883</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 moz-do-not-send="true"
            href="https://snag.gy/NicJow.jpg">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 moz-do-not-send="true"
            href="https://snag.gy/nuVfhg.jpg">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 moz-do-not-send="true"
            href="https://snag.gy/aPeUuK.jpg">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 class="mimeAttachmentHeader"></fieldset>
      <br>
      <pre wrap="">_______________________________________________
Ur mailing list
<a class="moz-txt-link-abbreviated" href="mailto:Ur@impredicative.com">Ur@impredicative.com</a>
<a class="moz-txt-link-freetext" href="http://www.impredicative.com/cgi-bin/mailman/listinfo/ur">http://www.impredicative.com/cgi-bin/mailman/listinfo/ur</a>
</pre>
    </blockquote>
    <br>
  </body>
</html>