最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to find a particular object in DevTools memory snapshot if the type of the object is just "Object&

programmeradmin4浏览0评论

queryObjects function returns instances of a given prototype. I want to inspect retainers of those instances. To do that I need to find them in devtools memory snapshot. One of those instances type is "Object", so searching by class type isn't helpful. They are just too many of them.

How can I find that particular instance in a snapshot?

queryObjects function returns instances of a given prototype. I want to inspect retainers of those instances. To do that I need to find them in devtools memory snapshot. One of those instances type is "Object", so searching by class type isn't helpful. They are just too many of them.

How can I find that particular instance in a snapshot?

Share Improve this question asked May 26, 2019 at 8:14 szymszym 3,1982 gold badges24 silver badges32 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 5

It would be very hard to distinct the plain "Objects" returned by queryObjects from all of the plain Objects in the heap snapshot

Here's what I've done to "tag" the objects so I can distinct them easily:

  1. Create a TaggedItem class that can be easily found in dev tools
  2. Create a wrapper function to be called in place of queryObjects
  3. The wrapper functions saves return values inside a WeakMap
  • the key is the original return value
  • the value is a TaggedItem
  • when the original value is no longer retained anywhere - it's automatically garbage collected from the WeakMap
Applying a decorator on queryObjects
function queryObjects { /* Implementation */ }

// or process.env.NODE_ENV == 'development'
if (__DEV__) {
  export default wrapWithObjectTagging(queryObjects);
}
else {
  export default queryObjects;
}
Decorator implementation
function wrapWithObjectTagging(func) {
  function withObjectTagging(...args) {
     const originalReturnValue = func.apply(this, args);

     /* We're using Promise.resolve in case the original function returns a promise */
     Promise.resolve(originalReturnValue)
       .then((data) => {
         // Weak map keys can be objects only
         if (typeof data == 'object') {
           const tag = new TaggedItem(data);
           taggedItems.set(data, tag);
         }
     });

     return originalReturnValue;
  }

  return withObjectTagging;
}

/**
 * Capture tags in a weak map
 * When the original object (key) is no longer used anywhere in app - the data (TaggedItem)
 * is free for garbage collection and removed
 * Using dev tools we see which TaggedItems are still retained and what's using them - preventing GC
 * @type {WeakMap<object, TaggedItem>}
 */
const taggedItems = new WeakMap();

/**
 * Class instances are easily tracked by Dev tools memory snapshots
 * Wrapping a result with such instance allows us to track it without
 * incurring extra weight
 */
class TaggedItem {
  constructor(data) {
    // This ref helps us inspect what is retaining the original data
    this.ref = data;

    /* This is not really necessary but makes the TaggedItem represent 
       Retained Memory more accurately */
    Object.assign(this, data);    
  }
}

The TaggedItem is referencing the original data directly, but since nothing is referencing the TaggedItem - we never expose it - it does not prevent the WeakMap from garbage collecting the original data and the TaggedItem that captured it


Here is a sample of how that works in Chrome Dev Tools:

  1. Take a Heap Snapshot
  2. Search for the object class name (TaggedItem)
  3. Select an item
  4. See retainers
Example

  • We're observing a TaggedItem
  • It's retained by the report property of another object, that uses the original data

A simpler approach is to append a key to the original object

Decorating by appending a custom field
function wrapWithObjectTagging(func) {
  function withObjectTagging(...args) {
     const originalReturnValue = func.apply(this, args);
     if (typeof originalReturnValue == 'object') {
       originalReturnValue.__tag = new TaggedItem();
     }

     return originalReturnValue;
  }

  return withObjectTagging;
}

class TaggedItem {}

This way you can still filter by TaggedItem in dev tools and see what's retaining the TaggedItem and in turn the parent object

The first approach gives a better estimate of total retained memory, while the simple approach would just allow you to trace what's retaining the original

Add explicit string property e.g. let o = {type:'myType'} and then use ctrl+F in snapshot.

There retainers are all your o objects.

If it's ok to modify these objects you can try the following trick.

Store the array you got from the queryObjects into a temp variable. You can do it via the context menu. Then run the following code to give each object a unique id:

temp1.forEach((t, i) => t.__id = String(i))

Then take a heap snapshot. Now each object should be identifiable by that __id string.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论