mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
Bug 865320 - Move the logic for deciding when to doing a merging CC into the cycle collector. r=smaug
This commit is contained in:
parent
ad94e2a5c9
commit
8765722e58
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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?
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user