programing tip

Chrome으로 자바 스크립트 메모리 누수 찾기

itbloger 2020. 6. 4. 19:16
반응형

Chrome으로 자바 스크립트 메모리 누수 찾기


백본 뷰를 만들고 처리기를 이벤트에 연결하고 사용자 정의 클래스를 인스턴스화하는 매우 간단한 테스트 사례를 만들었습니다. 이 샘플에서 "제거"버튼을 클릭하면 모든 것이 정리되고 메모리 누수가 없어야합니다.

코드에 대한 jsfiddle은 다음과 같습니다. http://jsfiddle.net/4QhR2/

// scope everything to a function
function main() {

    function MyWrapper() {
        this.element = null;
    }
    MyWrapper.prototype.set = function(elem) {
        this.element = elem;
    }
    MyWrapper.prototype.get = function() {
        return this.element;
    }

    var MyView = Backbone.View.extend({
        tagName : "div",
        id : "view",
        events : {
            "click #button" : "onButton",
        },    
        initialize : function(options) {        
            // done for demo purposes only, should be using templates
            this.html_text = "<input type='text' id='textbox' /><button id='button'>Remove</button>";        
            this.listenTo(this,"all",function(){console.log("Event: "+arguments[0]);});
        },
        render : function() {        
            this.$el.html(this.html_text);

            this.wrapper = new MyWrapper();
            this.wrapper.set(this.$("#textbox"));
            this.wrapper.get().val("placeholder");

            return this;
        },
        onButton : function() {
            // assume this gets .remove() called on subviews (if they existed)
            this.trigger("cleanup");
            this.remove();
        }
    });

    var view = new MyView();
    $("#content").append(view.render().el);
}

main();

그러나 Chrome 프로필러를 사용하여 이것이 사실인지 확인하는 방법을 잘 모르겠습니다. 힙 프로파일 러 스냅 샷에 표시되는 내용이 많으며, 무엇이 좋은지 나쁜지를 해독하는 방법을 모릅니다. 지금까지 살펴본 튜토리얼은 "스냅 샷 프로파일 러 사용"을 지시하거나 전체 프로파일 러의 작동 방식에 대해 매우 자세하게 설명합니다. 프로파일 러를 도구로 사용하는 것이 가능합니까? 아니면 전체가 어떻게 설계되었는지 이해해야합니까?

편집 : 다음과 같은 자습서 :

Gmail 메모리 누수 수정

DevTools 사용

Are representative of some of the stronger material out there, from what I've seen. However, beyond introducing the concept of the 3 Snapshot Technique, I find they offer very little in terms of practical knowledge (for a beginner like me). The 'Using DevTools' tutorial doesn't work through a real example, so its vague and general conceptual description of things aren't overly helpful. As for the 'Gmail' example:

So you found a leak. Now what?

  • Examine the retaining path of leaked objects in the lower half of the Profiles panel

  • If the allocation site cannot be easily inferred (i.e. event listeners):

  • Instrument the constructor of the retaining object via the JS console to save the stack trace for allocations

  • Using Closure? Enable the appropriate existing flag (i.e. goog.events.Listener.ENABLE_MONITORING) to set the creationStack property during construction

I find myself more confused after reading that, not less. And, again, it's just telling me to do things, not how to do them. From my perspective, all of the information out there is either too vague or would only make sense to someone who already understood the process.

Some of these more specific issues have been raised in @Jonathan Naguin's answer below.


A good workflow to find memory leaks is the three snapshot technique, first used by Loreena Lee and the Gmail team to solve some of their memory problems. The steps are, in general:

  • Take a heap snapshot.
  • Do stuff.
  • Take another heap snapshot.
  • Repeat the same stuff.
  • Take another heap snapshot.
  • Filter objects allocated between Snapshots 1 and 2 in Snapshot 3's "Summary" view.

