Bug 28819

Summary: Heap profiler: showing retainers
Product: WebKit Reporter: Mikhail Naganov <mnaganov>
Component: Tools / TestsAssignee: Nobody <webkit-unassigned>
Status: RESOLVED INVALID    
Severity: Normal CC: burg, joepeck, oliver, pfeldman, rik
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: All   
OS: All   
Attachments:
Description Flags
UI proposal
none
Update version of UI none

Description Mikhail Naganov 2009-08-28 14:16:15 PDT
Created attachment 38753 [details]
UI proposal

Hi Timothy,

I want to introduce another feature I want to add to heap profiler: an ability to show retainers for objects, that is, to trace paths from objects to GC roots. This helps a developer to understand, why a particular object can't be GCed.

A typical usage scenario is as follows:

- a developer looks at heap statistics and finds objects taking too much space, or not being collected, and she wonders why those objects are retained in heap;

- she invokes an action of showing object instances and retainers (e.g. by double clicking on a row in heap stats grid);

- a pane opens (if not already opened) showing a list of instances; every instance is presented as a row specifying the size of an instance (sizes may vary, e.g. for instances of Object or Array), rows are sorted in decreasing order (bigger instances first);

- every instance has an expansion triangle, by clicking on it all retainers of the particular instance are listed hierarchically, down to GC roots.

The exploration of instances and retainers is done dynamically on a "live" heap. It is actually not bound to a heap snapshot. An alternative approach would be to keep complete heap snapshots and make instances and retainers view per snapshot. But I'm against it, because as heap is mostly consist of object references, it means that data with sizes comparable to heap sizes (dozens of MB) will need to be transferred and stored in Inspector.

It means that objects and retainers pane isn't bound to heap snapshots (that's why the show / hide button isn't on view's statusbar), and snapshots are only serve for starting an inspection for a specified constructor.
Comment 1 Timothy Hatcher 2009-09-14 13:39:02 PDT
I like the idea of this and think it would be really useful. I am a little confused by the UI.

I think instead of double click, it should just be a normal selection and whatever is selected will show the retainers. Maybe a double click would still open the sidebar, but a row selection would still be in place to show where you came from.

Seeing the sizes in the tree with the retainers under it was confusing to me. It wasn't clear each size represented an instance of the bar object. I think we need a clearer way to show that (and have the size too.)

Here is what Instruments shows:

http://i607.photobucket.com/albums/tt158/steve5566/Picture3.png

Obvious they can show you a address for each object/malloc. So it is easy for them. With JS we don't have anything like that, that would be meaningful to the site developer…

Even just a "baz 1", "baz 2", "baz 3" would be fine. As long as the numbers were consistent between profiles, it would be really useful to see what objects still are alive, and what might be new (but happen to have the same size and an older object of the same class.)

What might make the UI clearer still is to not use a hierarchy in the sidebar, but split it vertically into two parts. Instances at the top and retainers at the bottom for the selected instance. But maybe that is too much, and once the sizes are not the top level items and it says "baz 1" it will be good as a hierarchy.
Comment 2 Mikhail Naganov 2009-09-16 03:04:16 PDT
Created attachment 39637 [details]
Update version of UI

I re-thinked about retainers, and experimented with real web apps. This radically changed my mind. I discovered that it is possible to put retainers graph into every snapshot in a reduced form. That is, retainments are tracked not for individual objects but rather for objects constructed with the same function. The only problem here is with anonymous Object and Array instances. For them, initially we consider every instance, but then we are aggregating them by similarity of their backreferences (in short, if two Object instances are held by instances of the same constructor, they are considered equivalent.)

Thus, we don't need an additional pane, and instead can show retainers naturally in the heap snapshot. To help a developer to prioritize back references, we should show how many object instances are retained for every backreference (e.g. objects of 'baz' constructor can be held by 'foo' and 'bar', of which 'foo' holds 90% of objects and 'bar' only 10%). I don't think we need to display the number of retained objects for next levels, because it's expensive to count them. Also I'm not sure whether we always could display deltas: it seems OK for named constructors, but for Objects and Arrays it's hard to find corresponding entries in two snapshots (we can't even use real memory addresses, because objects may get moved during GC.)
Comment 3 Timothy Hatcher 2009-10-03 16:25:06 PDT
(In reply to comment #2)
> Created an attachment (id=39637) [details]
> Update version of UI
> 
> I re-thinked about retainers, and experimented with real web apps. This
> radically changed my mind. I discovered that it is possible to put retainers
> graph into every snapshot in a reduced form. That is, retainments are tracked
> not for individual objects but rather for objects constructed with the same
> function. The only problem here is with anonymous Object and Array instances.
> For them, initially we consider every instance, but then we are aggregating
> them by similarity of their backreferences (in short, if two Object instances
> are held by instances of the same constructor, they are considered equivalent.)
> 
> Thus, we don't need an additional pane, and instead can show retainers
> naturally in the heap snapshot. To help a developer to prioritize back
> references, we should show how many object instances are retained for every
> backreference (e.g. objects of 'baz' constructor can be held by 'foo' and
> 'bar', of which 'foo' holds 90% of objects and 'bar' only 10%). I don't think
> we need to display the number of retained objects for next levels, because it's
> expensive to count them. Also I'm not sure whether we always could display
> deltas: it seems OK for named constructors, but for Objects and Arrays it's
> hard to find corresponding entries in two snapshots (we can't even use real
> memory addresses, because objects may get moved during GC.)

I like this approch! I assume "bar-retainer" is the name of the object (not you adding "-retainer" to it?
Comment 4 Mikhail Naganov 2009-10-05 02:31:33 PDT
(In reply to comment #3)
> (In reply to comment #2)
> > Created an attachment (id=39637) [details] [details]
> > Update version of UI
> > 
> > I re-thinked about retainers, and experimented with real web apps. This
> > radically changed my mind. I discovered that it is possible to put retainers
> > graph into every snapshot in a reduced form. That is, retainments are tracked
> > not for individual objects but rather for objects constructed with the same
> > function. The only problem here is with anonymous Object and Array instances.
> > For them, initially we consider every instance, but then we are aggregating
> > them by similarity of their backreferences (in short, if two Object instances
> > are held by instances of the same constructor, they are considered equivalent.)
> > 
> > Thus, we don't need an additional pane, and instead can show retainers
> > naturally in the heap snapshot. To help a developer to prioritize back
> > references, we should show how many object instances are retained for every
> > backreference (e.g. objects of 'baz' constructor can be held by 'foo' and
> > 'bar', of which 'foo' holds 90% of objects and 'bar' only 10%). I don't think
> > we need to display the number of retained objects for next levels, because it's
> > expensive to count them. Also I'm not sure whether we always could display
> > deltas: it seems OK for named constructors, but for Objects and Arrays it's
> > hard to find corresponding entries in two snapshots (we can't even use real
> > memory addresses, because objects may get moved during GC.)
> 
> I like this approch! I assume "bar-retainer" is the name of the object (not you
> adding "-retainer" to it?

Sure, I'm not modifying constructor names. I just called it so for demonstration purposes.