Bug 1161377 (part 2.5) - Move all static PLDHashTable instances onto the heap to avoid static constructors. r=froydnj.
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 12 May 2015 17:33:22 -0700
changeset 274393 6f6fd4dfe3a35c8a06548fa0e0d78d8eba08da46
parent 274392 8d4411ad492bff3a79fe372b35257eea52fb57ce
child 274394 a0587959d25ed24d9f87d9968814e72cbaf48f5e
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1161377
milestone41.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 1161377 (part 2.5) - Move all static PLDHashTable instances onto the heap to avoid static constructors. r=froydnj.
dom/base/nsContentList.cpp
dom/base/nsContentUtils.cpp
dom/plugins/base/nsJSNPRuntime.cpp
modules/libpref/Preferences.cpp
modules/libpref/nsPrefBranch.cpp
modules/libpref/prefapi.cpp
modules/libpref/prefapi_private_data.h
netwerk/protocol/http/nsHttp.cpp
parser/htmlparser/nsHTMLEntities.cpp
xpcom/ds/nsAtomTable.cpp
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -148,17 +148,17 @@ NS_IMPL_RELEASE_INHERITED(nsSimpleConten
 
 JSObject*
 nsSimpleContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return NodeListBinding::Wrap(cx, this, aGivenProto);
 }
 
 // Hashtable for storing nsContentLists
