2012-12-03 2 views
3

У меня есть сценарий GreaseMonkey, который работает на сайте, который использует фреймы как неотъемлемую часть интерфейса. Этот скрипт утечки памяти, как сито, и я считаю, что это связано с моим использованием addEventListener в одном из фреймов. Проще говоря, я подключаю различные прослушиватели событий, затем перезагружает фрейм и присоединяет слушателей событий, а затем кадр перезагружается, вокруг и вокруг для сотен или, возможно, тысяч итераций, когда вы взаимодействуете с различными элементами в этом фрейме или другими. К концу этого времени Firefox перешел от ~ 300M памяти до 2G (или сработает до того, как он туда доберется).Ошибка утечки памяти AddEventListener из-за кадров

Я читал где-то, что полная перезагрузка страницы позволяет запускать программы сбора мусора FireFox и восстанавливать всю память из сиротских обработчиков событий и, конечно же, когда я нажимаю F5 после того, как мой скрипт работает некоторое время, в течение 10 секунд память вернется на 300M. К сожалению, это разбивает другой фрейм на сайте (очень популярное окно чата), поэтому, похоже, это подтверждает мое подозрение, что addEventListener виноват, но это не вариант в качестве решения.

Есть ли что-то еще, что я могу сделать, чтобы освободить память правильно, не заставляя обновление полной страницы?

(В настоящее время с помощью GM 1.5 и FF 17, но проблема существует с GM 0.8/FF 4 или около того.)

+0

вместо добавления ваших слушателей событий внутри кадра (каждый раз, когда каркасные перезагружается), просто добавьте слушателей раз в коде вне рамки. Определите функции внутри кадра, соответствующие каждому событию, которое вы хотите обработать, и каждый обработчик событий (вне кадра) вызывает соответствующую функцию внутри фрейма. – Lee

+0

@ Lee, вы бы хотели набросать, как будет выглядеть этот код? Также как я мог бы обрабатывать элементы, которые приходят и уходят изредка? По существу, панель представляет собой панель состояния, и по мере того как ваш статус изменяет появление новых элементов, к которым я затем присоединяю некоторые события. (Есть также постоянные элементы, и я определенно с нетерпением жду, чтобы попытаться ваш подход. :-)) – Hellion

+0

Ли подход не будет работать, если это перекрестный домен. ... Неясно, что 'addEventListener' является проблемой /, но, возможно, существует обходное решение jQuery, если вы открыты для jQuery. Что произойдет, если вы используете сайт в течение как минимум 30 минут с отключенными сценариями Greasemonkey? –

ответ

9

Не видя ваш полный сценарий, или Short, Self Contained, Compilable Example, мы не можем быть уверены, что продолжается. Может быть, addEventListener не проблема.

Вот некоторые стратегии для улучшения кода, с меньшим количеством утечек памяти:

  1. Встроенные/анонимные функции являются часто виновником, особенно с обработчиков событий.

    Плохо/Дырявый:

    elem.onclick = function() {/*do something*/}; 
    elem.addEventListener ("click", function() {/*do something*/}, false); 
    $("elem").click (function() {/*do something*/}); 
    

    Не дырявая, а также легче поддерживать:

    elem.onclick = clickHandler; 
    elem.addEventListener ("click", clickHandler, false); 
    $("elem").click (clickHandler); 
    
    function clickHandler (evt) { 
        /*do something*/ 
    } 
    

    Обратите внимание, что для userscripts вы должны avoid onclick, etc. anyway.

  2. Аналогично не использовать JS по атрибутам HTML. EG не используют <span onclick="callSomeFunction()"> и т. Д.

  3. Свести к минимуму код, который работает в iframe, только для того кода, который вы явно хотите.

    1. Используйте @include, @exclude и @match директивы, чтобы заблокировать так много нежелательных Iframes как можно скорее.
    2. Wrap all code that doesn't need to run in iframes in a block так:

      if (window.top === window.self) { 
          // Not in a frame 
      } 
      
  4. Не используйте innerHTML.

  5. Для большого количества элементов или элементов, которые приходят и уходят с AJAX, не используйте addEventListener() или JQuery-х .bind(), .click() и т.д.
    Это размножается слушателя через потенциально тысячи узлов.

    Использование jQuery's .on(). Таким образом, слушатель подключается только один раз и запускается соответствующим образом через пузырь. (Обратите внимание, что в некоторых редких иш случаях .on() может быть заблокирован JavaScript страницы.)

    В вашем случае, вы, вероятно, хотите что-то вроде:

    $(document).on ("click", "YOUR ELEM SELECTOR", clickHandler); 
    
    function clickHandler (evt) { 
        /*do something*/ 
    } 
    
  6. Чтобы избежать неожиданность циклических ссылок или потерянные элементы, используйте jQuery для добавления или удаления элементов, а не прямых методов DOM, таких как createElement(), appendChild() и т. д.
    jQuery спроектирован/протестирован, чтобы минимизировать такие вещи.

  7. Опасайтесь злоупотребления GM_setValue(). Он легко может использовать множество глобальных ресурсов или вызвать сбой экземпляра сценария.

    1. Для значений с одинаковой областью используйте localStorage.
    2. Не используйте GM_setValue() для хранения всего, кроме строк. Для чего-либо еще используйте сериализатор, например GM_SuperValue. Даже невинные целые числа могут привести к поломке по умолчанию GM_setValue().
    3. Вместо того, чтобы хранить много мелких переменных, может быть лучше обернуть их в объект и сохранить , что с одним из сериализаторов.


  8. Всегда проверяйте возвращаемые значения и предположим, что элементы могут быть пропущены:
    Это бедных (и, увы, типичная):

    $("selector").text($("selector").text().match(/foo=([bar]+)/)[1]); 
    

    лучше:

    var salesItemDiv = $("selector"); 
    var fooMatch  = salesItemDiv.text().match (/\bfoo\s*=\s*([bar]+)\b/i); 
    if (fooMatch && fooMatch.length > 1) { 
        salesItemDiv.text (fooMatch[1]); 
    } 
    

    , возможно, с последующим:

    salesItemDiv = fooMatch = null; 
    

    смотри ниже.

  9. Остерегайтесь рекурсивных/inline setTimeout() вызовов. Используйте setInterval() для повторного выбора времени. Как и с обработчиками событий, не используйте встроенные/анонимные функции.

  10. Запустите свой код через JSLint.

  11. Избегать использования eval() и auto/hidden eval() invocations.

  12. Установите переменные на null, когда вы закончите с ними.See this, for example.

  13. Ссылка: "Do you know what may cause memory leaks in JavaScript?"

  14. Additional reading on JS memory leaks

  15. Mozilla Performance: Leak Tools

+1

Gadzooks! Я не могу себе представить, что мой сценарий не улучшился бы значительно после того, как он выполнил бы только 1/4 приведенного совета. :-) (Я, конечно, знаю, что он страдает некоторыми из проблем, которые вы перечисляете.) Если у меня все еще есть проблемы с этим (и, к сожалению, это довольно бегемот), я собираю SSCCE, который вы запрашиваете, сделаю это отдельным вопросом. – Hellion

Смежные вопросы