8 Replies Latest reply on Jun 26, 2009 11:53 AM by kapila Silwathge

    Still finding some memory issues with Seam Remoting and IE6

    Joshua Davis Expert

      I posted something about this a while ago in the old forums here.


      Basically, there still seem to be some memory issues that arise when invoking lots of Seam Remoting methods in IE6.  I've made a simple test page that invokes a single SFSB method over and over again by invoking the method inside it's own callback function.   On my test machine this seems to work until about 30k calls, then IE6 puts up an alert saying there is not enough memory to complete the operation.


      Has anyone else encountered this?


      We have a patch that seems to fix this, but it's kind of ugly.  Basically we wrap the request and the callback in a separate object and put those in an associative array.   I'm wondering if anyone knows of a better way to do this.


              // An object that holds the request and the callback
              function ReqEntry(req, callback)
              {
                  this.req = req
                  this.callback = callback
              }
      
              // Global array that requestCallback can use.
              var REQ = new Array(100);
              var REQ_COUNTER = 0;
      
              Seam.Remoting.sendAjaxRequest = function(envelope, path, callback, silent)
              {
                Seam.Remoting.log("Request packet:\n" + envelope);
      
                if (!silent)
                  Seam.Remoting.displayLoadingMessage();
      
                var asyncReq;
      
                if (window.XMLHttpRequest)
                {
                  asyncReq = new XMLHttpRequest();
                  if (asyncReq.overrideMimeType)
                    asyncReq.overrideMimeType('text/xml');
                }
                else
                  asyncReq = new ActiveXObject("Microsoft.XMLHTTP");
      
      //          var rcb = Seam.Remoting.requestCallback;
      //          asyncReq.onreadystatechange = function() {
      //            if (rcb) rcb(asyncReq, callback);
      //          }
      
                // Put the request into the array.
                if (REQ_COUNTER >= REQ.length)
                  REQ_COUNTER = 0;
                var reqIndex = REQ_COUNTER++;
                REQ[reqIndex] = new ReqEntry(asyncReq,callback);
                // Pass the index to the callback.
                asyncReq.onreadystatechange = new Function("Seam.Remoting.requestCallback(" + reqIndex +");")
      
                if (Seam.Remoting.encodedSessionId)
                {
                  path += ';jsessionid=' + Seam.Remoting.encodedSessionId;
                }
      
                asyncReq.open("POST", Seam.Remoting.resourcePath + path, true);
                asyncReq.send(envelope);
                return asyncReq;
              }
      
              Seam.Remoting.requestCallback = function(reqIndex)
              {
                // Get the request and the callback from the array.
                var req = REQ[reqIndex].req;
                var callback = REQ[reqIndex].callback;
                if (req.readyState == 4)
                {
                    try
                    {
                      var inScope = typeof(Seam) == "undefined" ? false : true;
      
                      if (inScope) Seam.Remoting.hideLoadingMessage();
      
                      // Don't need this.
                      // req.onreadystatechange = function() {};
      
                      if (req.status == 200)
                      {
                        if (inScope) Seam.Remoting.log("Response packet:\n" + req.responseText);
      
                        if (callback)
                          callback(req.responseXML);
                      }
                      else
                        alert("There was an error processing your request.  Error code: " + req.status);
                    }
                    finally
                    {
                      REQ[reqIndex] = null;
                      // Memory cleanup - Seam.Remoting.sendAjaxRequest creates a JS/COM/JS circular reference in IE by
                      // making a closure and then pointing the asyncRequest at it.  JavaScript is pointing at the
                      // COM object (asyncReq), and the asyncReq COM object now points at a JavaScript object (the closure).
                      // Here is the code:
                      // asyncReq.onreadystatechange = function() {Seam.Remoting.requestCallback(asyncReq, callback); }
                      // Since IE cannot GC the two interlinked object, we need to help out by forcibly deleting the
                      // closure.
                      delete req.onreadystatechange;
                      if (Seam.Remoting.batchAsyncReq == req)
                      {
                        delete Seam.Remoting.batchAsyncReq.onreadystatechange
                        Seam.Remoting.batchAsyncReq = null;
                      }
                   }
                 }
              }



      Simply deleting the onreadystatechange doesn't seem to work.  I'll keep experimenting.


        • 1. Re: Still finding some memory issues with Seam Remoting and IE6
          Joshua Davis Expert

          Okay, the above approach works fine for over 300k requests.   I'd like to contribute it if anyone is interested.

          • 3. Re: Still finding some memory issues with Seam Remoting and IE6
            Arbi Sookazian Master

            300K requests from one client or many clients?  this seems like an abnormal use case, Seam remoting doesn't seemed to be used that often and in fact a4j:jsFunction can cover much of the same ground...

            • 4. Re: Still finding some memory issues with Seam Remoting and IE6
              Luke Simon Newbie

              Hi Joshua,


              I don't know if you use Richfaces but instad of use your code you can use Prototype that is in Richfaces. It takes care of make the right ajax call.


              new Ajax.Request(url, {
                method: 'get',
                onSuccess: function(transport) {
                  alert(transport.responseText);
                }
              });
              



              It would not fix your problem, It's only a suggestion :)


              http://www.prototypejs.org/api/ajax

              • 5. Re: Still finding some memory issues with Seam Remoting and IE6
                Joshua Davis Expert

                Ron Ramirez wrote on Mar 24, 2009 22:12:


                300K requests from one client or many clients?


                Our requirements are that the information on the page is up to the second, and that the user can remain logged in all day looking at the page.   We use Seam Remoting to poll the server about 3 times per second, so 8h*60m*60s*3=86400 requests per day, per browser.



                this seems like an abnormal use case, Seam remoting doesn't seemed to be used that often and in fact a4j:jsFunction can cover much of the same ground...


                This is not an abnormal requirement for a trading application.  We tried a4j:jsFunction (and a4j polling, and mixing Seam Remoting and a4j), it was far too slow and did not give us the control we need on the JS side.   Seam Remoting works great for us, except a few bugs here and there.


                There's nothing wrong with having software that works properly and doesn't crash people's browsers, even if the requirements are abnormal or their browser is old and crusty.  :)



                We've had our system running, with our Seam Remoting JS patches for a year now without many major IE6 issues.




                • 6. Re: Still finding some memory issues with Seam Remoting and IE6
                  Joshua Davis Expert

                  We do use prototype that's included with RichFaces, but back-patching that into Seam Remoting seemed a little scary to me.   I'll give it a try.

                  • 7. Re: Still finding some memory issues with Seam Remoting and IE6
                    Joshua Davis Expert

                    Luke Simon wrote on Mar 25, 2009 09:00:


                    Hi Joshua,

                    I don't know if you use Richfaces but instad of use your code you can use Prototype that is in Richfaces. It takes care of make the right ajax call.

                    new Ajax.Request(url, {
                      method: 'get',
                      onSuccess: function(transport) {
                        alert(transport.responseText);
                      }
                    });
                    



                    It would not fix your problem, It's only a suggestion :)

                    http://www.prototypejs.org/api/ajax


                    Prototype's function seems to simplify the SeamRemoting code and it solves the memory issue in IE6:


                    Seam.Remoting.sendAjaxRequest = function(envelope, path, callback, silent)
                    {
                        Seam.Remoting.log("Request packet:\n" + envelope);
                    
                        if (!silent)
                            Seam.Remoting.displayLoadingMessage();
                    
                        if (Seam.Remoting.encodedSessionId)
                        {
                            path += ';jsessionid=' + Seam.Remoting.encodedSessionId;
                        }
                        var req = new Ajax.Request(
                                Seam.Remoting.resourcePath + path,
                        {
                            method: 'post',
                            postBody: envelope,
                            contentType: 'text/xml',
                            evalJS: false,
                            evalJSON: false,
                            onSuccess: function(transport)
                            {
                                Seam.Remoting.requestCallback(transport,callback);
                            },
                            onFailure: function(transport)
                            {
                    
                            }
                        });
                    };
                    
                    Seam.Remoting.requestCallback = function(req,callback)
                    {
                        if (callback)
                        {
                            // The following code deals with a Firefox security issue.  It reparses the XML
                            // response if accessing the documentElement throws an exception
                            try
                            {
                                req.responseXML.documentElement;
                                callback(req.responseXML);
                            }
                            catch (ex)
                            {
                                try
                                {
                                    // Try it the IE way first...
                                    var doc = new ActiveXObject("Microsoft.XMLDOM");
                                    doc.async = "false";
                                    doc.loadXML(req.responseText);
                                    callback(doc);
                                }
                                catch (e)
                                {
                                    // If that fails, use standards
                                    var parser = new DOMParser();
                                    callback(parser.parseFromString(req.responseText, "text/xml"));
                                }
                            }
                        }
                    };



                    I know this probably won't be part of Seam, but it works well and (my favorite part) it's much less code then what I posted earlier.

                    • 8. Re: Still finding some memory issues with Seam Remoting and IE6
                      kapila Silwathge Newbie

                      Great ! Yoshua,
                      I patched remote.js of seam 2.1.1 with your initial code ......
                      it works fine.