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:
parent
e2f2c77e6f
commit
8dc728126e
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
84
src/heap.cc
84
src/heap.cc
@ -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;
|
||||
|
52
src/heap.h
52
src/heap.h
@ -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_;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
2
src/v8.h
2
src/v8.h
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user