Commit 93283bf0 authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

[heap, api] Add API for automatically restoring the heap limit.

Now the embedder can instruct V8 to restore the initial heap limit
once the heap size drops below the given percentage of the heap limit.

Bug: chromium:922038
Change-Id: Ib668406c5d59c02b45a8eae7de96527ebc3f2b4d
Reviewed-on: https://chromium-review.googlesource.com/c/1411606
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58837}
parent 62876856
......@@ -8207,6 +8207,14 @@ class V8_EXPORT Isolate {
void RemoveNearHeapLimitCallback(NearHeapLimitCallback callback,
size_t heap_limit);
/**
* If the heap limit was changed by the NearHeapLimitCallback, then the
* initial heap limit will be restored once the heap size falls below the
* given threshold percentage of the initial heap limit.
* The threshold percentage is a number in (0.0, 1.0) range.
*/
void AutomaticallyRestoreInitialHeapLimit(double threshold_percent = 0.5);
/**
* Set the callback to invoke to check if code generation from
* strings should be allowed.
......
......@@ -8762,6 +8762,13 @@ void Isolate::RemoveNearHeapLimitCallback(v8::NearHeapLimitCallback callback,
isolate->heap()->RemoveNearHeapLimitCallback(callback, heap_limit);
}
void Isolate::AutomaticallyRestoreInitialHeapLimit(double threshold_percent) {
DCHECK_GT(threshold_percent, 0.0);
DCHECK_LT(threshold_percent, 1.0);
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
isolate->heap()->AutomaticallyRestoreInitialHeapLimit(threshold_percent);
}
bool Isolate::IsDead() {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
return isolate->IsDead();
......
......@@ -132,6 +132,7 @@ class IdleScavengeObserver : public AllocationObserver {
Heap::Heap()
: isolate_(isolate()),
initial_max_old_generation_size_(max_old_generation_size_),
initial_max_old_generation_size_threshold_(0),
initial_old_generation_size_(max_old_generation_size_ /
kInitalOldGenerationLimitFactor),
memory_pressure_level_(MemoryPressureLevel::kNone),
......@@ -1357,6 +1358,10 @@ bool Heap::CollectGarbage(AllocationSpace space,
if (deserialization_complete_) {
memory_reducer_->NotifyMarkCompact(event);
}
if (initial_max_old_generation_size_ < max_old_generation_size_ &&
used_memory_after < initial_max_old_generation_size_threshold_) {
max_old_generation_size_ = initial_max_old_generation_size_;
}
}
tracer()->Stop(collector);
......@@ -3304,6 +3309,11 @@ void Heap::RemoveNearHeapLimitCallback(v8::NearHeapLimitCallback callback,
UNREACHABLE();
}
void Heap::AutomaticallyRestoreInitialHeapLimit(double threshold_percent) {
initial_max_old_generation_size_threshold_ =
initial_max_old_generation_size_ * threshold_percent;
}
bool Heap::InvokeNearHeapLimitCallback() {
if (near_heap_limit_callbacks_.size() > 0) {
HandleScope scope(isolate());
......
......@@ -492,6 +492,7 @@ class Heap {
void AddNearHeapLimitCallback(v8::NearHeapLimitCallback, void* data);
void RemoveNearHeapLimitCallback(v8::NearHeapLimitCallback callback,
size_t heap_limit);
void AutomaticallyRestoreInitialHeapLimit(double threshold_percent);
double MonotonicallyIncreasingTimeInMs();
......@@ -1750,6 +1751,7 @@ class Heap {
size_t initial_semispace_size_ = kMinSemiSpaceSizeInKB * KB;
size_t max_old_generation_size_ = 700ul * (kSystemPointerSize / 4) * MB;
size_t initial_max_old_generation_size_;
size_t initial_max_old_generation_size_threshold_;
size_t initial_old_generation_size_;
bool old_generation_size_configured_ = false;
size_t maximum_committed_ = 0;
......
......@@ -6295,6 +6295,8 @@ struct OutOfMemoryState {
size_t old_generation_capacity_at_oom;
size_t memory_allocator_size_at_oom;
size_t new_space_capacity_at_oom;
size_t current_heap_limit;
size_t initial_heap_limit;
};
size_t NearHeapLimitCallback(void* raw_state, size_t current_heap_limit,
......@@ -6305,6 +6307,8 @@ size_t NearHeapLimitCallback(void* raw_state, size_t current_heap_limit,
state->old_generation_capacity_at_oom = heap->OldGenerationCapacity();
state->memory_allocator_size_at_oom = heap->memory_allocator()->Size();
state->new_space_capacity_at_oom = heap->new_space()->Capacity();
state->current_heap_limit = current_heap_limit;
state->initial_heap_limit = initial_heap_limit;
return initial_heap_limit + 100 * MB;
}
......@@ -6386,6 +6390,44 @@ UNINITIALIZED_TEST(OutOfMemoryLargeObjects) {
reinterpret_cast<v8::Isolate*>(isolate)->Dispose();
}
UNINITIALIZED_TEST(RestoreHeapLimit) {
if (FLAG_stress_incremental_marking) return;
#ifdef VERIFY_HEAP
if (FLAG_verify_heap) return;
#endif
ManualGCScope manual_gc_scope;
const size_t kOldGenerationLimit = 300 * MB;
FLAG_max_old_space_size = kOldGenerationLimit / MB;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
Isolate* isolate =
reinterpret_cast<Isolate*>(v8::Isolate::New(create_params));
Heap* heap = isolate->heap();
Factory* factory = isolate->factory();
OutOfMemoryState state;
state.heap = heap;
state.oom_triggered = false;
heap->AddNearHeapLimitCallback(NearHeapLimitCallback, &state);
heap->AutomaticallyRestoreInitialHeapLimit(0.5);
const int kFixedArrayLength = 1000000;
{
HandleScope handle_scope(isolate);
while (!state.oom_triggered) {
factory->NewFixedArray(kFixedArrayLength);
}
}
heap->MemoryPressureNotification(MemoryPressureLevel::kCritical, true);
state.oom_triggered = false;
{
HandleScope handle_scope(isolate);
while (!state.oom_triggered) {
factory->NewFixedArray(kFixedArrayLength);
}
}
CHECK_EQ(state.current_heap_limit, state.initial_heap_limit);
reinterpret_cast<v8::Isolate*>(isolate)->Dispose();
}
void HeapTester::UncommitFromSpace(Heap* heap) {
heap->UncommitFromSpace();
heap->memory_allocator()->unmapper()->EnsureUnmappingCompleted();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment