Index: lib/matcher.js
===================================================================
--- a/lib/matcher.js
+++ b/lib/matcher.js
@@ -235,30 +235,50 @@
     {
       let map = this._filterMapsByType.get(type);
       if (!map)
         this._filterMapsByType.set(type, map = new Map());
 
       addFilterByKeyword(filter, keyword, map);
     }
 
+    let {domains} = filter;
+
     let filtersByDomain = this._filterDomainMapsByKeyword.get(keyword);
     if (!filtersByDomain)
-      this._filterDomainMapsByKeyword.set(keyword, filtersByDomain = new Map());
+    {
+      // In most cases, there is only one pure generic filter to a keyword.
+      // Instead of Map { "foo" => Map { "" => Map { filter => true } } }, we
+      // can just reduce it to Map { "foo" => filter } and save a lot of
+      // memory.
+      if (!domains)
+      {
+        this._filterDomainMapsByKeyword.set(keyword, filter);
+        return;
+      }
 
-    for (let [domain, include] of filter.domains || defaultDomains)
+      filtersByDomain = new Map();
+      this._filterDomainMapsByKeyword.set(keyword, filtersByDomain);
+    }
+    else if (!(filtersByDomain instanceof Map))
+    {
+      filtersByDomain = new Map([["", filtersByDomain]]);
+      this._filterDomainMapsByKeyword.set(keyword, filtersByDomain);
+    }
+
+    for (let [domain, include] of domains || defaultDomains)
     {
       if (!include && domain == "")
         continue;
 
       let map = filtersByDomain.get(domain);
       if (!map)
       {
         filtersByDomain.set(domain, include ? filter :
-                              map = new Map([[filter, false]]));
+                                      map = new Map([[filter, false]]));
       }
       else if (map.size == 1 && !(map instanceof Map))
       {
         if (filter != map)
         {
           filtersByDomain.set(domain, new Map([[map, true],
                                                [filter, include]]));
         }
@@ -296,35 +316,74 @@
       let map = this._filterMapsByType.get(type);
       if (map)
         removeFilterByKeyword(filter, keyword, map);
     }
 
     let filtersByDomain = this._filterDomainMapsByKeyword.get(keyword);
     if (filtersByDomain)
     {
+      // Because of the memory optimization in the add function, most of the
+      // time this will be a filter rather than a map.
+      if (!(filtersByDomain instanceof Map))
+      {
+        this._filterDomainMapsByKeyword.delete(keyword);
+        return;
+      }
+
       let domains = filter.domains || defaultDomains;
       for (let domain of domains.keys())
       {
         let map = filtersByDomain.get(domain);
         if (map)
         {
           if (map.size > 1 || map instanceof Map)
           {
             map.delete(filter);
 
             if (map.size == 0)
+            {
               filtersByDomain.delete(domain);
+            }
+            else if (map.size == 1)
+            {
+              for (let [lastFilter, include] of map)
+              {
+                // Reduce Map { "example.com" => Map { filter => true } } to
+                // Map { "example.com" => filter }
+                if (include)
+                  filtersByDomain.set(domain, lastFilter);
+
+                break;
+              }
+            }
           }
           else if (filter == map)
           {
             filtersByDomain.delete(domain);
           }
         }
       }
+
+      if (filtersByDomain.size == 0)
+      {
+        this._filterDomainMapsByKeyword.delete(keyword);
+      }
+      else if (filtersByDomain.size == 1)
+      {
+        for (let [lastDomain, map] of filtersByDomain)
+        {
+          // Reduce Map { "foo" => Map { "" => filter } } to
+          // Map { "foo" => filter }
+          if (lastDomain == "" && !(map instanceof Map))
+            this._filterDomainMapsByKeyword.set(keyword, map);
+
+          break;
+        }
+      }
     }
   }
 
   /**
    * Chooses a keyword to be associated with the filter
    * @param {Filter} filter
    * @returns {string} keyword or an empty string if no keyword could be found
    * @protected
@@ -419,18 +478,31 @@
   }
 
   _checkEntryMatchByDomain(keyword, location, typeMask, docDomain, thirdParty,
                            sitekey, specificOnly, collection)
   {
     let filtersByDomain = this._filterDomainMapsByKeyword.get(keyword);
     if (filtersByDomain)
     {
-      // The code in this block is similar to the generateStyleSheetForDomain
-      // function in lib/elemHide.js.
+      // Because of the memory optimization in the add function, most of the
+      // time this will be a filter rather than a map.
+      if (!(filtersByDomain instanceof Map))
+      {
+        if (filtersByDomain.matchesWithoutDomain(location, typeMask,
+                                                 thirdParty, sitekey))
+        {
+          if (!collection)
+            return filtersByDomain;
+
+          collection.push(filtersByDomain);
+        }
+
+        return null;
+      }
 
       if (docDomain)
       {
         if (docDomain[docDomain.length - 1] == ".")
           docDomain = docDomain.replace(/\.+$/, "");
 
         docDomain = docDomain.toLowerCase();
       }