-static PLDHashTable gContentListHashTable;
+static PLDHashTable* gContentListHashTable;
 
 #define RECENTLY_USED_CONTENT_LIST_CACHE_SIZE 31
 static nsContentList*
   sRecentlyUsedContentLists[RECENTLY_USED_CONTENT_LIST_CACHE_SIZE] = {};
 
 static MOZ_ALWAYS_INLINE uint32_t
 RecentlyUsedCacheIndex(const nsContentListKey& aKey)
 {
@@ -210,29 +210,27 @@ NS_GetContentList(nsINode* aRootNode,
   {
     ContentListHashtableHashKey,
     ContentListHashtableMatchEntry,
     PL_DHashMoveEntryStub,
     PL_DHashClearEntryStub
   };
 
   // Initialize the hashtable if needed.
-  if (!gContentListHashTable.IsInitialized()) {
-    PL_DHashTableInit(&gContentListHashTable, &hash_table_ops,
-                      sizeof(ContentListHashEntry));
+  if (!gContentListHashTable) {
+    gContentListHashTable =
+      new PLDHashTable(&hash_table_ops, sizeof(ContentListHashEntry));
   }
 
   ContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
-  if (gContentListHashTable.IsInitialized()) {
-    entry = static_cast<ContentListHashEntry *>
-      (PL_DHashTableAdd(&gContentListHashTable, &hashKey, fallible));
-    if (entry)
-      list = entry->mContentList;
-  }
+  entry = static_cast<ContentListHashEntry *>
+    (PL_DHashTableAdd(gContentListHashTable, &hashKey, fallible));
+  if (entry)
+    list = entry->mContentList;
 
   if (!list) {
     // We need to create a ContentList and add it to our new entry, if
     // we have an entry
     nsCOMPtr<nsIAtom> xmlAtom = do_GetAtom(aTagname);
     nsCOMPtr<nsIAtom> htmlAtom;
     if (aMatchNameSpaceId == kNameSpaceID_Unknown) {
       nsAutoString lowercaseName;
@@ -267,17 +265,17 @@ nsCacheableFuncStringNodeList::WrapObjec
 
 JSObject*
 nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLCollectionBinding::Wrap(cx, this, aGivenProto);
 }
 
 // Hashtable for storing nsCacheableFuncStringContentList
-static PLDHashTable gFuncStringContentListHashTable;
+static PLDHashTable* gFuncStringContentListHashTable;
 
 struct FuncStringContentListHashEntry : public PLDHashEntryHdr
 {
   nsCacheableFuncStringContentList* mContentList;
 };
 
 static PLDHashNumber
 FuncStringContentListHashtableHashKey(PLDHashTable *table, const void *key)
@@ -316,28 +314,28 @@ GetFuncStringContentList(nsINode* aRootN
   {
     FuncStringContentListHashtableHashKey,
     FuncStringContentListHashtableMatchEntry,
     PL_DHashMoveEntryStub,
     PL_DHashClearEntryStub
   };
 
   // Initialize the hashtable if needed.
-  if (!gFuncStringContentListHashTable.IsInitialized()) {
-    PL_DHashTableInit(&gFuncStringContentListHashTable, &hash_table_ops,
-                      sizeof(FuncStringContentListHashEntry));
+  if (!gFuncStringContentListHashTable) {
+    gFuncStringContentListHashTable =
+      new PLDHashTable(&hash_table_ops, sizeof(FuncStringContentListHashEntry));
   }
 
   FuncStringContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
-  if (gFuncStringContentListHashTable.IsInitialized()) {
+  if (gFuncStringContentListHashTable) {
     nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
 
     entry = static_cast<FuncStringContentListHashEntry *>
-      (PL_DHashTableAdd(&gFuncStringContentListHashTable, &hashKey, fallible));
+      (PL_DHashTableAdd(gFuncStringContentListHashTable, &hashKey, fallible));
     if (entry) {
       list = entry->mContentList;
 #ifdef DEBUG
       MOZ_ASSERT_IF(list, list->mType == ListType::sType);
 #endif
     }
   }
 
@@ -965,23 +963,24 @@ nsContentList::RemoveFromHashtable()
   
   nsDependentAtomString str(mXMLMatchAtom);
   nsContentListKey key(mRootNode, mMatchNameSpaceId, str);
   uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(key);
   if (sRecentlyUsedContentLists[recentlyUsedCacheIndex] == this) {
     sRecentlyUsedContentLists[recentlyUsedCacheIndex] = nullptr;
   }
 
-  if (!gContentListHashTable.IsInitialized())
+  if (!gContentListHashTable)
     return;
 
-  PL_DHashTableRemove(&gContentListHashTable, &key);
+  PL_DHashTableRemove(gContentListHashTable, &key);
 
-  if (gContentListHashTable.EntryCount() == 0) {
-    PL_DHashTableFinish(&gContentListHashTable);
+  if (gContentListHashTable->EntryCount() == 0) {
+    delete gContentListHashTable;
+    gContentListHashTable = nullptr;
   }
 }
 
 void
 nsContentList::BringSelfUpToDate(bool aDoFlush)
 {
   if (mRootNode && aDoFlush && mFlushesNeeded) {
     // XXX sXBL/XBL2 issue
@@ -1003,25 +1002,26 @@ nsContentList::BringSelfUpToDate(bool aD
 nsCacheableFuncStringContentList::~nsCacheableFuncStringContentList()
 {
   RemoveFromFuncStringHashtable();
 }
 
 void
 nsCacheableFuncStringContentList::RemoveFromFuncStringHashtable()
 {
-  if (!gFuncStringContentListHashTable.IsInitialized()) {
+  if (!gFuncStringContentListHashTable) {
     return;
   }
 
   nsFuncStringCacheKey key(mRootNode, mFunc, mString);
-  PL_DHashTableRemove(&gFuncStringContentListHashTable, &key);
+  PL_DHashTableRemove(gFuncStringContentListHashTable, &key);
 
-  if (gFuncStringContentListHashTable.EntryCount() == 0) {
-    PL_DHashTableFinish(&gFuncStringContentListHashTable);
+  if (gFuncStringContentListHashTable->EntryCount() == 0) {
+    delete gFuncStringContentListHashTable;
+    gFuncStringContentListHashTable = nullptr;
   }
 }
 
 #ifdef DEBUG_CONTENT_LIST
 void
 nsContentList::AssertInSync()
 {
   if (mState == LIST_DIRTY) {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -332,35 +332,35 @@ static const nsAttrValue::EnumTable kAut
   { 0 }
 };
 
 namespace {
 
 static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
-static PLDHashTable sEventListenerManagersHash;
+static PLDHashTable* sEventListenerManagersHash;
 
 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter
 {
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   ~DOMEventListenerManagersHashReporter() {}
 
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                             nsISupports* aData, bool aAnonymize) override
   {
     // We don't measure the |EventListenerManager| objects pointed to by the
     // entries because those references are non-owning.
-    int64_t amount = sEventListenerManagersHash.IsInitialized()
+    int64_t amount = sEventListenerManagersHash
                    ? PL_DHashTableSizeOfExcludingThis(
-                       &sEventListenerManagersHash, nullptr, MallocSizeOf)
+                       sEventListenerManagersHash, nullptr, MallocSizeOf)
                    : 0;
 
     return MOZ_COLLECT_REPORT(
       "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
       amount,
       "Memory used by the event listener manager's hash table.");
   }
 };
@@ -483,28 +483,28 @@ nsContentUtils::Init()
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = CallGetService(NS_WBRK_CONTRACTID, &sWordBreaker);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!InitializeEventTable())
     return NS_ERROR_FAILURE;
 
-  if (!sEventListenerManagersHash.IsInitialized()) {
+  if (!sEventListenerManagersHash) {
     static const PLDHashTableOps hash_table_ops =
     {
       PL_DHashVoidPtrKeyStub,
       PL_DHashMatchEntryStub,
       PL_DHashMoveEntryStub,
       EventListenerManagerHashClearEntry,
       EventListenerManagerHashInitEntry
     };
 
-    PL_DHashTableInit(&sEventListenerManagersHash, &hash_table_ops,
-                      sizeof(EventListenerManagerMapEntry));
+    sEventListenerManagersHash =
+      new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
 
     RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
   }
 
   sBlockedScriptRunners = new nsTArray< nsCOMPtr<nsIRunnable> >;
 
   Preferences::AddBoolVarCache(&sAllowXULXBL_for_file,
                                "dom.allow_XUL_XBL_for_file");
@@ -1805,31 +1805,32 @@ nsContentUtils::Shutdown()
 
   delete sAtomEventTable;
   sAtomEventTable = nullptr;
   delete sStringEventTable;
   sStringEventTable = nullptr;
   delete sUserDefinedEvents;
   sUserDefinedEvents = nullptr;
 
-  if (sEventListenerManagersHash.IsInitialized()) {
-    NS_ASSERTION(sEventListenerManagersHash.EntryCount() == 0,
+  if (sEventListenerManagersHash) {
+    NS_ASSERTION(sEventListenerManagersHash->EntryCount() == 0,
                  "Event listener manager hash not empty at shutdown!");
 
     // See comment above.
 
     // However, we have to handle this table differently.  If it still
     // has entries, we want to leak it too, so that we can keep it alive
     // in case any elements are destroyed.  Because if they are, we need
     // their event listener managers to be destroyed too, or otherwise
     // it could leave dangling references in DOMClassInfo's preserved
     // wrapper table.
 
-    if (sEventListenerManagersHash.EntryCount() == 0) {
-      PL_DHashTableFinish(&sEventListenerManagersHash);
+    if (sEventListenerManagersHash->EntryCount() == 0) {
+      delete sEventListenerManagersHash;
+      sEventListenerManagersHash = nullptr;
     }
   }
 
   NS_ASSERTION(!sBlockedScriptRunners ||
                sBlockedScriptRunners->Length() == 0,
                "How'd this happen?");
   delete sBlockedScriptRunners;
   sBlockedScriptRunners = nullptr;
@@ -3984,54 +3985,54 @@ ListenerEnumerator(PLDHashTable* aTable,
     }
   }
   return PL_DHASH_NEXT;
 }
 
 void
 nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(uint32_t aGeneration)
 {
-  if (sEventListenerManagersHash.IsInitialized()) {
-    PL_DHashTableEnumerate(&sEventListenerManagersHash, ListenerEnumerator,
+  if (sEventListenerManagersHash) {
+    PL_DHashTableEnumerate(sEventListenerManagersHash, ListenerEnumerator,
                            &aGeneration);
   }
 }
 
 /* static */
 void
 nsContentUtils::TraverseListenerManager(nsINode *aNode,
                                         nsCycleCollectionTraversalCallback &cb)
 {
-  if (!sEventListenerManagersHash.IsInitialized()) {
+  if (!sEventListenerManagersHash) {
     // We're already shut down, just return.
     return;
   }
 
   EventListenerManagerMapEntry *entry =
     static_cast<EventListenerManagerMapEntry *>
-               (PL_DHashTableSearch(&sEventListenerManagersHash, aNode));
+               (PL_DHashTableSearch(sEventListenerManagersHash, aNode));
   if (entry) {
     CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
                              "[via hash] mListenerManager");
   }
 }
 
 EventListenerManager*
 nsContentUtils::GetListenerManagerForNode(nsINode *aNode)
 {
-  if (!sEventListenerManagersHash.IsInitialized()) {
+  if (!sEventListenerManagersHash) {
     // We're already shut down, don't bother creating an event listener
     // manager.
 
     return nullptr;
   }
 
   EventListenerManagerMapEntry *entry =
     static_cast<EventListenerManagerMapEntry *>
-      (PL_DHashTableAdd(&sEventListenerManagersHash, aNode, fallible));
+      (PL_DHashTableAdd(sEventListenerManagersHash, aNode, fallible));
 
   if (!entry) {
     return nullptr;
   }
 
   if (!entry->mListenerManager) {
     entry->mListenerManager = new EventListenerManager(aNode);
 
@@ -4043,47 +4044,47 @@ nsContentUtils::GetListenerManagerForNod
 
 EventListenerManager*
 nsContentUtils::GetExistingListenerManagerForNode(const nsINode *aNode)
 {
   if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
     return nullptr;
   }
 
-  if (!sEventListenerManagersHash.IsInitialized()) {
+  if (!sEventListenerManagersHash) {
     // We're already shut down, don't bother creating an event listener
     // manager.
 
     return nullptr;
   }
 
   EventListenerManagerMapEntry *entry =
     static_cast<EventListenerManagerMapEntry *>
-               (PL_DHashTableSearch(&sEventListenerManagersHash, aNode));
+               (PL_DHashTableSearch(sEventListenerManagersHash, aNode));
   if (entry) {
     return entry->mListenerManager;
   }
 
   return nullptr;
 }
 
 /* static */
 void
 nsContentUtils::RemoveListenerManager(nsINode *aNode)
 {
-  if (sEventListenerManagersHash.IsInitialized()) {
+  if (sEventListenerManagersHash) {
     EventListenerManagerMapEntry *entry =
       static_cast<EventListenerManagerMapEntry *>
-                 (PL_DHashTableSearch(&sEventListenerManagersHash, aNode));
+                 (PL_DHashTableSearch(sEventListenerManagersHash, aNode));
     if (entry) {
       nsRefPtr<EventListenerManager> listenerManager;
       listenerManager.swap(entry->mListenerManager);
       // Remove the entry and *then* do operations that could cause further
       // modification of sEventListenerManagersHash.  See bug 334177.
-      PL_DHashTableRawRemove(&sEventListenerManagersHash, entry);
+      PL_DHashTableRawRemove(sEventListenerManagersHash, entry);
       if (listenerManager) {
         listenerManager->Disconnect();
       }
     }
   }
 }
 
 /* static */
@@ -7758,9 +7759,9 @@ nsContentUtils::FirePageShowEvent(nsIDoc
     }
   }
 
   nsCOMPtr<nsIDocument> doc = aItem->GetDocument();
   NS_ASSERTION(doc, "What happened here?");
   if (doc->IsShowing() == aFireIfShowing) {
     doc->OnPageShow(true, aChromeEventHandler);
   }
-}
\ No newline at end of file
+}
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -78,17 +78,17 @@ typedef js::HashMap<nsJSObjWrapperKey,
                     js::SystemAllocPolicy> JSObjWrapperTable;
 static JSObjWrapperTable sJSObjWrappers;
 
 // Whether it's safe to iterate sJSObjWrappers.  Set to true when sJSObjWrappers
 // has been initialized and is not currently being enumerated.
 static bool sJSObjWrappersAccessible = false;
 
 // Hash of NPObject wrappers that wrap NPObjects as JSObjects.
-static PLDHashTable sNPObjWrappers;
+static PLDHashTable* sNPObjWrappers;
 
 // Global wrapper count. This includes JSObject wrappers *and*
 // NPObject wrappers. When this count goes to zero, there are no more
 // wrappers and we can kill off hash tables etc.
 static int32_t sWrapperCount;
 
 // The runtime service used to register/unregister GC callbacks.
 nsCOMPtr<nsIJSRuntimeService> sCallbackRuntime;
@@ -396,33 +396,34 @@ DestroyJSObjWrapperTable()
   // hash to prevent leaking it.
   sJSObjWrappers.finish();
   sJSObjWrappersAccessible = false;
 }
 
 static bool
 CreateNPObjWrapperTable()
 {
-  MOZ_ASSERT(!sNPObjWrappers.IsInitialized());
+  MOZ_ASSERT(!sNPObjWrappers);
 
   if (!RegisterGCCallbacks()) {
     return false;
   }
 
-  PL_DHashTableInit(&sNPObjWrappers, PL_DHashGetStubOps(),
-                    sizeof(NPObjWrapperHashEntry));
+  sNPObjWrappers =
+    new PLDHashTable(PL_DHashGetStubOps(), sizeof(NPObjWrapperHashEntry));
   return true;
 }
 
 static void
 DestroyNPObjWrapperTable()
 {
-  MOZ_ASSERT(sNPObjWrappers.EntryCount() == 0);
-
-  PL_DHashTableFinish(&sNPObjWrappers);
+  MOZ_ASSERT(sNPObjWrappers->EntryCount() == 0);
+
+  delete sNPObjWrappers;
+  sNPObjWrappers = nullptr;
 }
 
 static void
 OnWrapperCreated()
 {
   ++sWrapperCount;
 }
 
@@ -431,17 +432,17 @@ OnWrapperDestroyed()
 {
   NS_ASSERTION(sWrapperCount, "Whaaa, unbalanced created/destroyed calls!");
 
   if (--sWrapperCount == 0) {
     if (sJSObjWrappersAccessible) {
       DestroyJSObjWrapperTable();
     }
 
-    if (sNPObjWrappers.IsInitialized()) {
+    if (sNPObjWrappers) {
       // No more wrappers, and our hash was initialized. Finish the
       // hash to prevent leaking it.
       DestroyNPObjWrapperTable();
     }
 
     UnregisterGCCallbacks();
   }
 }
@@ -1756,46 +1757,46 @@ NPObjWrapper_Convert(JSContext *cx, JS::
   return false;
 }
 
 static void
 NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj)
 {
   NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
   if (npobj) {
-    if (sNPObjWrappers.IsInitialized()) {
-      PL_DHashTableRemove(&sNPObjWrappers, npobj);
+    if (sNPObjWrappers) {
+      PL_DHashTableRemove(sNPObjWrappers, npobj);
     }
   }
 
   if (!sDelayedReleases)
     sDelayedReleases = new nsTArray<NPObject*>;
   sDelayedReleases->AppendElement(npobj);
 }
 
 static void
 NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old)
 {
   // The wrapper JSObject has been moved, so we need to update the entry in the
   // sNPObjWrappers hash table, if present.
 
-  if (!sNPObjWrappers.IsInitialized()) {
+  if (!sNPObjWrappers) {
     return;
   }
 
   NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
   if (!npobj) {
     return;
   }
 
   // Calling PL_DHashTableSearch() will not result in GC.
   JS::AutoSuppressGCAnalysis nogc;
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableSearch(&sNPObjWrappers, npobj));
+    (PL_DHashTableSearch(sNPObjWrappers, npobj));
   MOZ_ASSERT(entry && entry->mJSObj);
   MOZ_ASSERT(entry->mJSObj == old);
   entry->mJSObj = obj;
 }
 
 static bool
 NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp)
 {
@@ -1831,33 +1832,33 @@ nsNPObjWrapper::OnDestroy(NPObject *npob
   }
 
   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
     // npobj is one of our own, no private data to clean up here.
 
     return;
   }
 
-  if (!sNPObjWrappers.IsInitialized()) {
+  if (!sNPObjWrappers) {
     // No hash yet (or any more), no used wrappers available.
 
     return;
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableSearch(&sNPObjWrappers, npobj));
+    (PL_DHashTableSearch(sNPObjWrappers, npobj));
 
   if (entry && entry->mJSObj) {
     // Found a live NPObject wrapper, null out its JSObjects' private
     // data.
 
     ::JS_SetPrivate(entry->mJSObj, nullptr);
 
     // Remove the npobj from the hash now that it went away.
-    PL_DHashTableRawRemove(&sNPObjWrappers, entry);
+    PL_DHashTableRawRemove(sNPObjWrappers, entry);
 
     // The finalize hook will call OnWrapperDestroyed().
   }
 }
 
 // Look up or create a JSObject that wraps the NPObject npobj.
 
 // static
@@ -1881,25 +1882,25 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
   }
 
   if (!npp) {
     NS_ERROR("No npp passed to nsNPObjWrapper::GetNewOrUsed()!");
 
     return nullptr;
   }
 
-  if (!sNPObjWrappers.IsInitialized()) {
+  if (!sNPObjWrappers) {
     // No hash yet (or any more), initialize it.
     if (!CreateNPObjWrapperTable()) {
       return nullptr;
     }
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableAdd(&sNPObjWrappers, npobj, fallible));
+    (PL_DHashTableAdd(sNPObjWrappers, npobj, fallible));
 
   if (!entry) {
     // Out of memory
     JS_ReportOutOfMemory(cx);
 
     return nullptr;
   }
 
@@ -1911,34 +1912,34 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
       return nullptr;
     }
     return obj;
   }
 
   entry->mNPObj = npobj;
   entry->mNpp = npp;
 
-  uint32_t generation = sNPObjWrappers.Generation();
+  uint32_t generation = sNPObjWrappers->Generation();
 
   // No existing JSObject, create one.
 
   JS::Rooted<JSObject*> obj(cx, ::JS_NewObject(cx, js::Jsvalify(&sNPObjectJSWrapperClass)));
 
-  if (generation != sNPObjWrappers.Generation()) {
+  if (generation != sNPObjWrappers->Generation()) {
       // Reload entry if the JS_NewObject call caused a GC and reallocated
       // the table (see bug 445229). This is guaranteed to succeed.
 
-      NS_ASSERTION(PL_DHashTableSearch(&sNPObjWrappers, npobj),
+      NS_ASSERTION(PL_DHashTableSearch(sNPObjWrappers, npobj),
                    "Hashtable didn't find what we just added?");
   }
 
   if (!obj) {
     // OOM? Remove the stale entry from the hash.
 
-    PL_DHashTableRawRemove(&sNPObjWrappers, entry);
+    PL_DHashTableRawRemove(sNPObjWrappers, entry);
 
     return nullptr;
   }
 
   OnWrapperCreated();
 
   entry->mJSObj = obj;
 
@@ -2034,19 +2035,19 @@ nsJSNPRuntime::OnPluginDestroy(NPP npp)
     }
 
     sJSObjWrappersAccessible = true;
   }
 
   // Use the safe JSContext here as we're not always able to find the
   // JSContext associated with the NPP any more.
   AutoSafeJSContext cx;
-  if (sNPObjWrappers.IsInitialized()) {
+  if (sNPObjWrappers) {
     NppAndCx nppcx = { npp, cx };
-    PL_DHashTableEnumerate(&sNPObjWrappers,
+    PL_DHashTableEnumerate(sNPObjWrappers,
                            NPObjWrapperPluginDestroyedCallback, &nppcx);
   }
 }
 
 // static
 void
 nsJSNPRuntime::OnPluginDestroyPending(NPP npp)
 {
@@ -2069,17 +2070,17 @@ static NPP
 LookupNPP(NPObject *npobj)
 {
   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
     nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj);
     return o->mNpp;
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableAdd(&sNPObjWrappers, npobj, fallible));
+    (PL_DHashTableAdd(sNPObjWrappers, npobj, fallible));
 
   if (!entry) {
     return nullptr;
   }
 
   NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
 
   return entry->mNpp;
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -230,20 +230,20 @@ SizeOfObserverEntryExcludingThis(ValueOb
 // Although this is a member of Preferences, it measures sPreferences and
 // several other global structures.
 /* static */ int64_t
 Preferences::SizeOfIncludingThisAndOtherStuff(mozilla::MallocSizeOf aMallocSizeOf)
 {
   NS_ENSURE_TRUE(InitStaticMembers(), 0);
 
   size_t n = aMallocSizeOf(sPreferences);
-  if (gHashTable.IsInitialized()) {
+  if (gHashTable) {
     // pref keys are allocated in a private arena, which we count elsewhere.
     // pref stringvals are allocated out of the same private arena.
-    n += PL_DHashTableSizeOfExcludingThis(&gHashTable, nullptr, aMallocSizeOf);
+    n += PL_DHashTableSizeOfExcludingThis(gHashTable, nullptr, aMallocSizeOf);
   }
   if (gCacheData) {
     n += gCacheData->SizeOfIncludingThis(aMallocSizeOf);
     for (uint32_t i = 0, count = gCacheData->Length(); i < count; ++i) {
       n += aMallocSizeOf((*gCacheData)[i]);
     }
   }
   if (gObserverTable) {
@@ -733,18 +733,18 @@ Preferences::GetPreference(PrefSetting* 
     return;
 
   pref_GetPrefFromEntry(entry, aPref);
 }
 
 void
 Preferences::GetPreferences(InfallibleTArray<PrefSetting>* aPrefs)
 {
-  aPrefs->SetCapacity(gHashTable.Capacity());
-  PL_DHashTableEnumerate(&gHashTable, pref_GetPrefs, aPrefs);
+  aPrefs->SetCapacity(gHashTable->Capacity());
+  PL_DHashTableEnumerate(gHashTable, pref_GetPrefs, aPrefs);
 }
 
 NS_IMETHODIMP
 Preferences::GetBranch(const char *aPrefRoot, nsIPrefBranch **_retval)
 {
   nsresult rv;
 
   if ((nullptr != aPrefRoot) && (*aPrefRoot != '\0')) {
@@ -939,47 +939,48 @@ Preferences::WritePrefFile(nsIFile* aFil
     NS_LINEBREAK
     NS_LINEBREAK;
 
   nsCOMPtr<nsIOutputStream> outStreamSink;
   nsCOMPtr<nsIOutputStream> outStream;
   uint32_t                  writeAmount;
   nsresult                  rv;
 
-  if (!gHashTable.IsInitialized())
+  if (!gHashTable)
     return NS_ERROR_NOT_INITIALIZED;
 
   // execute a "safe" save by saving through a tempfile
   rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStreamSink),
                                        aFile,
                                        -1,
                                        0600);
   if (NS_FAILED(rv)) 
       return rv;
   rv = NS_NewBufferedOutputStream(getter_AddRefs(outStream), outStreamSink, 4096);
   if (NS_FAILED(rv)) 
       return rv;  
 
-  nsAutoArrayPtr<char*> valueArray(new char*[gHashTable.EntryCount()]);
-  memset(valueArray, 0, gHashTable.EntryCount() * sizeof(char*));
+  nsAutoArrayPtr<char*> valueArray(new char*[gHashTable->EntryCount()]);
+  memset(valueArray, 0, gHashTable->EntryCount() * sizeof(char*));
   pref_saveArgs saveArgs;
   saveArgs.prefArray = valueArray;
   saveArgs.saveTypes = SAVE_ALL;
-  
+
   // get the lines that we're supposed to be writing to the file
-  PL_DHashTableEnumerate(&gHashTable, pref_savePref, &saveArgs);
-    
+  PL_DHashTableEnumerate(gHashTable, pref_savePref, &saveArgs);
+
   /* Sort the preferences to make a readable file on disk */
-  NS_QuickSort(valueArray, gHashTable.EntryCount(), sizeof(char *), pref_CompareStrings, nullptr);
-  
+  NS_QuickSort(valueArray, gHashTable->EntryCount(), sizeof(char *),
+               pref_CompareStrings, nullptr);
+
   // write out the file header
   outStream->Write(outHeader, sizeof(outHeader) - 1, &writeAmount);
 
   char** walker = valueArray;
-  for (uint32_t valueIdx = 0; valueIdx < gHashTable.EntryCount(); valueIdx++, walker++) {
+  for (uint32_t valueIdx = 0; valueIdx < gHashTable->EntryCount(); valueIdx++, walker++) {
     if (*walker) {
       outStream->Write(*walker, strlen(*walker), &writeAmount);
       outStream->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &writeAmount);
       free(*walker);
     }
   }
 
   // tell the safe output stream to overwrite the real prefs file
--- a/modules/libpref/nsPrefBranch.cpp
+++ b/modules/libpref/nsPrefBranch.cpp
@@ -550,25 +550,25 @@ NS_IMETHODIMP nsPrefBranch::GetChildList
 
   NS_ENSURE_ARG(aStartingAt);
   NS_ENSURE_ARG_POINTER(aCount);
   NS_ENSURE_ARG_POINTER(aChildArray);
 
   *aChildArray = nullptr;
   *aCount = 0;
 
-  if (!gHashTable.IsInitialized())
+  if (!gHashTable->IsInitialized())
     return NS_ERROR_NOT_INITIALIZED;
 
   // this will contain a list of all the pref name strings
   // allocate on the stack for speed
-  
+
   ed.parent = getPrefName(aStartingAt);
   ed.pref_list = &prefArray;
-  PL_DHashTableEnumerate(&gHashTable, pref_enumChild, &ed);
+  PL_DHashTableEnumerate(gHashTable, pref_enumChild, &ed);
 
   // now that we've built up the list, run the callback on
   // all the matching elements
   numPrefs = prefArray.Length();
 
   if (numPrefs) {
     outArray = (char **)moz_xmalloc(numPrefs * sizeof(char *));
     if (!outArray)
--- a/modules/libpref/prefapi.cpp
+++ b/modules/libpref/prefapi.cpp
@@ -63,17 +63,17 @@ matchPrefEntry(PLDHashTable*, const PLDH
     if (prefEntry->key == key) return true;
 
     if (!prefEntry->key || !key) return false;
 
     const char *otherKey = reinterpret_cast<const char*>(key);
     return (strcmp(prefEntry->key, otherKey) == 0);
 }
 
-PLDHashTable        gHashTable;
+PLDHashTable*       gHashTable;
 static PLArenaPool  gPrefNameArena;
 bool                gDirty = false;
 
 static struct CallbackNode* gCallbacks = nullptr;
 static bool         gIsAnyPrefLocked = false;
 // These are only used during the call to pref_DoCallback
 static bool         gCallbacksInProgress = false;
 static bool         gShouldCleanupDeadNodes = false;
@@ -144,19 +144,20 @@ enum {
     kPrefStickyDefault = 4,
 };
 static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags);
 
 #define PREF_HASHTABLE_INITIAL_LENGTH   1024
 
 nsresult PREF_Init()
 {
-    if (!gHashTable.IsInitialized()) {
-        PL_DHashTableInit(&gHashTable, &pref_HashTableOps,
-                          sizeof(PrefHashEntry), PREF_HASHTABLE_INITIAL_LENGTH);
+    if (!gHashTable) {
+        gHashTable = new PLDHashTable(&pref_HashTableOps,
+                                      sizeof(PrefHashEntry),
+                                      PREF_HASHTABLE_INITIAL_LENGTH);
 
         PL_INIT_ARENA_POOL(&gPrefNameArena, "PrefNameArena",
                            PREFNAME_ARENA_SIZE);
     }
     return NS_OK;
 }
 
 /* Frees the callback list. */
@@ -177,18 +178,19 @@ void PREF_Cleanup()
     gCallbacks = nullptr;
 
     PREF_CleanupPrefs();
 }
 
 /* Frees up all the objects except the callback list. */
 void PREF_CleanupPrefs()
 {
-    if (gHashTable.IsInitialized()) {
-        PL_DHashTableFinish(&gHashTable);
+    if (gHashTable) {
+        delete gHashTable;
+        gHashTable = nullptr;
         PL_FinishArenaPool(&gPrefNameArena);
     }
 }
 
 // note that this appends to aResult, and does not assign!
 static void str_escape(const char * original, nsAFlatCString& aResult)
 {
     /* JavaScript does not allow quotes, slashes, or line terminators inside
@@ -461,31 +463,31 @@ pref_CompareStrings(const void *v1, cons
     else if (!s2)
         return 1;
     else
         return strcmp(s1, s2);
 }
 
 bool PREF_HasUserPref(const char *pref_name)
 {
-    if (!gHashTable.IsInitialized())
+    if (!gHashTable)
         return false;
 
     PrefHashEntry *pref = pref_HashTableLookup(pref_name);
     if (!pref) return false;
 
     /* convert PREF_HAS_USER_VALUE to bool */
     return (PREF_HAS_USER_VALUE(pref) != 0);
 
 }
 
 nsresult
 PREF_CopyCharPref(const char *pref_name, char ** return_buffer, bool get_default)
 {
-    if (!gHashTable.IsInitialized())
+    if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     nsresult rv = NS_ERROR_UNEXPECTED;
     char* stringVal;
     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
 
     if (pref && (pref->flags & PREF_STRING))
     {
@@ -499,17 +501,17 @@ PREF_CopyCharPref(const char *pref_name,
             rv = NS_OK;
         }
     }
     return rv;
 }
 
 nsresult PREF_GetIntPref(const char *pref_name,int32_t * return_int, bool get_default)
 {
-    if (!gHashTable.IsInitialized())
+    if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     nsresult rv = NS_ERROR_UNEXPECTED;
     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
     if (pref && (pref->flags & PREF_INT))
     {
         if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref))
         {
@@ -523,17 +525,17 @@ nsresult PREF_GetIntPref(const char *pre
             *return_int = pref->userPref.intVal;
         rv = NS_OK;
     }
     return rv;
 }
 
 nsresult PREF_GetBoolPref(const char *pref_name, bool * return_value, bool get_default)
 {
-    if (!gHashTable.IsInitialized())
+    if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     nsresult rv = NS_ERROR_UNEXPECTED;
     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
     //NS_ASSERTION(pref, pref_name);
     if (pref && (pref->flags & PREF_BOOL))
     {
         if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref))
@@ -574,49 +576,49 @@ nsresult
 PREF_DeleteBranch(const char *branch_name)
 {
 #ifndef MOZ_B2G
     MOZ_ASSERT(NS_IsMainThread());
 #endif
 
     int len = (int)strlen(branch_name);
 
-    if (!gHashTable.IsInitialized())
+    if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     /* The following check insures that if the branch name already has a "."
      * at the end, we don't end up with a "..". This fixes an incompatibility
      * between nsIPref, which needs the period added, and nsIPrefBranch which
      * does not. When nsIPref goes away this function should be fixed to
      * never add the period at all.
      */
     nsAutoCString branch_dot(branch_name);
     if ((len > 1) && branch_name[len - 1] != '.')
         branch_dot += '.';
 
-    PL_DHashTableEnumerate(&gHashTable, pref_DeleteItem,
+    PL_DHashTableEnumerate(gHashTable, pref_DeleteItem,
                            (void*) branch_dot.get());
     gDirty = true;
     return NS_OK;
 }
 
 
 nsresult
 PREF_ClearUserPref(const char *pref_name)
 {
-    if (!gHashTable.IsInitialized())
+    if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
     if (pref && PREF_HAS_USER_VALUE(pref))
     {
         pref->flags &= ~PREF_USERSET;
 
         if (!(pref->flags & PREF_HAS_DEFAULT)) {
-            PL_DHashTableRemove(&gHashTable, pref_name);
+            PL_DHashTableRemove(gHashTable, pref_name);
         }
 
         pref_DoCallback(pref_name);
         gDirty = true;
     }
     return NS_OK;
 }
 
@@ -642,33 +644,33 @@ pref_ClearUserPref(PLDHashTable *table, 
 
 nsresult
 PREF_ClearAllUserPrefs()
 {
 #ifndef MOZ_B2G
     MOZ_ASSERT(NS_IsMainThread());
 #endif
 
-    if (!gHashTable.IsInitialized())
+    if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     std::vector<std::string> prefStrings;
-    PL_DHashTableEnumerate(&gHashTable, pref_ClearUserPref, static_cast<void*>(&prefStrings));
+    PL_DHashTableEnumerate(gHashTable, pref_ClearUserPref, static_cast<void*>(&prefStrings));
 
     for (std::string& prefString : prefStrings) {
         pref_DoCallback(prefString.c_str());
     }
 
     gDirty = true;
     return NS_OK;
 }
 
 nsresult PREF_LockPref(const char *key, bool lockit)
 {
-    if (!gHashTable.IsInitialized())
+    if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     PrefHashEntry* pref = pref_HashTableLookup(key);
     if (!pref)
         return NS_ERROR_UNEXPECTED;
 
     if (lockit) {
         if (!PREF_IS_LOCKED(pref))
@@ -729,30 +731,30 @@ static void pref_SetValue(PrefValue* exi
 }
 
 PrefHashEntry* pref_HashTableLookup(const void *key)
 {
 #ifndef MOZ_B2G
     MOZ_ASSERT(NS_IsMainThread());
 #endif
 
-    return static_cast<PrefHashEntry*>(PL_DHashTableSearch(&gHashTable, key));
+    return static_cast<PrefHashEntry*>(PL_DHashTableSearch(gHashTable, key));
 }
 
 nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags)
 {
 #ifndef MOZ_B2G
     MOZ_ASSERT(NS_IsMainThread());
 #endif
 
-    if (!gHashTable.IsInitialized())
+    if (!gHashTable)
         return NS_ERROR_OUT_OF_MEMORY;
 
     PrefHashEntry* pref = static_cast<PrefHashEntry*>
-        (PL_DHashTableAdd(&gHashTable, key, fallible));
+        (PL_DHashTableAdd(gHashTable, key, fallible));
 
     if (!pref)
         return NS_ERROR_OUT_OF_MEMORY;
 
     // new entry, better initialize
     if (!pref->key) {
 
         // initialize the pref entry
@@ -834,18 +836,17 @@ pref_SizeOfPrivateData(MallocSizeOf aMal
         n += aMallocSizeOf(node->domain);
     }
     return n;
 }
 
 PrefType
 PREF_GetPrefType(const char *pref_name)
 {
-    if (gHashTable.IsInitialized())
-    {
+    if (gHashTable) {
         PrefHashEntry* pref = pref_HashTableLookup(pref_name);
         if (pref)
         {
             if (pref->flags & PREF_STRING)
                 return PREF_STRING;
             else if (pref->flags & PREF_INT)
                 return PREF_INT;
             else if (pref->flags & PREF_BOOL)
@@ -856,17 +857,17 @@ PREF_GetPrefType(const char *pref_name)
 }
 
 /* -- */
 
 bool
 PREF_PrefIsLocked(const char *pref_name)
 {
     bool result = false;
-    if (gIsAnyPrefLocked && gHashTable.IsInitialized()) {
+    if (gIsAnyPrefLocked && gHashTable) {
         PrefHashEntry* pref = pref_HashTableLookup(pref_name);
         if (pref && PREF_IS_LOCKED(pref))
             result = true;
     }
 
     return result;
 }
 
--- a/modules/libpref/prefapi_private_data.h
+++ b/modules/libpref/prefapi_private_data.h
@@ -5,18 +5,18 @@
 
 /* Data shared between prefapi.c and nsPref.cpp */
 
 #ifndef prefapi_private_data_h
 #define prefapi_private_data_h
 
 #include "mozilla/MemoryReporting.h"
 
-extern PLDHashTable			gHashTable;
-extern bool                 gDirty;
+extern PLDHashTable* gHashTable;
+extern bool gDirty;
 
 namespace mozilla {
 namespace dom {
 class PrefSetting;
 }
 }
 
 enum pref_SaveTypes { SAVE_NONSHARED, SAVE_SHARED, SAVE_ALL, SAVE_ALL_AND_DEFAULTS };
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -35,17 +35,17 @@ enum {
 // the atom table is destroyed.  The structure and value string are allocated
 // as one contiguous block.
 
 struct HttpHeapAtom {
     struct HttpHeapAtom *next;
     char                 value[1];
 };
 
-static PLDHashTable         sAtomTable;
+static PLDHashTable        *sAtomTable;
 static struct HttpHeapAtom *sHeapAtoms = nullptr;
 static Mutex               *sLock = nullptr;
 
 HttpHeapAtom *
 NewHeapAtom(const char *value) {
     int len = strlen(value);
 
     HttpHeapAtom *a =
@@ -89,87 +89,84 @@ static const PLDHashTableOps ops = {
     PL_DHashClearEntryStub,
     nullptr
 };
 
 // We put the atoms in a hash table for speedy lookup.. see ResolveAtom.
 nsresult
 nsHttp::CreateAtomTable()
 {
-    MOZ_ASSERT(!sAtomTable.IsInitialized(), "atom table already initialized");
+    MOZ_ASSERT(!sAtomTable, "atom table already initialized");
 
     if (!sLock) {
         sLock = new Mutex("nsHttp.sLock");
     }
 
     // The initial length for this table is a value greater than the number of
     // known atoms (NUM_HTTP_ATOMS) because we expect to encounter a few random
     // headers right off the bat.
-    PL_DHashTableInit(&sAtomTable, &ops, sizeof(PLDHashEntryStub),
-                      NUM_HTTP_ATOMS + 10);
+    sAtomTable = new PLDHashTable(&ops, sizeof(PLDHashEntryStub),
+                                  NUM_HTTP_ATOMS + 10);
 
     // fill the table with our known atoms
     const char *const atoms[] = {
 #define HTTP_ATOM(_name, _value) nsHttp::_name._val,
 #include "nsHttpAtomList.h"
 #undef HTTP_ATOM
         nullptr
     };
 
     for (int i = 0; atoms[i]; ++i) {
         PLDHashEntryStub *stub = reinterpret_cast<PLDHashEntryStub *>
-            (PL_DHashTableAdd(&sAtomTable, atoms[i], fallible));
+            (PL_DHashTableAdd(sAtomTable, atoms[i], fallible));
         if (!stub)
             return NS_ERROR_OUT_OF_MEMORY;
 
         MOZ_ASSERT(!stub->key, "duplicate static atom");
         stub->key = atoms[i];
     }
 
     return NS_OK;
 }
 
 void
 nsHttp::DestroyAtomTable()
 {
-    if (sAtomTable.IsInitialized()) {
-        PL_DHashTableFinish(&sAtomTable);
-    }
+    delete sAtomTable;
+    sAtomTable = nullptr;
 
     while (sHeapAtoms) {
         HttpHeapAtom *next = sHeapAtoms->next;
         free(sHeapAtoms);
         sHeapAtoms = next;
     }
 
-    if (sLock) {
-        delete sLock;
-        sLock = nullptr;
-    }
+    delete sLock;
+    sLock = nullptr;
 }
 
 Mutex *
 nsHttp::GetLock()
 {
     return sLock;
 }
 
 // this function may be called from multiple threads
 nsHttpAtom
 nsHttp::ResolveAtom(const char *str)
 {
     nsHttpAtom atom = { nullptr };
 
-    if (!str || !sAtomTable.IsInitialized())
+    if (!str || !sAtomTable)
         return atom;
 
     MutexAutoLock lock(*sLock);
 
     PLDHashEntryStub *stub = reinterpret_cast<PLDHashEntryStub *>
-        (PL_DHashTableAdd(&sAtomTable, str, fallible));
+        (PL_DHashTableAdd(sAtomTable, str, fallible));
     if (!stub)
         return atom;  // out of memory
 
     if (stub->key) {
         atom._val = reinterpret_cast<const char *>(stub->key);
         return atom;
     }
 
--- a/parser/htmlparser/nsHTMLEntities.cpp
+++ b/parser/htmlparser/nsHTMLEntities.cpp
@@ -2,18 +2,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsHTMLEntities.h"
 
-
-
 #include "nsString.h"
 #include "nsCRT.h"
 #include "pldhash.h"
 
 using namespace mozilla;
 
 struct EntityNode {
   const char* mStr; // never owns buffer
@@ -62,102 +60,101 @@ static const PLDHashTableOps EntityToUni
 static const PLDHashTableOps UnicodeToEntityOps = {
   hashUnicodeValue,
   matchNodeUnicode,
   PL_DHashMoveEntryStub,
   PL_DHashClearEntryStub,
   nullptr,
 };
 
-static PLDHashTable gEntityToUnicode;
-static PLDHashTable gUnicodeToEntity;
+static PLDHashTable* gEntityToUnicode;
+static PLDHashTable* gUnicodeToEntity;
 static nsrefcnt gTableRefCnt = 0;
 
 #define HTML_ENTITY(_name, _value) { #_name, _value },
 static const EntityNode gEntityArray[] = {
 #include "nsHTMLEntityList.h"
 };
 #undef HTML_ENTITY
 
 #define NS_HTML_ENTITY_COUNT ((int32_t)ArrayLength(gEntityArray))
 
 nsresult
 nsHTMLEntities::AddRefTable(void)
 {
   if (!gTableRefCnt) {
-    PL_DHashTableInit(&gEntityToUnicode, &EntityToUnicodeOps,
-                      sizeof(EntityNodeEntry), NS_HTML_ENTITY_COUNT);
-    PL_DHashTableInit(&gUnicodeToEntity, &UnicodeToEntityOps,
-                      sizeof(EntityNodeEntry), NS_HTML_ENTITY_COUNT);
+    gEntityToUnicode = new PLDHashTable(&EntityToUnicodeOps,
+                                        sizeof(EntityNodeEntry),
+                                        NS_HTML_ENTITY_COUNT);
+    gUnicodeToEntity = new PLDHashTable(&UnicodeToEntityOps,
+                                        sizeof(EntityNodeEntry),
+                                        NS_HTML_ENTITY_COUNT);
     for (const EntityNode *node = gEntityArray,
                  *node_end = ArrayEnd(gEntityArray);
          node < node_end; ++node) {
 
       // add to Entity->Unicode table
       EntityNodeEntry* entry =
         static_cast<EntityNodeEntry*>
-                   (PL_DHashTableAdd(&gEntityToUnicode, node->mStr, fallible));
+                   (PL_DHashTableAdd(gEntityToUnicode, node->mStr, fallible));
       NS_ASSERTION(entry, "Error adding an entry");
       // Prefer earlier entries when we have duplication.
       if (!entry->node)
         entry->node = node;
 
       // add to Unicode->Entity table
       entry = static_cast<EntityNodeEntry*>
-                         (PL_DHashTableAdd(&gUnicodeToEntity,
+                         (PL_DHashTableAdd(gUnicodeToEntity,
                                            NS_INT32_TO_PTR(node->mUnicode),
                                            fallible));
       NS_ASSERTION(entry, "Error adding an entry");
       // Prefer earlier entries when we have duplication.
       if (!entry->node)
         entry->node = node;
     }
 #ifdef DEBUG
-    PL_DHashMarkTableImmutable(&gUnicodeToEntity);
-    PL_DHashMarkTableImmutable(&gEntityToUnicode);
+    PL_DHashMarkTableImmutable(gUnicodeToEntity);
+    PL_DHashMarkTableImmutable(gEntityToUnicode);
 #endif
   }
   ++gTableRefCnt;
   return NS_OK;
 }
 
 void
 nsHTMLEntities::ReleaseTable(void)
 {
   if (--gTableRefCnt != 0)
     return;
 
-  if (gEntityToUnicode.IsInitialized()) {
-    PL_DHashTableFinish(&gEntityToUnicode);
-  }
-  if (gUnicodeToEntity.IsInitialized()) {
-    PL_DHashTableFinish(&gUnicodeToEntity);
-  }
+  delete gEntityToUnicode;
+  delete gUnicodeToEntity;
+  gEntityToUnicode = nullptr;
+  gUnicodeToEntity = nullptr;
 }
 
 int32_t
 nsHTMLEntities::EntityToUnicode(const nsCString& aEntity)
 {
-  NS_ASSERTION(gEntityToUnicode.IsInitialized(),
-               "no lookup table, needs addref");
-  if (!gEntityToUnicode.IsInitialized())
+  NS_ASSERTION(gEntityToUnicode, "no lookup table, needs addref");
+  if (!gEntityToUnicode)
     return -1;
 
     //this little piece of code exists because entities may or may not have the terminating ';'.
     //if we see it, strip if off for this test...
 
     if(';'==aEntity.Last()) {
       nsAutoCString temp(aEntity);
       temp.Truncate(aEntity.Length()-1);
       return EntityToUnicode(temp);
     }
 
   EntityNodeEntry* entry =
     static_cast<EntityNodeEntry*>
-               (PL_DHashTableSearch(&gEntityToUnicode, aEntity.get()));
+               (PL_DHashTableSearch(gEntityToUnicode, aEntity.get()));
 
   return entry ? entry->node->mUnicode : -1;
 }
 
 
 int32_t 
 nsHTMLEntities::EntityToUnicode(const nsAString& aEntity) {
   nsAutoCString theEntity; theEntity.AssignWithConversion(aEntity);
@@ -167,21 +164,20 @@ nsHTMLEntities::EntityToUnicode(const ns
 
   return EntityToUnicode(theEntity);
 }
 
 
 const char*
 nsHTMLEntities::UnicodeToEntity(int32_t aUnicode)
 {
-  NS_ASSERTION(gUnicodeToEntity.IsInitialized(),
-               "no lookup table, needs addref");
+  NS_ASSERTION(gUnicodeToEntity, "no lookup table, needs addref");
   EntityNodeEntry* entry =
     static_cast<EntityNodeEntry*>
-               (PL_DHashTableSearch(&gUnicodeToEntity, NS_INT32_TO_PTR(aUnicode)));
+               (PL_DHashTableSearch(gUnicodeToEntity, NS_INT32_TO_PTR(aUnicode)));
 
   return entry ? entry->node->mStr : nullptr;
 }
 
 #ifdef DEBUG
 #include <stdio.h>
 
 class nsTestEntityTable {
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -36,17 +36,17 @@ using namespace mozilla;
 
 /**
  * The shared hash table for atom lookups.
  *
  * XXX This should be manipulated in a threadsafe way or we should make
  * sure it's only manipulated from the main thread.  Probably the latter
  * is better, since the former would hurt performance.
  */
-static PLDHashTable gAtomTable;
+static PLDHashTable* gAtomTable;
 
 class StaticAtomEntry : public PLDHashEntryHdr
 {
 public:
   typedef const nsAString& KeyType;
   typedef const nsAString* KeyTypePointer;
 
   explicit StaticAtomEntry(KeyTypePointer aKey) {}
@@ -331,29 +331,31 @@ PromoteToPermanent(AtomImpl* aAtom)
 #endif
   aAtom = new (aAtom) PermanentAtomImpl();
 }
 
 void
 NS_PurgeAtomTable()
 {
   delete gStaticAtomTable;
+  gStaticAtomTable = nullptr;
 
-  if (gAtomTable.IsInitialized()) {
+  if (gAtomTable) {
 #ifdef DEBUG
     const char* dumpAtomLeaks = PR_GetEnv("MOZ_DUMP_ATOM_LEAKS");
     if (dumpAtomLeaks && *dumpAtomLeaks) {
       uint32_t leaked = 0;
       printf("*** %d atoms still exist (including permanent):\n",
-             gAtomTable.EntryCount());
-      PL_DHashTableEnumerate(&gAtomTable, DumpAtomLeaks, &leaked);
+             gAtomTable->EntryCount());
+      PL_DHashTableEnumerate(gAtomTable, DumpAtomLeaks, &leaked);
       printf("*** %u non-permanent atoms leaked\n", leaked);
     }
 #endif
-    PL_DHashTableFinish(&gAtomTable);
+    delete gAtomTable;
+    gAtomTable = nullptr;
   }
 }
 
 AtomImpl::AtomImpl(const nsAString& aString, uint32_t aHash)
 {
   mLength = aString.Length();
   nsRefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
   if (buf) {
@@ -392,27 +394,26 @@ AtomImpl::AtomImpl(nsStringBuffer* aStri
   NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
   NS_ASSERTION(aStringBuffer &&
                aStringBuffer->StorageSize() == (mLength + 1) * sizeof(char16_t),
                "correct storage");
 }
 
 AtomImpl::~AtomImpl()
 {
-  NS_PRECONDITION(gAtomTable.IsInitialized(), "uninitialized atom hashtable");
+  MOZ_ASSERT(gAtomTable, "uninitialized atom hashtable");
   // Permanent atoms are removed from the hashtable at shutdown, and we
   // don't want to remove them twice.  See comment above in
   // |AtomTableClearEntry|.
   if (!IsPermanentInDestructor()) {
     AtomTableKey key(mString, mLength, mHash);
-    PL_DHashTableRemove(&gAtomTable, &key);
-    if (gAtomTable.IsInitialized() && gAtomTable.EntryCount() == 0) {
-      PL_DHashTableFinish(&gAtomTable);
-      NS_ASSERTION(gAtomTable.EntryCount() == 0,
-                   "PL_DHashTableFinish changed the entry count");
+    PL_DHashTableRemove(gAtomTable, &key);
+    if (gAtomTable->EntryCount() == 0) {
+      delete gAtomTable;
+      gAtomTable = nullptr;
     }
   }
 
   nsStringBuffer::FromData(mString)->Release();
 }
 
 NS_IMPL_ISUPPORTS(AtomImpl, nsIAtom)
 
@@ -518,58 +519,58 @@ SizeOfAtomTableEntryExcludingThis(PLDHas
   AtomTableEntry* entry = static_cast<AtomTableEntry*>(aHdr);
   return entry->mAtom->SizeOfIncludingThis(aMallocSizeOf);
 }
 
 void
 NS_SizeOfAtomTablesIncludingThis(MallocSizeOf aMallocSizeOf,
                                  size_t* aMain, size_t* aStatic)
 {
-  *aMain = gAtomTable.IsInitialized()
-         ? PL_DHashTableSizeOfExcludingThis(&gAtomTable,
+  *aMain = gAtomTable
+         ? PL_DHashTableSizeOfExcludingThis(gAtomTable,
                                             SizeOfAtomTableEntryExcludingThis,
                                             aMallocSizeOf)
          : 0;
 
   // The atoms in the this table are almost certainly stored in static data, so
   // we don't need a SizeOfEntry function.
   *aStatic = gStaticAtomTable
            ? gStaticAtomTable->SizeOfIncludingThis(nullptr, aMallocSizeOf)
            : 0;
 }
 
 #define ATOM_HASHTABLE_INITIAL_LENGTH  2048
 
 static inline void
 EnsureTableExists()
 {
-  if (!gAtomTable.IsInitialized()) {
-    PL_DHashTableInit(&gAtomTable, &AtomTableOps,
-                      sizeof(AtomTableEntry), ATOM_HASHTABLE_INITIAL_LENGTH);
+  if (!gAtomTable) {
+    gAtomTable = new PLDHashTable(&AtomTableOps, sizeof(AtomTableEntry),
+                                  ATOM_HASHTABLE_INITIAL_LENGTH);
   }
 }
 
 static inline AtomTableEntry*
 GetAtomHashEntry(const char* aString, uint32_t aLength, uint32_t* aHashOut)
 {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   EnsureTableExists();
   AtomTableKey key(aString, aLength, aHashOut);
   // This is an infallible add.
-  return static_cast<AtomTableEntry*>(PL_DHashTableAdd(&gAtomTable, &key));
+  return static_cast<AtomTableEntry*>(PL_DHashTableAdd(gAtomTable, &key));
 }
 
 static inline AtomTableEntry*
 GetAtomHashEntry(const char16_t* aString, uint32_t aLength, uint32_t* aHashOut)
 {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   EnsureTableExists();
   AtomTableKey key(aString, aLength, aHashOut);
   // This is an infallible add.
-  return static_cast<AtomTableEntry*>(PL_DHashTableAdd(&gAtomTable, &key));
+  return static_cast<AtomTableEntry*>(PL_DHashTableAdd(gAtomTable, &key));
 }
 
 class CheckStaticAtomSizes
 {
   CheckStaticAtomSizes()
   {
     static_assert((sizeof(nsFakeStringBuffer<1>().mRefCnt) ==
                    sizeof(nsStringBuffer().mRefCount)) &&
@@ -704,17 +705,18 @@ NS_NewPermanentAtom(const nsAString& aUT
 
   // No need to addref since permanent atoms aren't refcounted anyway
   return atom;
 }
 
 nsrefcnt
 NS_GetNumberOfAtoms(void)
 {
-  return gAtomTable.EntryCount();
+  MOZ_ASSERT(gAtomTable);
+  return gAtomTable->EntryCount();
 }
 
 nsIAtom*
 NS_GetStaticAtom(const nsAString& aUTF16String)
 {
   NS_PRECONDITION(gStaticAtomTable, "Static atom table not created yet.");
   NS_PRECONDITION(gStaticAtomTableSealed, "Static atom table not sealed yet.");
   StaticAtomEntry* entry = gStaticAtomTable->GetEntry(aUTF16String);