#124 bug
Rodrigo Rosauro

Ajax request consumes huge memory (1GB+) for big responses

Reported by Rodrigo Rosauro | May 29th, 2008 @ 03:59 PM | in After 1.7

I'm using "Ajax.Request" to get a big (4 MB) XML file from the server with Firefox (2 and 3RC1) and prototype.js 1.6.0.2.

Every time this ajax call is performed, the browser hangs for around a minute and the memory consumption grows up to ~1GB.

At first I thought this was a FF problem, but just to be sure I tested using only the native XMLHttpRequest object, getting for my surprise an absolutely instantaneous response without any visible memory consumption.

After some investigation I found that the cause of the problem relies on line #1361 (the Ajax.Response constructor). Here follows the problematic code:

    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
      this.status       = this.getStatus();
      this.statusText   = this.getStatusText();
      this.responseText = String.interpret(transport.responseText); // <- problem
      this.headerJSON   = this._getHeaderJSON();
    }

Since Firefox sends a "readyState" 3 for every 4096 bytes read, the "transport.responseText" property is accessed many times during the request execution, and every time this native property is accessed, the browser needs to create a new (big) JavaScript String instance with its contents.

In order to fix MY problem, I just removed the verification of readyState 3 (because I don't need any data when this status arrives), but a more general solution could be to just move line #1361 to the next "if" (which checks explicitly for readyState 4.

My final code (modified):

Ajax.Response = Class.create({
  initialize: function(request){
    this.request = request;
    var transport  = this.transport  = request.transport,
        readyState = this.readyState = transport.readyState;

    if(readyState == 4) {
      this.status       = this.getStatus();
      this.statusText   = this.getStatusText();
      this.responseText = String.interpret(transport.responseText);
      this.headerJSON   = this._getHeaderJSON();
      var xml = transport.responseXML;
      this.responseXML  = Object.isUndefined(xml) ? null : xml;
      this.responseJSON = this._getResponseJSON();
    }
  },

Proposed general-solution code:

  initialize: function(request){
    this.request = request;
    var transport  = this.transport  = request.transport,
        readyState = this.readyState = transport.readyState;

    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
      this.status       = this.getStatus();
      this.statusText   = this.getStatusText();
      this.headerJSON   = this._getHeaderJSON();
    }

    if(readyState == 4) {
      this.responseText = String.interpret(transport.responseText);
      var xml = transport.responseXML;
      this.responseXML  = Object.isUndefined(xml) ? null : xml;
      this.responseJSON = this._getResponseJSON();
    }
  },

Comments and changes to this ticket

  • Juriy Zaytsev

    Juriy Zaytsev May 29th, 2008 @ 07:57 PM

    • Milestone set to 1.7
    • State changed from “new” to “enhancement”
  • Tobie Langel

    Tobie Langel May 30th, 2008 @ 01:34 AM

    • State changed from “enhancement” to “bug”
  • eno

    eno November 16th, 2008 @ 02:16 PM

    • Tag set to ajax, needs_patch, needs_tests

    I run into the same problem, in similar circumstances. Now even though the OP's original workaround would fix that problem, I would appreciate fixing

    String.interpret(transport.responseText);

    instead. As this is not exactly documented - and may be not part of the official API - I take it it is meant to convert some invalid values into empty strings and leave strings unchanged. Firefox, however, seens to always create new string objects. See #447

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 01:54 AM

    • Tag changed from ajax, needs_patch, needs_tests to needs_patch, needs_tests, section:ajax

    [not-tagged:"ajax" tagged:"section:ajax" bulk edit command]

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 02:26 AM

    • Tag changed from needs_patch, needs_tests, section:ajax to missing:tests, needs_patch, section:ajax

    [not-tagged:"needs_tests" tagged:"missing:tests" bulk edit command]

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 02:28 AM

    • Tag changed from missing:tests, needs_patch, section:ajax to missing:patch, missing:tests, section:ajax

    [not-tagged:"needs_patch" tagged:"missing:patch" bulk edit command]

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 03:36 AM

    • Tag changed from missing:patch, missing:tests, section:ajax to missing:patch, needs:tests, section:ajax

    [not-tagged:"missing:tests" tagged:"needs:tests" bulk edit command]

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 03:37 AM

    • Tag changed from missing:patch, needs:tests, section:ajax to needs:patch, needs:tests, section:ajax

    [not-tagged:"missing:patch" tagged:"needs:patch" bulk edit command]

  • T.J. Crowder

    T.J. Crowder November 16th, 2009 @ 04:50 PM

    [responsible:none bulk edit command]

  • Andrew Dupont

    Andrew Dupont October 19th, 2010 @ 03:49 AM

    • Milestone changed from 1.7 to After 1.7
    • Assigned user set to “Tobie Langel”
    • Importance changed from “” to “Medium”

    It's too late to change this for 1.7, but we're planning to address a bunch of Ajax issues sometime soon after 1.7. Pushing this back.

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

The Prototype JavaScript library.

Shared Ticket Bins

Referenced by

Pages