Start incremental marking on idle notification.

BUG=v8:1458
TEST=cctest/test-api/IdleNotification*

Review URL: http://codereview.chromium.org/8519002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10093 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
ulan@chromium.org 2011-11-30 11:13:36 +00:00
parent e2f2c77e6f
commit 8dc728126e
10 changed files with 223 additions and 14 deletions

View File

@ -3194,8 +3194,12 @@ class V8EXPORT V8 {
* Returns true if the embedder should stop calling IdleNotification
* until real work has been done. This indicates that V8 has done
* as much cleanup as it will be able to do.
*
* The hint argument specifies the amount of work to be done in the function
* on scale from 1 to 1000. There is no guarantee that the actual work will
* match the hint.
*/
static bool IdleNotification();
static bool IdleNotification(int hint = 1000);
/**
* Optional notification that the system is running low on memory.

View File

@ -4020,12 +4020,12 @@ void v8::V8::GetHeapStatistics(HeapStatistics* heap_statistics) {
}
bool v8::V8::IdleNotification() {
bool v8::V8::IdleNotification(int hint) {
// Returning true tells the caller that it need not
// continue to call IdleNotification.
i::Isolate* isolate = i::Isolate::Current();
if (isolate == NULL || !isolate->IsInitialized()) return true;
return i::V8::IdleNotification();
return i::V8::IdleNotification(hint);
}

View File

@ -144,6 +144,11 @@ Heap::Heap()
number_idle_notifications_(0),
last_idle_notification_gc_count_(0),
last_idle_notification_gc_count_init_(false),
idle_notification_will_schedule_next_gc_(false),
mark_sweeps_since_idle_round_started_(0),
ms_count_at_last_idle_notification_(0),
gc_count_at_last_idle_gc_(0),
scavenges_since_last_idle_round_(kIdleScavengeThreshold),
promotion_queue_(this),
configured_(false),
chunks_queued_for_free_(NULL) {
@ -1081,8 +1086,7 @@ void Heap::Scavenge() {
incremental_marking()->PrepareForScavenge();
old_pointer_space()->AdvanceSweeper(new_space_.Size());
old_data_space()->AdvanceSweeper(new_space_.Size());
AdvanceSweepers(new_space_.Size());
// Flip the semispaces. After flipping, to space is empty, from space has
// live objects.
@ -1171,6 +1175,8 @@ void Heap::Scavenge() {
LOG(isolate_, ResourceEvent("scavenge", "end"));
gc_state_ = NOT_IN_GC;
scavenges_since_last_idle_round_++;
}
@ -4534,7 +4540,79 @@ void Heap::EnsureHeapIsIterable() {
}
bool Heap::IdleNotification() {
bool Heap::IdleNotification(int hint) {
if (!FLAG_incremental_marking || FLAG_expose_gc || Serializer::enabled()) {
return hint < 1000 ? true : IdleGlobalGC();
}
// By doing small chunks of GC work in each IdleNotification,
// perform a round of incremental GCs and after that wait until
// the mutator creates enough garbage to justify a new round.
// An incremental GC progresses as follows:
// 1. many incremental marking steps,
// 2. one old space mark-sweep-compact,
// 3. many lazy sweep steps.
// Use mark-sweep-compact events to count incremental GCs in a round.
intptr_t size_factor = Min(Max(hint, 30), 1000) / 10;
// The size factor is in range [3..100].
intptr_t step_size = size_factor * IncrementalMarking::kAllocatedThreshold;
if (incremental_marking()->IsStopped()) {
if (!IsSweepingComplete() && !AdvanceSweepers(step_size)) {
return false;
}
}
if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
if (EnoughGarbageSinceLastIdleRound()) {
StartIdleRound();
} else {
return true;
}
}
int new_mark_sweeps = ms_count_ - ms_count_at_last_idle_notification_;
mark_sweeps_since_idle_round_started_ += new_mark_sweeps;
ms_count_at_last_idle_notification_ = ms_count_;
if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
FinishIdleRound();
return true;
}
if (incremental_marking()->IsStopped()) {
if (hint < 1000 && !WorthStartingGCWhenIdle()) {
FinishIdleRound();
return true;
}
incremental_marking()->Start();
}
// This flag prevents incremental marking from requesting GC via stack guard
idle_notification_will_schedule_next_gc_ = true;
incremental_marking()->Step(step_size);
idle_notification_will_schedule_next_gc_ = false;
if (incremental_marking()->IsComplete()) {
bool uncommit = false;
if (gc_count_at_last_idle_gc_ == gc_count_) {
// No GC since the last full GC, the mutator is probably not active.
isolate_->compilation_cache()->Clear();
uncommit = true;
}
CollectAllGarbage(kNoGCFlags);
gc_count_at_last_idle_gc_ = gc_count_;
if (uncommit) {
new_space_.Shrink();
UncommitFromSpace();
}
}
return false;
}
bool Heap::IdleGlobalGC() {
static const int kIdlesBeforeScavenge = 4;
static const int kIdlesBeforeMarkSweep = 7;
static const int kIdlesBeforeMarkCompact = 8;

View File

@ -1331,8 +1331,8 @@ class Heap {
return Min(limit, halfway_to_the_max);
}
// Can be called when the embedding application is idle.
bool IdleNotification();
// Implements the corresponding V8 API function.
bool IdleNotification(int hint);
// Declare all the root indices.
enum RootListIndex {
@ -1455,6 +1455,17 @@ class Heap {
return &incremental_marking_;
}
bool IsSweepingComplete() {
return old_data_space()->IsSweepingComplete() &&
old_pointer_space()->IsSweepingComplete();
}
bool AdvanceSweepers(int step_size) {
bool sweeping_complete = old_data_space()->AdvanceSweeper(step_size);
sweeping_complete &= old_pointer_space()->AdvanceSweeper(step_size);
return sweeping_complete;
}
ExternalStringTable* external_string_table() {
return &external_string_table_;
}
@ -1490,6 +1501,10 @@ class Heap {
// The roots that have an index less than this are always in old space.
static const int kOldSpaceRoots = 0x20;
bool idle_notification_will_schedule_next_gc() {
return idle_notification_will_schedule_next_gc_;
}
private:
Heap();
@ -1823,6 +1838,30 @@ class Heap {
void SelectScavengingVisitorsTable();
void StartIdleRound() {
mark_sweeps_since_idle_round_started_ = 0;
ms_count_at_last_idle_notification_ = ms_count_;
}
void FinishIdleRound() {
mark_sweeps_since_idle_round_started_ = kMaxMarkSweepsInIdleRound;
scavenges_since_last_idle_round_ = 0;
}
bool EnoughGarbageSinceLastIdleRound() {
return (scavenges_since_last_idle_round_ >= kIdleScavengeThreshold);
}
bool WorthStartingGCWhenIdle() {
if (contexts_disposed_ > 0) {
return true;
}
return incremental_marking()->WorthActivating();
}
// Returns true if no more GC work is left.
bool IdleGlobalGC();
static const int kInitialSymbolTableSize = 2048;
static const int kInitialEvalCacheSize = 64;
@ -1852,6 +1891,15 @@ class Heap {
unsigned int last_idle_notification_gc_count_;
bool last_idle_notification_gc_count_init_;
bool idle_notification_will_schedule_next_gc_;
int mark_sweeps_since_idle_round_started_;
int ms_count_at_last_idle_notification_;
unsigned int gc_count_at_last_idle_gc_;
int scavenges_since_last_idle_round_;
static const int kMaxMarkSweepsInIdleRound = 7;
static const int kIdleScavengeThreshold = 5;
// Shared state read by the scavenge collector and set by ScavengeObject.
PromotionQueue promotion_queue_;

View File

@ -743,8 +743,10 @@ void IncrementalMarking::MarkingComplete() {
if (FLAG_trace_incremental_marking) {
PrintF("[IncrementalMarking] Complete (normal).\n");
}
if (!heap_->idle_notification_will_schedule_next_gc()) {
heap_->isolate()->stack_guard()->RequestGC();
}
}
void IncrementalMarking::Step(intptr_t allocated_bytes) {
@ -771,8 +773,7 @@ void IncrementalMarking::Step(intptr_t allocated_bytes) {
}
if (state_ == SWEEPING) {
if (heap_->old_pointer_space()->AdvanceSweeper(bytes_to_process) &&
heap_->old_data_space()->AdvanceSweeper(bytes_to_process)) {
if (heap_->AdvanceSweepers(bytes_to_process)) {
bytes_scanned_ = 0;
StartMarking(PREVENT_COMPACTION);
}

View File

@ -63,6 +63,8 @@ class IncrementalMarking {
inline bool IsMarkingIncomplete() { return state() == MARKING; }
inline bool IsComplete() { return state() == COMPLETE; }
bool WorthActivating();
void Start();
@ -101,6 +103,7 @@ class IncrementalMarking {
void OldSpaceStep(intptr_t allocated) {
Step(allocated * kFastMarking / kInitialAllocationMarkingFactor);
}
void Step(intptr_t allocated);
inline void RestartIfNotMarking() {

View File

@ -166,13 +166,13 @@ uint32_t V8::RandomPrivate(Isolate* isolate) {
}
bool V8::IdleNotification() {
bool V8::IdleNotification(int hint) {
// Returning true tells the caller that there is no need to call
// IdleNotification again.
if (!FLAG_use_idle_notification) return true;
// Tell the heap that it may want to adjust.
return HEAP->IdleNotification();
return HEAP->IdleNotification(hint);
}

View File

@ -106,7 +106,7 @@ class V8 : public AllStatic {
Context* context);
// Idle notification directly from the API.
static bool IdleNotification();
static bool IdleNotification(int hint);
private:
static void InitializeOncePerProcess();

View File

@ -13439,13 +13439,60 @@ TEST(SourceURLInStackTrace) {
// Test that idle notification can be handled and eventually returns true.
THREADED_TEST(IdleNotification) {
v8::HandleScope scope;
LocalContext env;
CompileRun("function binom(n, m) {"
" var C = [[1]];"
" for (var i = 1; i <= n; ++i) {"
" C[i] = [1];"
" for (var j = 1; j < i; ++j) {"
" C[i][j] = C[i-1][j-1] + C[i-1][j];"
" }"
" C[i][i] = 1;"
" }"
" return C[n][m];"
"};"
"binom(1000, 500)");
bool rv = false;
intptr_t old_size = HEAP->SizeOfObjects();
bool no_idle_work = v8::V8::IdleNotification();
for (int i = 0; i < 100; i++) {
rv = v8::V8::IdleNotification();
if (rv)
break;
}
CHECK(rv == true);
intptr_t new_size = HEAP->SizeOfObjects();
CHECK(no_idle_work || new_size < 3 * old_size / 4);
}
// Test that idle notification can be handled and eventually returns true.
THREADED_TEST(IdleNotificationWithHint) {
v8::HandleScope scope;
LocalContext env;
CompileRun("function binom(n, m) {"
" var C = [[1]];"
" for (var i = 1; i <= n; ++i) {"
" C[i] = [1];"
" for (var j = 1; j < i; ++j) {"
" C[i][j] = C[i-1][j-1] + C[i-1][j];"
" }"
" C[i][i] = 1;"
" }"
" return C[n][m];"
"};"
"binom(1000, 500)");
bool rv = false;
intptr_t old_size = HEAP->SizeOfObjects();
bool no_idle_work = v8::V8::IdleNotification(10);
for (int i = 0; i < 200; i++) {
rv = v8::V8::IdleNotification(10);
if (rv)
break;
}
CHECK(rv == true);
intptr_t new_size = HEAP->SizeOfObjects();
CHECK(no_idle_work || new_size < 3 * old_size / 4);
}

View File

@ -1289,3 +1289,31 @@ TEST(CollectingAllAvailableGarbageShrinksNewSpace) {
new_capacity = new_space->Capacity();
CHECK(old_capacity == new_capacity);
}
TEST(IdleNotificationAdvancesIncrementalMarking) {
if (!FLAG_incremental_marking || !FLAG_incremental_marking_steps) return;
InitializeVM();
v8::HandleScope scope;
const char* source = "function binom(n, m) {"
" var C = [[1]];"
" for (var i = 1; i <= n; ++i) {"
" C[i] = [1];"
" for (var j = 1; j < i; ++j) {"
" C[i][j] = C[i-1][j-1] + C[i-1][j];"
" }"
" C[i][i] = 1;"
" }"
" return C[n][m];"
"};"
"binom(1000, 500)";
{
AlwaysAllocateScope aa_scope;
CompileRun(source);
}
intptr_t old_size = HEAP->SizeOfObjects();
bool no_idle_work = v8::V8::IdleNotification();
while (!v8::V8::IdleNotification());
intptr_t new_size = HEAP->SizeOfObjects();
CHECK(no_idle_work || new_size < 3 * old_size / 4);
}