Возможна утечка памяти JavaScript при обработке массивов?

Я использую Humble Finance, чтобы построить ряд данных по одной точке за раз для достижения эффект таймлапса. Мой код ниже, но я хотел бы сначала объяснить его:

Если вы не знакомы с HF, его функция инициализации принимает три массива JavaScript (priceData, volumeData и summaryData), которые затем наносятся на три графика. Каждый из этих массивов содержит несколько массивов пар XY.

В моей программе я объявляю три пустых массива «графиков» (priceData, volumeData и summaryData), затем получаю значения из базы данных и помещаю их в «основные» массивы JavaScript с массивами, называемыми priceDataOrig, volumeDataOrig и summaryDataOrig. Вместо того, чтобы передавать эти массивы в HF и отображать все данные сразу, я вызываю функцию UpdateGraph(), которая вызывает себя каждые 40 мс. UpdateGraph() помещает одно значение (фактически, массив, содержащий пару XY) за раз из мастер-массивов в соответствующие массивы графов, затем вызывает функцию инициализации HF (передавая ей массивы графов), рисуя три графа. После того, как 50 точек находятся в массивах графиков, я начинаю смещать самые старые точки, прежде чем добавлять новые, чтобы одновременно отображалось не более 50 точек на график.

Кроме того, я использую jQuery load() для загрузки графика, поэтому всякий раз, когда пользователь нажимает кнопку «Перейти», graph.php (который обрабатывает все, что описано выше) загружается в div на странице и начинает строить график по пунктам. . Идея состоит в том, что пользователь должен иметь возможность щелкнуть «Перейти» всякий раз, когда он хочет перезагрузить график и снова посмотреть, как истекает время.

Итак, теперь о моей проблеме: в моей тестовой программе я рисую всего около 500 значений, так что это ни в коем случае не большой набор данных. Когда Go нажимается в первый раз, все значения отображаются нормально. Однако использование памяти браузером резко возрастает (я пробовал и в Firefox, и в Chrome), и когда пользователь снова нажимает «Перейти», браузер полностью падает на полпути к графику. Я в полной растерянности, почему это происходит - я пытался обнулить все массивы после завершения построения графика и т.д.

У кого-нибудь есть какие-либо идеи? Вот мой код graph.php, слегка измененный для ясности:

<?php
    #Queries the DB and constructs the data strings assigned to JavaScript arrays below,
    #An example might be: $priceData = '[[0,100.34],[1,108.31],[2,109.40],[3,104.87]]';
?>

<script>
//Global variables used in UpdateGraph():

//The "master" arrays -- I'm just using the same data for all of
//them for now (an array containing around 500 arrays of XY pairs)
var priceDataOrig = <?php echo $priceData; ?>;
var volumeDataOrig = <?php echo $priceData; ?>;
var summaryDataOrig = <?php echo $priceData; ?>;

var priceData = [],
    volumeData = [],
    jsonData = [];
    i = 0,
    pointsToShowAtOnce = 50,
    totalPoints = priceDataOrig.length,
    updateRate = 40;    //milliseconds

UpdateGraph();

//Periodically updates the graph to show time lapse, adding one new point at a time
function UpdateGraph() {
    //Only add a new point if all the points haven't already been added
    if (i != totalPoints) {
        //Add a new point to each array
        priceData.push(priceDataOrig[i]);
        volumeData.push(volumeDataOrig[i]);
        summaryData.push(jsonDataOrig[i]);

        if (i >= pointsToShowAtOnce) {
            //We're showing the amount of points we want to show, so remove the oldest point
            priceData.shift();
            volumeData.shift();
            jsonData.shift();

            //Sets the new X values for these points -- not really pertinent to
            //my question, it works the same if this block is commented out.
            var pLen = priceData.length;
            var j, c = 0;
            for (j = 0; j < pLen; j++) {
                priceData[j][0] = c;
                volumeData[j][0] = c;
                c++;
            }
        }

        //Load the graph itself. 'humblefinance' is just a div on the page.
        HumbleFinance.init('humblefinance', priceData, volumeData, summaryData);

        i++;

        //This function calls itself at the interval in
        //updateRate until all the points have been drawn
        setTimeout('UpdateGraph()', updateRate);
    } else {
        //I null these out here even though it doesn't seem necessary.
        //It doesn't help though.
        priceDataOrig = null;
        volumeDataOrig = null;
        summaryData = null;
        jsonDataOrig = null;
        priceData = null;
        volumeData = null;
        jsonData = null;
    }
}
</script>

<div id="humblefinance"></div>

person user428517    schedule 22.06.2011    source источник
comment
я не уверен, но это может произойти, поскольку setTimeout('UpdateGraph()', updateRate); вызывается изнутри функции. если я не ошибаюсь (и я могу быть XD), JS не будет очищать память, пока используется функция. вы должны установить тайм-аут при загрузке страницы, а не внутри функции.   -  person Rafael Herscovici    schedule 22.06.2011
comment
Насколько я знаю, нет способа гарантировать, что функция вызывается каждые 40 мс, если только она не вызывает себя. Проблема, с которой я сталкиваюсь, возникает после многократного запуска всего промежутка времени, каждый раз перезагружая скрипт в div. Таким образом, кажется, что JS каким-то образом продолжает использовать память даже после перезагрузки graph.php.   -  person user428517    schedule 22.06.2011
comment
я согласен с тем, что нет способа гарантировать, что функция вызывается каждые 40 мс, более медленные компьютеры будут занимать больше времени (поскольку js является скриптом на стороне клиента). но если вы запустите setTimeout вне функции, она все равно будет пытаться сделать это с указанными интервалами. если вы говорите, что JS продолжает работать ПОСЛЕ перезагрузки страницы (что на самом деле невозможно), у вас может быть проблема на работающем компьютере, а не в функции.   -  person Rafael Herscovici    schedule 23.06.2011


Ответы (2)


Попробуйте внести небольшое изменение в HumbleFinance. В методе init эти функции вызываются:

this.buildDOM();
this.attachEventObservers();

Похоже, это одноразовые функции конфигурации, которые вы хотели бы запускать только при первом обновлении графа. Кажется, что было создано много элементов и связанных событий, которые будут выполняться снова и снова, каждый раз потребляя все больше памяти, поскольку они никогда не освобождаются.

Проще всего было бы добавить параметр к init, чтобы вы могли условно исключить эту часть или, альтернативно, выделить код, который фактически рисует график, как отдельный метод. В конце концов, я думаю, вы хотите, чтобы эти процедуры настройки вызывались только один раз.

person Jamie Treworgy    schedule 22.06.2011
comment
Большое спасибо, jamietre, похоже, это исправлено - мне нужно было более внимательно изучить эту часть библиотеки. - person user428517; 23.06.2011

2 идеи.

1 - Отладчики иногда вызывают утечку памяти. Это происходит при выключенном firebug/devtools?

2. Бегло взглянув на код HumbleFinance.js, похоже, что графики добавляются к контейнеру, а не заменяют то, что там есть. Что-то вроде этого может помочь перед вызовом init:

$('#humbledata')[0].innerHTML = ''

Или, как бы там ни было, jQuery любит, чтобы вы это делали.

person Tamzin Blake    schedule 22.06.2011