For your example, I have adapted the code to show this process (you can find it here) delaying the creation of the Backbone View until the click event of the Start button. Now:

  • Run the HTML (saved locally of using this address) and take a snapshot.
  • Click Start to create the view.
  • Take another snapshot.
  • Click remove.
  • Take another snapshot.
  • Filter objects allocated between Snapshots 1 and 2 in Snapshot 3's "Summary" view.

Now you are ready to find memory leaks!

You will notice nodes of a few different colors. Red nodes do not have direct references from Javascript to them, but are alive because they are part of a detached DOM tree. There may be a node in the tree referenced from Javascript (maybe as a closure or variable) but is coincidentally preventing the entire DOM tree from being garbage collected.

enter image description here

Yellow nodes however do have direct references from Javascript. Look for yellow nodes in the same detached DOM tree to locate references from your Javascript. There should be a chain of properties leading from the DOM window to the element.

In your particular you can see a HTML Div element marked as red. If you expand the element you will see that is referenced by a "cache" function.

enter image description here

Select the row and in your console type $0, you will see the actual function and location:

>$0
function cache( key, value ) {
        // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
        if ( keys.push( key += " " ) > Expr.cacheLength ) {
            // Only keep the most recent entries
            delete cache[ keys.shift() ];
        }
        return (cache[ key ] = value);
    }                                                     jquery-2.0.2.js:1166

This is where your element is being referenced. Unfortunally there is not much you can do, it is a internal mechanism from jQuery. But, just for testing purpose, go the function and change the method to:

function cache( key, value ) {
    return value;
}

Now if you repeat the process you will not see any red node :)

Documentation:


Here's a tip on memory profiling of a jsfiddle: Use the following URL to isolate your jsfiddle result, it removes all of the jsfiddle framework and loads only your result.

http://jsfiddle.net/4QhR2/show/

I was never able to figure out how to use the Timeline and Profiler to track down memory leaks, until I read the following documentation. After reading the section entitled 'Object allocation tracker' I was able to use the 'Record Heap Allocations' tool, and track some some Detached DOM nodes.

I fixed the problem by switching from jQuery event binding, to using Backbone event delegation. It's my understanding that newer versions of Backbone will automatically unbind the events for you if you call View.remove(). Execute some of the demos yourself, they are set up with memory leaks for you to identify. Feel free to ask questions here if you still don't get it after studying this documentation.

https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling


Basically you need to look at the number of objects inside your heap snapshot. If the number of objects increases between two snapshots and you've disposed of objects then you have a memory leak. My advice is to look for event handlers in your code which do not get detached.


There is an introduction video from Google, which will be very helpful to find JavaScript memory leaks.

https://www.youtube.com/watch?v=L3ugr9BJqIs


You could also look at the Timeline tab in developer tools. Record the usage of your app and keep an eye on the DOM Node and Event listener count.

If the memory graph would indeed indicate a memory leak, then you can use the profiler to figure out what is leaking.


You also might want to read :

http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/

It explains the use of the chrome developer tools and gives some step-by-step advices on how to confirm and locate a memory leak using heap snapshot comparison and the different hep snapshot views available.


I second the advice to take a heap snapshot, they're excellent for detecting memory leaks, chrome does an excellent job of snapshotting.

In my research project for my degree I was building an interactive web application that had to generate a lot of data built up in 'layers', many of these layers would be 'deleted' in the UI but for some reason the memory wasn't being deallocated, using the snapshot tool I was able to determine that JQuery had been keeping a reference on the object (the source was when I was trying to trigger a .load() event which kept the reference despite going out of scope). Having this information at hand single-handedly saved my project, it's a highly useful tool when you're using other people's libraries and you have this issue of lingering references stopping the GC from doing its job.

EDIT: It's also useful to plan ahead what actions you're going to perform to minimize time spent snapshotting, hypothesize what could be causing the problem and test each scenario out, making snapshots before and after.

참고URL : https://stackoverflow.com/questions/19621074/finding-javascript-memory-leaks-with-chrome

반응형