Bug 699279 - Run GC_SHRINK collection cycle when under memory pressure; r=mrbkap
authorTerrence Cole <terrence@mozilla.com>
Wed, 09 Nov 2011 18:14:11 -0800
changeset 80897 5a2fc54f90a4d5b7aab199e87ea31713c4796664
parent 80896 6cf9f0eed66022437f171ba26fde3cb1fee7c91d
child 80898 d8aedf3e698cc675fc5b6469c89354b9f74eaaea
push idunknown
push userunknown
push dateunknown
reviewersmrbkap
bugs699279
milestone11.0a1
Bug 699279 - Run GC_SHRINK collection cycle when under memory pressure; r=mrbkap GC_SHRINK is a fairly new type of GC that does more aggressive cleanups than a normal GC. This patch makes the browser run the GC in this mode when under memory pressure, or when the user pushes the Minimize Memory Usage button when on the about:memory page.
dom/base/nsJSEnvironment.cpp
dom/base/nsJSEnvironment.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/xpconnect/idl/nsIXPConnect.idl
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
xpcom/base/nsCycleCollector.h
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -189,17 +189,17 @@ public:
 
 NS_IMPL_ISUPPORTS1(nsMemoryPressureObserver, nsIObserver)
 
 NS_IMETHODIMP
 nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
                                   const PRUnichar* aData)
 {
   if (sGCOnMemoryPressure) {
-    nsJSContext::GarbageCollectNow();
+    nsJSContext::GarbageCollectNow(true);
     nsJSContext::CycleCollectNow();
   }
   return NS_OK;
 }
 
 class nsRootedJSValueArray {
 public:
   explicit nsRootedJSValueArray(JSContext *cx) : avr(cx, vals.Length(), vals.Elements()) {}
@@ -3182,33 +3182,33 @@ nsJSContext::ScriptExecuted()
 {
   ScriptEvaluated(!::JS_IsRunning(mContext));
 
   return NS_OK;
 }
 
 //static
 void
-nsJSContext::GarbageCollectNow()
+nsJSContext::GarbageCollectNow(bool shrinkingGC)
 {
   NS_TIME_FUNCTION_MIN(1.0);
 
   KillGCTimer();
 
   // Reset sPendingLoadCount in case the timer that fired was a
   // timer we scheduled due to a normal GC timer firing while
   // documents were loading. If this happens we're waiting for a
   // document that is taking a long time to load, and we effectively
   // ignore the fact that the currently loading documents are still
   // loading and move on as if they weren't.
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
 
   if (nsContentUtils::XPConnect()) {
-    nsContentUtils::XPConnect()->GarbageCollect();
+    nsContentUtils::XPConnect()->GarbageCollect(shrinkingGC);
   }
 }
 
 //Static
 void
 nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener)
 {
   if (!NS_IsMainThread()) {
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -177,17 +177,17 @@ public:
   virtual void EnterModalState();
   virtual void LeaveModalState();
 
   NS_DECL_NSIXPCSCRIPTNOTIFY
 
   static void LoadStart();
   static void LoadEnd();
 
-  static void GarbageCollectNow();
+  static void GarbageCollectNow(bool shrinkingGC = false);
   static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull);
 
   static void PokeGC();
   static void KillGCTimer();
 
   static void PokeCC();
   static void MaybePokeCC();
   static void KillCCTimer();
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -127,16 +127,22 @@ JS_NewObjectWithUniqueType(JSContext *cx
 
 JS_FRIEND_API(uint32)
 JS_ObjectCountDynamicSlots(JSObject *obj)
 {
     if (obj->hasSlotsArray())
         return obj->numDynamicSlots(obj->numSlots());
     return 0;
 }
+    
+JS_PUBLIC_API(void)
+JS_ShrinkingGC(JSContext *cx)
+{
+    js_GC(cx, NULL, GC_SHRINK, gcstats::PUBLIC_API);
+}
 
 JS_FRIEND_API(JSPrincipals *)
 JS_GetCompartmentPrincipals(JSCompartment *compartment)
 {
     return compartment->principals;
 }
 
 JS_FRIEND_API(JSBool)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -65,16 +65,19 @@ extern JS_FRIEND_API(JSBool)
 JS_SplicePrototype(JSContext *cx, JSObject *obj, JSObject *proto);
 
 extern JS_FRIEND_API(JSObject *)
 JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);
 
 extern JS_FRIEND_API(uint32)
 JS_ObjectCountDynamicSlots(JSObject *obj);
 
+extern JS_FRIEND_API(void)
+JS_ShrinkingGC(JSContext *cx);
+
 extern JS_FRIEND_API(size_t)
 JS_GetE4XObjectsCreated(JSContext *cx);
 
 extern JS_FRIEND_API(size_t)
 JS_SetProtoCalled(JSContext *cx);
 
 extern JS_FRIEND_API(size_t)
 JS_GetCustomIteratorCount(JSContext *cx);
--- a/js/xpconnect/idl/nsIXPConnect.idl
+++ b/js/xpconnect/idl/nsIXPConnect.idl
@@ -387,17 +387,17 @@ interface nsIXPCFunctionThisTranslator :
 %{ C++
 // For use with the service manager
 // {CB6593E0-F9B2-11d2-BDD6-000064657374}
 #define NS_XPCONNECT_CID \
 { 0xcb6593e0, 0xf9b2, 0x11d2, \
     { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } }
 %}
 
-[uuid(29b63029-0868-4344-b0ca-d93256ee7c78)]
+[uuid(07661008-5505-4784-a612-89f7dc2144da)]
 interface nsIXPConnect : nsISupports
 {
 %{ C++
   NS_DEFINE_STATIC_CID_ACCESSOR(NS_XPCONNECT_CID)
 %}
 
     /**
      * Initializes classes on a global object that has already been created.
@@ -730,17 +730,17 @@ interface nsIXPConnect : nsISupports
      * MOZ_REPORT_ALL_JS_EXCEPTIONS environment variable will override the value
      * passed here.
      */
     void setReportAllJSExceptions(in boolean reportAllJSExceptions);
 
     /**
      * Trigger a JS garbage collection.
      */
-    void GarbageCollect();
+    void GarbageCollect(in boolean shrinkingGC);
 
     /**
      * Define quick stubs on the given object, @a proto.
      *
      * @param cx
      *     A context.  Requires request.
      * @param proto
      *     The (newly created) prototype object for a DOM class.  The JS half
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -338,17 +338,17 @@ nsXPConnect::GetInfoForName(const char *
 
 bool
 nsXPConnect::NeedCollect()
 {
     return !!mNeedGCBeforeCC;
 }
 
 void
-nsXPConnect::Collect()
+nsXPConnect::Collect(bool shrinkingGC)
 {
     // We're dividing JS objects into 2 categories:
     //
     // 1. "real" roots, held by the JS engine itself or rooted through the root
     //    and lock JS APIs. Roots from this category are considered black in the
     //    cycle collector, any cycle they participate in is uncollectable.
     //
     // 2. roots held by C++ objects that participate in cycle collection,
@@ -401,25 +401,28 @@ nsXPConnect::Collect()
     // cycle collection. So to compensate for JS_BeginRequest in
     // XPCCallContext::Init we disable the conservative scanner if that call
     // has started the request on this thread.
     js::ThreadData &threadData = cx->thread()->data;
     JS_ASSERT(threadData.requestDepth >= 1);
     JS_ASSERT(!threadData.conservativeGC.requestThreshold);
     if (threadData.requestDepth == 1)
         threadData.conservativeGC.requestThreshold = 1;
-    JS_GC(cx);
+    if (shrinkingGC)
+        JS_ShrinkingGC(cx);
+    else
+        JS_GC(cx);
     if (threadData.requestDepth == 1)
         threadData.conservativeGC.requestThreshold = 0;
 }
 
 NS_IMETHODIMP
-nsXPConnect::GarbageCollect()
+nsXPConnect::GarbageCollect(bool shrinkingGC)
 {
-    Collect();
+    Collect(shrinkingGC);
     return NS_OK;
 }
 
 #ifdef DEBUG_CC
 struct NoteJSRootTracer : public JSTracer
 {
     NoteJSRootTracer(PLDHashTable *aObjects,
                      nsCycleCollectionTraversalCallback& cb)
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -561,17 +561,17 @@ public:
     virtual void NotifyLeaveCycleCollectionThread();
     virtual void NotifyEnterMainThread();
     virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
                                           bool explainExpectedLiveGarbage);
     virtual nsresult FinishTraverse();
     virtual nsresult FinishCycleCollection();
     virtual nsCycleCollectionParticipant *ToParticipant(void *p);
     virtual bool NeedCollect();
-    virtual void Collect();
+    virtual void Collect(bool shrinkingGC=false);
 #ifdef DEBUG_CC
     virtual void PrintAllReferencesTo(void *p);
 #endif
 
     XPCCallContext *GetCycleCollectionContext()
     {
         return mCycleCollectionContext;
     }
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -87,17 +87,17 @@ struct nsCycleCollectionJSRuntime : publ
     /**
      * Should we force a JavaScript GC before a CC?
      */
     virtual bool NeedCollect() = 0;
 
     /**
      * Runs the JavaScript GC.
      */
-    virtual void Collect() = 0;
+    virtual void Collect(bool shrinkingGC = false) = 0;
 };
 
 #ifdef DEBUG
 void nsCycleCollector_DEBUG_shouldBeFreed(nsISupports *n);
 void nsCycleCollector_DEBUG_wasFreed(nsISupports *n);
 #endif
 
 // Helpers for interacting with language-identified scripts