Bug 1577825 - Refactor zone memory tracker to allow tracking non-GC thing pointers apart from ZoneAllocPolicy r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Tue, 03 Dec 2019 13:26:02 +0000
changeset 505297 f5d3a765322802e3259faff377a5d5ccd88cc309
parent 505296 e8463f077741d3bf4145f67ac7a0c762c72867ca
child 505298 eff36ae1a58ba617617b6bbb0593126c5bedb98a
push id36881
push userdvarga@mozilla.com
push dateWed, 04 Dec 2019 16:22:31 +0000
treeherdermozilla-central@13fb375eaf14 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1577825
milestone73.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1577825 - Refactor zone memory tracker to allow tracking non-GC thing pointers apart from ZoneAllocPolicy r=sfink This makes the hash table used for tracking ZoneAllocPolicy generic and able to track memory owned by other non-GC things. The idea is to use this for tracking the memory associated with shared array buffers in the next patch. Differential Revision: https://phabricator.services.mozilla.com/D55533
js/src/gc/GCEnum.h
js/src/gc/Scheduling.cpp
js/src/gc/Scheduling.h
js/src/gc/Zone.cpp
js/src/gc/ZoneAllocator.h
--- a/js/src/gc/GCEnum.h
+++ b/js/src/gc/GCEnum.h
@@ -135,17 +135,18 @@ enum class ZealMode {
   _(DebuggerFrameGeneratorInfo)            \
   _(DebuggerFrameIterData)                 \
   _(DebuggerOnStepHandler)                 \
   _(DebuggerOnPopHandler)                  \
   _(RealmInstrumentation)                  \
   _(ICUObject)                             \
   _(FinalizationGroupHoldingsVector)       \
   _(FinalizationGroupRegistrations)        \
-  _(FinalizationRecordVector)
+  _(FinalizationRecordVector)              \
+  _(ZoneAllocPolicy)
 
 #define JS_FOR_EACH_MEMORY_USE(_)  \
   JS_FOR_EACH_PUBLIC_MEMORY_USE(_) \
   JS_FOR_EACH_INTERNAL_MEMORY_USE(_)
 
 enum class MemoryUse : uint8_t {
 #define DEFINE_MEMORY_USE(Name) Name,
   JS_FOR_EACH_MEMORY_USE(DEFINE_MEMORY_USE)
--- a/js/src/gc/Scheduling.cpp
+++ b/js/src/gc/Scheduling.cpp
@@ -449,32 +449,35 @@ void MallocHeapThreshold::updateAfterGC(
 
 #ifdef DEBUG
 
 void MemoryTracker::adopt(MemoryTracker& other) {
   LockGuard<Mutex> lock(mutex);
 
   AutoEnterOOMUnsafeRegion oomUnsafe;
 
-  for (auto r = other.map.all(); !r.empty(); r.popFront()) {
-    if (!map.put(r.front().key(), r.front().value())) {
+  for (auto r = other.gcMap.all(); !r.empty(); r.popFront()) {
+    if (!gcMap.put(r.front().key(), r.front().value())) {
       oomUnsafe.crash("MemoryTracker::adopt");
     }
   }
-  other.map.clear();
+  other.gcMap.clear();
 
   // There may still be ZoneAllocPolicies associated with the old zone since
   // some are not destroyed until the zone itself dies. Instead check there is
   // no memory associated with them and clear their zone pointer in debug builds
   // to catch further memory association.
-  for (auto r = other.policyMap.all(); !r.empty(); r.popFront()) {
+  for (auto r = other.nonGCMap.all(); !r.empty(); r.popFront()) {
     MOZ_ASSERT(r.front().value() == 0);
-    r.front().key()->zone_ = nullptr;
+    if (r.front().key().use() == MemoryUse::ZoneAllocPolicy) {
+      auto policy = static_cast<ZoneAllocPolicy*>(r.front().key().ptr());
+      policy->zone_ = nullptr;
+    }
   }
-  other.policyMap.clear();
+  other.nonGCMap.clear();
 }
 
 static const char* MemoryUseName(MemoryUse use) {
   switch (use) {
 #  define DEFINE_CASE(Name) \
     case MemoryUse::Name:   \
       return #Name;
     JS_FOR_EACH_MEMORY_USE(DEFINE_CASE)
@@ -484,248 +487,285 @@ static const char* MemoryUseName(MemoryU
   MOZ_CRASH("Unknown memory use");
 }
 
 MemoryTracker::MemoryTracker() : mutex(mutexid::MemoryTracker) {}
 
 void MemoryTracker::checkEmptyOnDestroy() {
   bool ok = true;
 
-  if (!map.empty()) {
+  if (!gcMap.empty()) {
     ok = false;
     fprintf(stderr, "Missing calls to JS::RemoveAssociatedMemory:\n");
-    for (auto r = map.all(); !r.empty(); r.popFront()) {
-      fprintf(stderr, "  %p 0x%zx %s\n", r.front().key().cell(),
+    for (auto r = gcMap.all(); !r.empty(); r.popFront()) {
+      fprintf(stderr, "  %p 0x%zx %s\n", r.front().key().ptr(),
               r.front().value(), MemoryUseName(r.front().key().use()));
     }
   }
 
-  if (!policyMap.empty()) {
+  if (!nonGCMap.empty()) {
     ok = false;
-    fprintf(stderr, "Missing calls to Zone::decPolicyMemory:\n");
-    for (auto r = policyMap.all(); !r.empty(); r.popFront()) {
-      fprintf(stderr, "  %p 0x%zx\n", r.front().key(), r.front().value());
+    fprintf(stderr, "Missing calls to Zone::decNonGCMemory:\n");
+    for (auto r = nonGCMap.all(); !r.empty(); r.popFront()) {
+      fprintf(stderr, "  %p 0x%zx\n", r.front().key().ptr(), r.front().value());
     }
   }
 
   MOZ_ASSERT(ok);
 }
 
-inline bool MemoryTracker::allowMultipleAssociations(MemoryUse use) const {
+/* static */
+inline bool MemoryTracker::isGCMemoryUse(MemoryUse use) {
+  // Most memory uses are for memory associated with GC things but some are for
+  // memory associated with non-GC thing pointers.
+  return !isNonGCMemoryUse(use);
+}
+
+/* static */
+inline bool MemoryTracker::isNonGCMemoryUse(MemoryUse use) {
+  return use == MemoryUse::ZoneAllocPolicy;
+}
+
+/* static */
+inline bool MemoryTracker::allowMultipleAssociations(MemoryUse use) {
   // For most uses only one association is possible for each GC thing. Allow a
   // one-to-many relationship only where necessary.
-  return use == MemoryUse::RegExpSharedBytecode ||
+  return isNonGCMemoryUse(use) || use == MemoryUse::RegExpSharedBytecode ||
          use == MemoryUse::BreakpointSite || use == MemoryUse::Breakpoint ||
          use == MemoryUse::ForOfPICStub || use == MemoryUse::ICUObject;
 }
 
-void MemoryTracker::trackMemory(Cell* cell, size_t nbytes, MemoryUse use) {
+void MemoryTracker::trackGCMemory(Cell* cell, size_t nbytes, MemoryUse use) {
   MOZ_ASSERT(cell->isTenured());
+  MOZ_ASSERT(isGCMemoryUse(use));
 
   LockGuard<Mutex> lock(mutex);
 
-  Key key{cell, use};
+  Key<Cell> key{cell, use};
   AutoEnterOOMUnsafeRegion oomUnsafe;
-  auto ptr = map.lookupForAdd(key);
+  auto ptr = gcMap.lookupForAdd(key);
   if (ptr) {
     if (!allowMultipleAssociations(use)) {
       MOZ_CRASH_UNSAFE_PRINTF("Association already present: %p 0x%zx %s", cell,
                               nbytes, MemoryUseName(use));
     }
     ptr->value() += nbytes;
     return;
   }
 
-  if (!map.add(ptr, key, nbytes)) {
-    oomUnsafe.crash("MemoryTracker::noteExternalAlloc");
+  if (!gcMap.add(ptr, key, nbytes)) {
+    oomUnsafe.crash("MemoryTracker::trackGCMemory");
   }
 }
 
-void MemoryTracker::untrackMemory(Cell* cell, size_t nbytes, MemoryUse use) {
+void MemoryTracker::untrackGCMemory(Cell* cell, size_t nbytes, MemoryUse use) {
   MOZ_ASSERT(cell->isTenured());
 
   LockGuard<Mutex> lock(mutex);
 
-  Key key{cell, use};
-  auto ptr = map.lookup(key);
+  Key<Cell> key{cell, use};
+  auto ptr = gcMap.lookup(key);
   if (!ptr) {
     MOZ_CRASH_UNSAFE_PRINTF("Association not found: %p 0x%zx %s", cell, nbytes,
                             MemoryUseName(use));
   }
 
   if (!allowMultipleAssociations(use) && ptr->value() != nbytes) {
     MOZ_CRASH_UNSAFE_PRINTF(
         "Association for %p %s has different size: "
         "expected 0x%zx but got 0x%zx",
         cell, MemoryUseName(use), ptr->value(), nbytes);
   }
 
-  if (ptr->value() < nbytes) {
+  if (nbytes > ptr->value()) {
     MOZ_CRASH_UNSAFE_PRINTF(
-        "Association for %p %s size is too small: "
-        "expected at least 0x%zx but got 0x%zx",
-        cell, MemoryUseName(use), nbytes, ptr->value());
+        "Association for %p %s size is too large: "
+        "expected at most 0x%zx but got 0x%zx",
+        cell, MemoryUseName(use), ptr->value(), nbytes);
   }
 
   ptr->value() -= nbytes;
 
   if (ptr->value() == 0) {
-    map.remove(ptr);
+    gcMap.remove(ptr);
   }
 }
 
-void MemoryTracker::swapMemory(Cell* a, Cell* b, MemoryUse use) {
+void MemoryTracker::swapGCMemory(Cell* a, Cell* b, MemoryUse use) {
   MOZ_ASSERT(a->isTenured());
   MOZ_ASSERT(b->isTenured());
 
-  Key ka{a, use};
-  Key kb{b, use};
+  Key<Cell> ka{a, use};
+  Key<Cell> kb{b, use};
 
   LockGuard<Mutex> lock(mutex);
 
   size_t sa = getAndRemoveEntry(ka, lock);
   size_t sb = getAndRemoveEntry(kb, lock);
 
   AutoEnterOOMUnsafeRegion oomUnsafe;
 
-  if ((sa && !map.put(kb, sa)) || (sb && !map.put(ka, sb))) {
-    oomUnsafe.crash("MemoryTracker::swapTrackedMemory");
+  if ((sa && !gcMap.put(kb, sa)) || (sb && !gcMap.put(ka, sb))) {
+    oomUnsafe.crash("MemoryTracker::swapGCMemory");
   }
 }
 
-size_t MemoryTracker::getAndRemoveEntry(const Key& key,
+size_t MemoryTracker::getAndRemoveEntry(const Key<Cell>& key,
                                         LockGuard<Mutex>& lock) {
-  auto ptr = map.lookup(key);
+  auto ptr = gcMap.lookup(key);
   if (!ptr) {
     return 0;
   }
 
   size_t size = ptr->value();
-  map.remove(ptr);
+  gcMap.remove(ptr);
   return size;
 }
 
-void MemoryTracker::registerPolicy(ZoneAllocPolicy* policy) {
+void MemoryTracker::registerNonGCMemory(void* mem, MemoryUse use) {
   LockGuard<Mutex> lock(mutex);
 
-  auto ptr = policyMap.lookupForAdd(policy);
+  Key<void> key{mem, use};
+  auto ptr = nonGCMap.lookupForAdd(key);
   if (ptr) {
-    MOZ_CRASH_UNSAFE_PRINTF("ZoneAllocPolicy %p already registered", policy);
+    MOZ_CRASH_UNSAFE_PRINTF("%s assocaition %p already registered",
+                            MemoryUseName(use), mem);
   }
 
   AutoEnterOOMUnsafeRegion oomUnsafe;
-  if (!policyMap.add(ptr, policy, 0)) {
-    oomUnsafe.crash("MemoryTracker::registerPolicy");
+  if (!nonGCMap.add(ptr, key, 0)) {
+    oomUnsafe.crash("MemoryTracker::registerNonGCMemory");
   }
 }
 
-void MemoryTracker::unregisterPolicy(ZoneAllocPolicy* policy) {
+void MemoryTracker::unregisterNonGCMemory(void* mem, MemoryUse use) {
   LockGuard<Mutex> lock(mutex);
 
-  auto ptr = policyMap.lookup(policy);
+  Key<void> key{mem, use};
+  auto ptr = nonGCMap.lookup(key);
   if (!ptr) {
-    MOZ_CRASH_UNSAFE_PRINTF("ZoneAllocPolicy %p not found", policy);
+    MOZ_CRASH_UNSAFE_PRINTF("%s association %p not found", MemoryUseName(use),
+                            mem);
   }
+
   if (ptr->value() != 0) {
     MOZ_CRASH_UNSAFE_PRINTF(
-        "ZoneAllocPolicy %p still has 0x%zx bytes associated", policy,
-        ptr->value());
+        "%s association %p still has 0x%zx bytes associated",
+        MemoryUseName(use), mem, ptr->value());
   }
 
-  policyMap.remove(ptr);
+  nonGCMap.remove(ptr);
 }
 
-void MemoryTracker::movePolicy(ZoneAllocPolicy* dst, ZoneAllocPolicy* src) {
+void MemoryTracker::moveNonGCMemory(void* dst, void* src, MemoryUse use) {
   LockGuard<Mutex> lock(mutex);
 
-  auto srcPtr = policyMap.lookup(src);
+  Key<void> srcKey{src, use};
+  auto srcPtr = nonGCMap.lookup(srcKey);
   if (!srcPtr) {
-    MOZ_CRASH_UNSAFE_PRINTF("ZoneAllocPolicy %p not found", src);
+    MOZ_CRASH_UNSAFE_PRINTF("%s association %p not found", MemoryUseName(use),
+                            src);
   }
 
   size_t nbytes = srcPtr->value();
-  policyMap.remove(srcPtr);
+  nonGCMap.remove(srcPtr);
 
-  auto dstPtr = policyMap.lookupForAdd(dst);
+  Key<void> dstKey{dst, use};
+  auto dstPtr = nonGCMap.lookupForAdd(dstKey);
   if (dstPtr) {
-    MOZ_CRASH_UNSAFE_PRINTF("ZoneAllocPolicy %p already registered", dst);
+    MOZ_CRASH_UNSAFE_PRINTF("%s %p already registered", MemoryUseName(use),
+                            dst);
   }
 
   AutoEnterOOMUnsafeRegion oomUnsafe;
-  if (!policyMap.add(dstPtr, dst, nbytes)) {
-    oomUnsafe.crash("MemoryTracker::movePolicy");
+  if (!nonGCMap.add(dstPtr, dstKey, nbytes)) {
+    oomUnsafe.crash("MemoryTracker::moveNonGCMemory");
   }
 }
 
-void MemoryTracker::incPolicyMemory(ZoneAllocPolicy* policy, size_t nbytes) {
+void MemoryTracker::incNonGCMemory(void* mem, size_t nbytes, MemoryUse use) {
+  MOZ_ASSERT(isNonGCMemoryUse(use));
+
   LockGuard<Mutex> lock(mutex);
 
-  auto ptr = policyMap.lookup(policy);
+  Key<void> key{mem, use};
+  auto ptr = nonGCMap.lookup(key);
   if (!ptr) {
-    MOZ_CRASH_UNSAFE_PRINTF("ZoneAllocPolicy %p not found", policy);
+    MOZ_CRASH_UNSAFE_PRINTF("%s allocation %p not found", MemoryUseName(use),
+                            mem);
   }
 
   ptr->value() += nbytes;
 }
 
-void MemoryTracker::decPolicyMemory(ZoneAllocPolicy* policy, size_t nbytes) {
+void MemoryTracker::decNonGCMemory(void* mem, size_t nbytes, MemoryUse use) {
+  MOZ_ASSERT(isNonGCMemoryUse(use));
+
   LockGuard<Mutex> lock(mutex);
 
-  auto ptr = policyMap.lookup(policy);
+  Key<void> key{mem, use};
+  auto ptr = nonGCMap.lookup(key);
   if (!ptr) {
-    MOZ_CRASH_UNSAFE_PRINTF("ZoneAllocPolicy %p not found", policy);
+    MOZ_CRASH_UNSAFE_PRINTF("%s allocation %p not found", MemoryUseName(use),
+                            mem);
   }
 
   size_t& value = ptr->value();
-  if (value < nbytes) {
+  if (nbytes > value) {
     MOZ_CRASH_UNSAFE_PRINTF(
-        "ZoneAllocPolicy %p is too small: "
-        "expected at least 0x%zx but got 0x%zx bytes",
-        policy, nbytes, value);
+        "%s allocation %p is too large: "
+        "expected at most 0x%zx but got 0x%zx bytes",
+        MemoryUseName(use), mem, value, nbytes);
   }
 
   value -= nbytes;
 }
 
 void MemoryTracker::fixupAfterMovingGC() {
   // Update the table after we move GC things. We don't use MovableCellHasher
   // because that would create a difference between debug and release builds.
-  for (Map::Enum e(map); !e.empty(); e.popFront()) {
-    const Key& key = e.front().key();
-    Cell* cell = key.cell();
+  for (GCMap::Enum e(gcMap); !e.empty(); e.popFront()) {
+    const auto& key = e.front().key();
+    Cell* cell = key.ptr();
     if (cell->isForwarded()) {
       cell = gc::RelocationOverlay::fromCell(cell)->forwardingAddress();
-      e.rekeyFront(Key{cell, key.use()});
+      e.rekeyFront(Key<Cell>{cell, key.use()});
     }
   }
 }
 
-inline MemoryTracker::Key::Key(Cell* cell, MemoryUse use)
-    : cell_(uint64_t(cell)), use_(uint64_t(use)) {
+template <typename Ptr>
+inline MemoryTracker::Key<Ptr>::Key(Ptr* ptr, MemoryUse use)
+    : ptr_(uint64_t(ptr)), use_(uint64_t(use)) {
 #  ifdef JS_64BIT
   static_assert(sizeof(Key) == 8,
                 "MemoryTracker::Key should be packed into 8 bytes");
 #  endif
-  MOZ_ASSERT(this->cell() == cell);
+  MOZ_ASSERT(this->ptr() == ptr);
   MOZ_ASSERT(this->use() == use);
 }
 
-inline Cell* MemoryTracker::Key::cell() const {
-  return reinterpret_cast<Cell*>(cell_);
+template <typename Ptr>
+inline Ptr* MemoryTracker::Key<Ptr>::ptr() const {
+  return reinterpret_cast<Ptr*>(ptr_);
 }
-inline MemoryUse MemoryTracker::Key::use() const {
+template <typename Ptr>
+inline MemoryUse MemoryTracker::Key<Ptr>::use() const {
   return static_cast<MemoryUse>(use_);
 }
 
-inline HashNumber MemoryTracker::Hasher::hash(const Lookup& l) {
-  return mozilla::HashGeneric(DefaultHasher<Cell*>::hash(l.cell()),
+template <typename Ptr>
+inline HashNumber MemoryTracker::Hasher<Ptr>::hash(const Lookup& l) {
+  return mozilla::HashGeneric(DefaultHasher<Ptr*>::hash(l.ptr()),
                               DefaultHasher<unsigned>::hash(unsigned(l.use())));
 }
 
-inline bool MemoryTracker::Hasher::match(const Key& k, const Lookup& l) {
-  return k.cell() == l.cell() && k.use() == l.use();
+template <typename Ptr>
+inline bool MemoryTracker::Hasher<Ptr>::match(const KeyT& k, const Lookup& l) {
+  return k.ptr() == l.ptr() && k.use() == l.use();
 }
 
-inline void MemoryTracker::Hasher::rekey(Key& k, const Key& newKey) {
+template <typename Ptr>
+inline void MemoryTracker::Hasher<Ptr>::rekey(KeyT& k, const KeyT& newKey) {
   k = newKey;
 }
 
 #endif  // DEBUG
--- a/js/src/gc/Scheduling.h
+++ b/js/src/gc/Scheduling.h
@@ -805,76 +805,84 @@ class JitHeapThreshold : public HeapThre
  public:
   explicit JitHeapThreshold(size_t bytes) { bytes_ = bytes; }
 };
 
 #ifdef DEBUG
 
 // Counts memory associated with GC things in a zone.
 //
-// This records details of the cell the memory allocations is associated with to
-// check the correctness of the information provided. This is not present in opt
-// builds.
+// This records details of the cell (or non-cell pointer) the memory allocation
+// is associated with to check the correctness of the information provided. This
+// is not present in opt builds.
 class MemoryTracker {
  public:
   MemoryTracker();
   void fixupAfterMovingGC();
   void checkEmptyOnDestroy();
 
   void adopt(MemoryTracker& other);
 
-  void trackMemory(Cell* cell, size_t nbytes, MemoryUse use);
-  void untrackMemory(Cell* cell, size_t nbytes, MemoryUse use);
-  void swapMemory(Cell* a, Cell* b, MemoryUse use);
-  void registerPolicy(ZoneAllocPolicy* policy);
-  void unregisterPolicy(ZoneAllocPolicy* policy);
-  void movePolicy(ZoneAllocPolicy* dst, ZoneAllocPolicy* src);
-  void incPolicyMemory(ZoneAllocPolicy* policy, size_t nbytes);
-  void decPolicyMemory(ZoneAllocPolicy* policy, size_t nbytes);
+  // Track memory by associated GC thing pointer.
+  void trackGCMemory(Cell* cell, size_t nbytes, MemoryUse use);
+  void untrackGCMemory(Cell* cell, size_t nbytes, MemoryUse use);
+  void swapGCMemory(Cell* a, Cell* b, MemoryUse use);
+
+  // Track memory by associated non-GC thing pointer.
+  void registerNonGCMemory(void* ptr, MemoryUse use);
+  void unregisterNonGCMemory(void* ptr, MemoryUse use);
+  void moveNonGCMemory(void* dst, void* src, MemoryUse use);
+  void incNonGCMemory(void* ptr, size_t nbytes, MemoryUse use);
+  void decNonGCMemory(void* ptr, size_t nbytes, MemoryUse use);
 
  private:
+  template <typename Ptr>
   struct Key {
-    Key(Cell* cell, MemoryUse use);
-    Cell* cell() const;
+    Key(Ptr* ptr, MemoryUse use);
+    Ptr* ptr() const;
     MemoryUse use() const;
 
    private:
 #  ifdef JS_64BIT
     // Pack this into a single word on 64 bit platforms.
-    uintptr_t cell_ : 56;
+    uintptr_t ptr_ : 56;
     uintptr_t use_ : 8;
 #  else
-    uintptr_t cell_ : 32;
+    uintptr_t ptr_ : 32;
     uintptr_t use_ : 8;
 #  endif
   };
 
+  template <typename Ptr>
   struct Hasher {
-    using Lookup = Key;
+    using KeyT = Key<Ptr>;
+    using Lookup = KeyT;
     static HashNumber hash(const Lookup& l);
-    static bool match(const Key& key, const Lookup& l);
-    static void rekey(Key& k, const Key& newKey);
+    static bool match(const KeyT& key, const Lookup& l);
+    static void rekey(KeyT& k, const KeyT& newKey);
   };
 
+  template <typename Ptr>
+  using Map = HashMap<Key<Ptr>, size_t, Hasher<Ptr>, SystemAllocPolicy>;
+  using GCMap = Map<Cell>;
+  using NonGCMap = Map<void>;
+
+  static bool isGCMemoryUse(MemoryUse use);
+  static bool isNonGCMemoryUse(MemoryUse use);
+  static bool allowMultipleAssociations(MemoryUse use);
+
+  size_t getAndRemoveEntry(const Key<Cell>& key, LockGuard<Mutex>& lock);
+
+  Mutex mutex;
+
   // Map containing the allocated size associated with (cell, use) pairs.
-  using Map = HashMap<Key, size_t, Hasher, SystemAllocPolicy>;
-
-  // Map containing the allocated size associated with each instance of a
-  // container that uses ZoneAllocPolicy.
-  using ZoneAllocPolicyMap =
-      HashMap<ZoneAllocPolicy*, size_t, DefaultHasher<ZoneAllocPolicy*>,
-              SystemAllocPolicy>;
+  GCMap gcMap;
 
-  bool allowMultipleAssociations(MemoryUse use) const;
-
-  size_t getAndRemoveEntry(const Key& key, LockGuard<Mutex>& lock);
-
-  Mutex mutex;
-  Map map;
-  ZoneAllocPolicyMap policyMap;
+  // Map containing the allocated size associated (non-cell pointer, use) pairs.
+  NonGCMap nonGCMap;
 };
 
 #endif  // DEBUG
 
 }  // namespace gc
 }  // namespace js
 
 #endif  // gc_Scheduling_h
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -71,17 +71,18 @@ void js::ZoneAllocator::updateGCThreshol
                                     gc.tunables.mallocGrowthFactor(), lock);
 }
 
 void ZoneAllocPolicy::decMemory(size_t nbytes) {
   // Unfortunately we don't have enough context here to know whether we're being
   // called on behalf of the collector so we have to do a TLS lookup to find
   // out.
   JSContext* cx = TlsContext.get();
-  zone_->decPolicyMemory(this, nbytes, cx->defaultFreeOp()->isCollecting());
+  zone_->decNonGCMemory(this, nbytes, MemoryUse::ZoneAllocPolicy,
+                        cx->defaultFreeOp()->isCollecting());
 }
 
 JS::Zone::Zone(JSRuntime* rt)
     : ZoneAllocator(rt),
       // Note: don't use |this| before initializing helperThreadUse_!
       // ProtectedData checks in CheckZone::check may read this field.
       helperThreadUse_(HelperThreadUse::None),
       helperThreadOwnerContext_(nullptr),
--- a/js/src/gc/ZoneAllocator.h
+++ b/js/src/gc/ZoneAllocator.h
@@ -73,70 +73,73 @@ class ZoneAllocator : public JS::shadow:
   void addCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use) {
     MOZ_ASSERT(cell);
     MOZ_ASSERT(nbytes);
     mallocHeapSize.addBytes(nbytes);
 
     // We don't currently check GC triggers here.
 
 #ifdef DEBUG
-    mallocTracker.trackMemory(cell, nbytes, use);
+    mallocTracker.trackGCMemory(cell, nbytes, use);
 #endif
   }
 
   void removeCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use,
                         bool wasSwept = false) {
     MOZ_ASSERT(cell);
     MOZ_ASSERT(nbytes);
     MOZ_ASSERT_IF(CurrentThreadIsGCSweeping(), wasSwept);
 
     mallocHeapSize.removeBytes(nbytes, wasSwept);
 
 #ifdef DEBUG
-    mallocTracker.untrackMemory(cell, nbytes, use);
+    mallocTracker.untrackGCMemory(cell, nbytes, use);
 #endif
   }
 
   void swapCellMemory(js::gc::Cell* a, js::gc::Cell* b, js::MemoryUse use) {
 #ifdef DEBUG
-    mallocTracker.swapMemory(a, b, use);
+    mallocTracker.swapGCMemory(a, b, use);
 #endif
   }
 
+  void registerNonGCMemory(void* mem, MemoryUse use) {
 #ifdef DEBUG
-  void registerPolicy(js::ZoneAllocPolicy* policy) {
-    return mallocTracker.registerPolicy(policy);
-  }
-  void unregisterPolicy(js::ZoneAllocPolicy* policy) {
-    return mallocTracker.unregisterPolicy(policy);
+    return mallocTracker.registerNonGCMemory(mem, use);
+#endif
   }
-  void movePolicy(js::ZoneAllocPolicy* dst, js::ZoneAllocPolicy* src) {
-    return mallocTracker.movePolicy(dst, src);
+  void unregisterNonGCMemory(void* mem, MemoryUse use) {
+#ifdef DEBUG
+    return mallocTracker.unregisterNonGCMemory(mem, use);
+#endif
   }
+  void moveOtherMemory(void* dst, void* src, MemoryUse use) {
+#ifdef DEBUG
+    return mallocTracker.moveNonGCMemory(dst, src, use);
 #endif
+  }
 
-  void incPolicyMemory(js::ZoneAllocPolicy* policy, size_t nbytes) {
+  void incNonGCMemory(void* mem, size_t nbytes, MemoryUse use) {
     MOZ_ASSERT(nbytes);
     mallocHeapSize.addBytes(nbytes);
 
 #ifdef DEBUG
-    mallocTracker.incPolicyMemory(policy, nbytes);
+    mallocTracker.incNonGCMemory(mem, nbytes, use);
 #endif
 
     maybeMallocTriggerZoneGC();
   }
-  void decPolicyMemory(js::ZoneAllocPolicy* policy, size_t nbytes,
-                       bool wasSwept) {
+  void decNonGCMemory(void* mem, size_t nbytes, MemoryUse use, bool wasSwept) {
     MOZ_ASSERT(nbytes);
     MOZ_ASSERT_IF(CurrentThreadIsGCSweeping(), wasSwept);
 
     mallocHeapSize.removeBytes(nbytes, wasSwept);
 
 #ifdef DEBUG
-    mallocTracker.decPolicyMemory(policy, nbytes);
+    mallocTracker.decNonGCMemory(mem, nbytes, use);
 #endif
   }
 
   void incJitMemory(size_t nbytes) {
     MOZ_ASSERT(nbytes);
     jitHeapSize.addBytes(nbytes);
     maybeTriggerZoneGC(jitHeapSize, jitHeapThreshold,
                        JS::GCReason::TOO_MUCH_JIT_CODE);
@@ -210,52 +213,40 @@ class ZoneAllocPolicy : public MallocPro
   ZoneAllocator* zone_;
 
 #ifdef DEBUG
   friend class js::gc::MemoryTracker;  // Can clear |zone_| on merge.
 #endif
 
  public:
   MOZ_IMPLICIT ZoneAllocPolicy(ZoneAllocator* z) : zone_(z) {
-#ifdef DEBUG
-    zone()->registerPolicy(this);
-#endif
+    zone()->registerNonGCMemory(this, MemoryUse::ZoneAllocPolicy);
   }
   MOZ_IMPLICIT ZoneAllocPolicy(JS::Zone* z)
       : ZoneAllocPolicy(ZoneAllocator::from(z)) {}
   ZoneAllocPolicy(ZoneAllocPolicy& other) : ZoneAllocPolicy(other.zone_) {}
   ZoneAllocPolicy(ZoneAllocPolicy&& other) : zone_(other.zone_) {
-#ifdef DEBUG
-    zone()->movePolicy(this, &other);
-#endif
+    zone()->moveOtherMemory(this, &other, MemoryUse::ZoneAllocPolicy);
     other.zone_ = nullptr;
   }
   ~ZoneAllocPolicy() {
-#ifdef DEBUG
     if (zone_) {
-      zone_->unregisterPolicy(this);
+      zone_->unregisterNonGCMemory(this, MemoryUse::ZoneAllocPolicy);
     }
-#endif
   }
 
   ZoneAllocPolicy& operator=(const ZoneAllocPolicy& other) {
-#ifdef DEBUG
-    zone()->unregisterPolicy(this);
-#endif
+    zone()->unregisterNonGCMemory(this, MemoryUse::ZoneAllocPolicy);
     zone_ = other.zone();
-#ifdef DEBUG
-    zone()->registerPolicy(this);
-#endif
+    zone()->registerNonGCMemory(this, MemoryUse::ZoneAllocPolicy);
     return *this;
   }
   ZoneAllocPolicy& operator=(ZoneAllocPolicy&& other) {
-#ifdef DEBUG
-    zone()->unregisterPolicy(this);
-    zone()->movePolicy(this, &other);
-#endif
+    zone()->unregisterNonGCMemory(this, MemoryUse::ZoneAllocPolicy);
+    zone()->moveOtherMemory(this, &other, MemoryUse::ZoneAllocPolicy);
     other.zone_ = nullptr;
     return *this;
   }
 
   // Public methods required to fulfill the AllocPolicy interface.
 
   template <typename T>
   void free_(T* p, size_t numElems) {
@@ -275,17 +266,17 @@ class ZoneAllocPolicy : public MallocPro
 
   MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc,
                                    arena_id_t arena, size_t nbytes,
                                    void* reallocPtr = nullptr) {
     return zone()->onOutOfMemory(allocFunc, arena, nbytes, reallocPtr);
   }
   void reportAllocationOverflow() const { zone()->reportAllocationOverflow(); }
   void updateMallocCounter(size_t nbytes) {
-    zone()->incPolicyMemory(this, nbytes);
+    zone()->incNonGCMemory(this, nbytes, MemoryUse::ZoneAllocPolicy);
   }
 
  private:
   ZoneAllocator* zone() const {
     MOZ_ASSERT(zone_);
     return zone_;
   }
   void decMemory(size_t nbytes);