Bug 865320 - Move the logic for deciding when to doing a merging CC into the cycle collector. r=smaug

This commit is contained in:
Andrew McCreight 2013-04-29 16:41:41 -07:00
parent ad94e2a5c9
commit 8765722e58
7 changed files with 117 additions and 93 deletions

View File

@ -2544,72 +2544,6 @@ nsJSContext::ShrinkGCBuffersNow()
JS::ShrinkGCBuffers(nsJSRuntime::sRuntime);
}
// Return true if there exists a JSContext with a default global whose current
// inner is gray. The intent is to look for JS Object windows. We don't merge
// system compartments, so we don't use them to trigger merging CCs.
static bool
AnyGrayCurrentContentInnerWindows()
{
if (!nsJSRuntime::sRuntime) {
return false;
}
JSContext *iter = nullptr;
JSContext *cx;
while ((cx = JS_ContextIterator(nsJSRuntime::sRuntime, &iter))) {
// Skip anything without an nsIScriptContext, as well as any scx whose
// NativeGlobal() is not an outer window (this happens with XUL Prototype
// compilation scopes, for example, which we're not interested in).
nsIScriptContext *scx = GetScriptContextFromJSContext(cx);
JS::RootedObject global(cx, scx ? scx->GetNativeGlobal() : nullptr);
if (!global || !js::GetObjectParent(global)) {
continue;
}
// Grab the inner from the outer.
global = JS_ObjectToInnerObject(cx, global);
MOZ_ASSERT(!js::GetObjectParent(global));
if (JS::GCThingIsMarkedGray(global) &&
!js::IsSystemCompartment(js::GetObjectCompartment(global))) {
return true;
}
}
return false;
}
static bool
DoMergingCC(bool aForced)
{
// Don't merge too many times in a row, and do at least a minimum
// number of unmerged CCs in a row.
static const int32_t kMinConsecutiveUnmerged = 3;
static const int32_t kMaxConsecutiveMerged = 3;
static int32_t sUnmergedNeeded = 0;
static int32_t sMergedInARow = 0;
MOZ_ASSERT(0 <= sUnmergedNeeded && sUnmergedNeeded <= kMinConsecutiveUnmerged);
MOZ_ASSERT(0 <= sMergedInARow && sMergedInARow <= kMaxConsecutiveMerged);
if (sMergedInARow == kMaxConsecutiveMerged) {
MOZ_ASSERT(sUnmergedNeeded == 0);
sUnmergedNeeded = kMinConsecutiveUnmerged;
}
if (sUnmergedNeeded > 0) {
sUnmergedNeeded--;
sMergedInARow = 0;
return false;
}
if (!aForced && AnyGrayCurrentContentInnerWindows()) {
sMergedInARow++;
return true;
} else {
sMergedInARow = 0;
return false;
}
}
static void
FinishAnyIncrementalGC()
{
@ -2652,7 +2586,7 @@ TimeBetween(PRTime start, PRTime end)
void
nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
int32_t aExtraForgetSkippableCalls,
bool aForced)
bool aManuallyTriggered)
{
if (!NS_IsMainThread()) {
return;
@ -2691,9 +2625,8 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
uint32_t skippableDuration = TimeBetween(endGCTime, endSkippableTime);
// Prepare to actually run the CC.
bool mergingCC = DoMergingCC(aForced);
nsCycleCollectorResults ccResults;
nsCycleCollector_collect(mergingCC, &ccResults, aListener);
nsCycleCollector_collect(aManuallyTriggered, &ccResults, aListener);
sCCollectedWaitingForGC += ccResults.mFreedRefCounted + ccResults.mFreedGCed;
// If we collected a substantial amount of cycles, poke the GC since more objects
@ -2728,7 +2661,7 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
if (sPostGCEventsToConsole) {
nsCString mergeMsg;
if (mergingCC) {
if (ccResults.mMergedZones) {
mergeMsg.AssignLiteral(" merged");
}

View File

@ -129,7 +129,7 @@ public:
// called even if the previous collection was GC.
static void CycleCollectNow(nsICycleCollectorListener *aListener = nullptr,
int32_t aExtraForgetSkippableCalls = 0,
bool aForced = true);
bool aManuallyTriggered = true);
static void PokeGC(JS::gcreason::Reason aReason, int aDelay = 0);
static void KillGCTimer();

View File

@ -22,6 +22,7 @@
#include "nsIURI.h"
#include "nsJSEnvironment.h"
#include "nsThreadUtils.h"
#include "nsDOMJSUtils.h"
#include "XrayWrapper.h"
#include "WrapperFactory.h"
@ -535,6 +536,36 @@ nsXPConnect::NotifyEnterMainThread()
JS_SetRuntimeThread(mRuntime->GetJSRuntime());
}
/*
* Return true if there exists a JSContext with a default global whose current
* inner is gray. The intent is to look for JS Object windows. We don't merge
* system compartments, so we don't use them to trigger merging CCs.
*/
bool
nsXPConnect::UsefulToMergeZones()
{
JSContext *iter = nullptr;
JSContext *cx;
while ((cx = JS_ContextIterator(GetRuntime()->GetJSRuntime(), &iter))) {
// Skip anything without an nsIScriptContext, as well as any scx whose
// NativeGlobal() is not an outer window (this happens with XUL Prototype
// compilation scopes, for example, which we're not interested in).
nsIScriptContext *scx = GetScriptContextFromJSContext(cx);
JS::RootedObject global(cx, scx ? scx->GetNativeGlobal() : nullptr);
if (!global || !js::GetObjectParent(global)) {
continue;
}
// Grab the inner from the outer.
global = JS_ObjectToInnerObject(cx, global);
MOZ_ASSERT(!js::GetObjectParent(global));
if (JS::GCThingIsMarkedGray(global) &&
!js::IsSystemCompartment(js::GetObjectCompartment(global))) {
return true;
}
}
return false;
}
class nsXPConnectParticipant: public nsCycleCollectionParticipant
{
public:

View File

@ -524,6 +524,7 @@ public:
virtual void NotifyEnterMainThread();
virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb);
virtual nsCycleCollectionParticipant *GetParticipant();
virtual bool UsefulToMergeZones();
virtual void FixWeakMappingGrayBits();
virtual bool NeedCollect();
virtual void Collect(uint32_t reason);

View File

@ -917,6 +917,12 @@ nsPurpleBuffer::SelectPointers(GCGraphBuilder &aBuilder)
}
}
enum ccType {
ScheduledCC, /* Automatically triggered, based on time or the purple buffer. */
ManualCC, /* Explicitly triggered. */
ShutdownCC /* Shutdown CC, used for finding leaks. */
};
class nsCycleCollector;
class nsCycleCollectorRunner : public nsRunnable
@ -931,7 +937,7 @@ class nsCycleCollectorRunner : public nsRunnable
bool mRunning;
bool mShutdown;
bool mCollected;
bool mMergeZones;
ccType mCCType;
public:
nsCycleCollectorRunner(nsCycleCollector *collector,
@ -945,7 +951,7 @@ public:
mRunning(false),
mShutdown(false),
mCollected(false),
mMergeZones(false)
mCCType(ScheduledCC)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
}
@ -960,7 +966,7 @@ public:
return NS_NewThread(getter_AddRefs(mThread), this);
}
void Collect(bool aMergeZones,
void Collect(ccType aCCType,
nsCycleCollectorResults *aResults,
nsICycleCollectorListener *aListener);
@ -1029,6 +1035,9 @@ private:
nsPurpleBuffer mPurpleBuf;
uint32_t mUnmergedNeeded;
uint32_t mMergedInARow;
public:
void RegisterJSRuntime(nsCycleCollectionJSRuntime *aJSRuntime);
void ForgetJSRuntime();
@ -1070,13 +1079,13 @@ public:
void CheckThreadSafety();
private:
void MainThreadCollect(bool aMergeZones,
void MainThreadCollect(ccType aCCType,
nsCycleCollectorResults *aResults,
uint32_t aTryCollections,
nsICycleCollectorListener *aListener);
public:
void Collect(bool aMergeZones,
void Collect(ccType aCCType,
nsCycleCollectorResults *aResults,
nsICycleCollectorListener *aListener);
@ -1084,10 +1093,11 @@ public:
bool PrepareForCollection(nsCycleCollectorResults *aResults,
nsTArray<PtrInfo*> *aWhiteNodes);
void FixGrayBits(bool aForceGC);
bool ShouldMergeZones(ccType aCCType);
void CleanupAfterCollection();
// Start and finish an individual collection.
bool BeginCollection(bool aMergeZones, nsICycleCollectorListener *aListener);
bool BeginCollection(ccType aCCType, nsICycleCollectorListener *aListener);
bool FinishCollection(nsICycleCollectorListener *aListener);
uint32_t SuspectedCount();
@ -1144,7 +1154,7 @@ nsCycleCollectorRunner::Run()
}
mCollector->JSRuntime()->NotifyEnterCycleCollectionThread();
mCollected = mCollector->BeginCollection(mMergeZones, mListener);
mCollected = mCollector->BeginCollection(mCCType, mListener);
mCollector->JSRuntime()->NotifyLeaveCycleCollectionThread();
mReply.Notify();
@ -1154,7 +1164,7 @@ nsCycleCollectorRunner::Run()
}
void
nsCycleCollectorRunner::Collect(bool aMergeZones,
nsCycleCollectorRunner::Collect(ccType aCCType,
nsCycleCollectorResults *aResults,
nsICycleCollectorListener *aListener)
{
@ -1181,7 +1191,7 @@ nsCycleCollectorRunner::Collect(bool aMergeZones,
if (aListener && NS_FAILED(aListener->Begin()))
aListener = nullptr;
mListener = aListener;
mMergeZones = aMergeZones;
mCCType = aCCType;
if (mModel == CCWithTraverseThread &&
mCollector->JSRuntime()->NotifyLeaveMainThread()) {
@ -1189,7 +1199,7 @@ nsCycleCollectorRunner::Collect(bool aMergeZones,
mReply.Wait();
mCollector->JSRuntime()->NotifyEnterMainThread();
} else {
mCollected = mCollector->BeginCollection(aMergeZones, mListener);
mCollected = mCollector->BeginCollection(aCCType, mListener);
}
mListener = nullptr;
@ -2521,7 +2531,9 @@ nsCycleCollector::nsCycleCollector(CCThreadingModel aModel) :
mBeforeUnlinkCB(nullptr),
mForgetSkippableCB(nullptr),
mReporter(nullptr),
mPurpleBuf(mParams)
mPurpleBuf(mParams),
mUnmergedNeeded(0),
mMergedInARow(0)
{
nsRefPtr<nsCycleCollectorRunner> runner =
new nsCycleCollectorRunner(this, aModel);
@ -2737,7 +2749,7 @@ nsCycleCollector::CleanupAfterCollection()
}
void
nsCycleCollector::MainThreadCollect(bool aMergeZones,
nsCycleCollector::MainThreadCollect(ccType aCCType,
nsCycleCollectorResults *aResults,
uint32_t aTryCollections,
nsICycleCollectorListener *aListener)
@ -2753,7 +2765,7 @@ nsCycleCollector::MainThreadCollect(bool aMergeZones,
FixGrayBits(true);
if (aListener && NS_FAILED(aListener->Begin()))
aListener = nullptr;
if (!(BeginCollection(aMergeZones, aListener) &&
if (!(BeginCollection(aCCType, aListener) &&
FinishCollection(aListener)))
break;
@ -2764,15 +2776,50 @@ nsCycleCollector::MainThreadCollect(bool aMergeZones,
}
void
nsCycleCollector::Collect(bool aMergeZones,
nsCycleCollector::Collect(ccType aCCType,
nsCycleCollectorResults *aResults,
nsICycleCollectorListener *aListener)
{
mRunner->Collect(aMergeZones, aResults, aListener);
mRunner->Collect(aCCType, aResults, aListener);
}
// Don't merge too many times in a row, and do at least a minimum
// number of unmerged CCs in a row.
static const uint32_t kMinConsecutiveUnmerged = 3;
static const uint32_t kMaxConsecutiveMerged = 3;
bool
nsCycleCollector::ShouldMergeZones(ccType aCCType)
{
if (!mJSRuntime) {
return false;
}
MOZ_ASSERT(mUnmergedNeeded <= kMinConsecutiveUnmerged);
MOZ_ASSERT(mMergedInARow <= kMaxConsecutiveMerged);
if (mMergedInARow == kMaxConsecutiveMerged) {
MOZ_ASSERT(mUnmergedNeeded == 0);
mUnmergedNeeded = kMinConsecutiveUnmerged;
}
if (mUnmergedNeeded > 0) {
mUnmergedNeeded--;
mMergedInARow = 0;
return false;
}
if (aCCType == ScheduledCC && mJSRuntime->UsefulToMergeZones()) {
mMergedInARow++;
return true;
} else {
mMergedInARow = 0;
return false;
}
}
bool
nsCycleCollector::BeginCollection(bool aMergeZones,
nsCycleCollector::BeginCollection(ccType aCCType,
nsICycleCollectorListener *aListener)
{
// aListener should be Begin()'d before this
@ -2781,8 +2828,13 @@ nsCycleCollector::BeginCollection(bool aMergeZones,
if (mParams.mDoNothing)
return false;
bool mergeZones = ShouldMergeZones(aCCType);
if (mResults) {
mResults->mMergedZones = mergeZones;
}
GCGraphBuilder builder(this, mGraph, mJSRuntime, aListener,
aMergeZones);
mergeZones);
if (!builder.Initialized())
return false;
@ -2866,7 +2918,7 @@ nsCycleCollector::Shutdown()
listener->SetAllTraces();
}
}
MainThreadCollect(false, nullptr, SHUTDOWN_COLLECTIONS(mParams),
MainThreadCollect(ShutdownCC, nullptr, SHUTDOWN_COLLECTIONS(mParams),
listener);
}
@ -3029,7 +3081,7 @@ nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes)
}
void
nsCycleCollector_collect(bool aMergeZones,
nsCycleCollector_collect(bool aManuallyTriggered,
nsCycleCollectorResults *aResults,
nsICycleCollectorListener *aListener)
{
@ -3045,7 +3097,7 @@ nsCycleCollector_collect(bool aMergeZones,
listener = new nsCycleCollectorLogger();
}
collector->Collect(aMergeZones, aResults, listener);
collector->Collect(aManuallyTriggered ? ManualCC : ScheduledCC, aResults, listener);
}
void

View File

@ -15,9 +15,11 @@ class nsCycleCollectorResults
{
public:
nsCycleCollectorResults() :
mForcedGC(false), mVisitedRefCounted(0), mVisitedGCed(0),
mForcedGC(false), mMergedZones(false),
mVisitedRefCounted(0), mVisitedGCed(0),
mFreedRefCounted(0), mFreedGCed(0) {}
bool mForcedGC;
bool mMergedZones;
uint32_t mVisitedRefCounted;
uint32_t mVisitedGCed;
uint32_t mFreedRefCounted;
@ -41,7 +43,7 @@ void nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB)
void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false);
void nsCycleCollector_collect(bool aMergeCompartments,
void nsCycleCollector_collect(bool aManuallyTriggered,
nsCycleCollectorResults *aResults,
nsICycleCollectorListener *aListener);
uint32_t nsCycleCollector_suspectedCount();

View File

@ -31,6 +31,11 @@ struct nsCycleCollectionJSRuntime
*/
virtual void FixWeakMappingGrayBits() = 0;
/**
* Return true if merging content zones may greatly reduce the size of the CC graph.
*/
virtual bool UsefulToMergeZones() = 0;
/**
* Should we force a JavaScript GC before a CC?
*/