[heap] Refactoring heap growing strategy from Heap to HeapController class.
Bug: chromium:845409 Change-Id: I377d6f9d26a193f7fd829f7b74f9fdabc1337dc0 Reviewed-on: https://chromium-review.googlesource.com/1089053 Commit-Queue: Rodrigo Bruno <rfbpb@google.com> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Cr-Commit-Position: refs/heads/master@{#53580}
This commit is contained in:
parent
e838e75e39
commit
db4b7e7598
2
BUILD.gn
2
BUILD.gn
@ -1933,6 +1933,8 @@ v8_source_set("v8_base") {
|
||||
"src/heap/gc-idle-time-handler.h",
|
||||
"src/heap/gc-tracer.cc",
|
||||
"src/heap/gc-tracer.h",
|
||||
"src/heap/heap-controller.cc",
|
||||
"src/heap/heap-controller.h",
|
||||
"src/heap/heap-inl.h",
|
||||
"src/heap/heap.cc",
|
||||
"src/heap/heap.h",
|
||||
|
189
src/heap/heap-controller.cc
Normal file
189
src/heap/heap-controller.cc
Normal file
@ -0,0 +1,189 @@
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/heap/heap-controller.h"
|
||||
#include "src/heap/heap.h"
|
||||
#include "src/heap/memory-reducer.h"
|
||||
#include "src/isolate-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
const double HeapController::kMinHeapGrowingFactor = 1.1;
|
||||
const double HeapController::kMaxHeapGrowingFactor = 4.0;
|
||||
const double HeapController::kMaxHeapGrowingFactorMemoryConstrained = 2.0;
|
||||
const double HeapController::kMaxHeapGrowingFactorIdle = 1.5;
|
||||
const double HeapController::kConservativeHeapGrowingFactor = 1.3;
|
||||
const double HeapController::kTargetMutatorUtilization = 0.97;
|
||||
|
||||
// Given GC speed in bytes per ms, the allocation throughput in bytes per ms
|
||||
// (mutator speed), this function returns the heap growing factor that will
|
||||
// achieve the kTargetMutatorUtilisation if the GC speed and the mutator speed
|
||||
// remain the same until the next GC.
|
||||
//
|
||||
// For a fixed time-frame T = TM + TG, the mutator utilization is the ratio
|
||||
// TM / (TM + TG), where TM is the time spent in the mutator and TG is the
|
||||
// time spent in the garbage collector.
|
||||
//
|
||||
// Let MU be kTargetMutatorUtilisation, the desired mutator utilization for the
|
||||
// time-frame from the end of the current GC to the end of the next GC. Based
|
||||
// on the MU we can compute the heap growing factor F as
|
||||
//
|
||||
// F = R * (1 - MU) / (R * (1 - MU) - MU), where R = gc_speed / mutator_speed.
|
||||
//
|
||||
// This formula can be derived as follows.
|
||||
//
|
||||
// F = Limit / Live by definition, where the Limit is the allocation limit,
|
||||
// and the Live is size of live objects.
|
||||
// Let’s assume that we already know the Limit. Then:
|
||||
// TG = Limit / gc_speed
|
||||
// TM = (TM + TG) * MU, by definition of MU.
|
||||
// TM = TG * MU / (1 - MU)
|
||||
// TM = Limit * MU / (gc_speed * (1 - MU))
|
||||
// On the other hand, if the allocation throughput remains constant:
|
||||
// Limit = Live + TM * allocation_throughput = Live + TM * mutator_speed
|
||||
// Solving it for TM, we get
|
||||
// TM = (Limit - Live) / mutator_speed
|
||||
// Combining the two equation for TM:
|
||||
// (Limit - Live) / mutator_speed = Limit * MU / (gc_speed * (1 - MU))
|
||||
// (Limit - Live) = Limit * MU * mutator_speed / (gc_speed * (1 - MU))
|
||||
// substitute R = gc_speed / mutator_speed
|
||||
// (Limit - Live) = Limit * MU / (R * (1 - MU))
|
||||
// substitute F = Limit / Live
|
||||
// F - 1 = F * MU / (R * (1 - MU))
|
||||
// F - F * MU / (R * (1 - MU)) = 1
|
||||
// F * (1 - MU / (R * (1 - MU))) = 1
|
||||
// F * (R * (1 - MU) - MU) / (R * (1 - MU)) = 1
|
||||
// F = R * (1 - MU) / (R * (1 - MU) - MU)
|
||||
double HeapController::HeapGrowingFactor(double gc_speed, double mutator_speed,
|
||||
double max_factor) {
|
||||
DCHECK_LE(kMinHeapGrowingFactor, max_factor);
|
||||
DCHECK_GE(kMaxHeapGrowingFactor, max_factor);
|
||||
if (gc_speed == 0 || mutator_speed == 0) return max_factor;
|
||||
|
||||
const double speed_ratio = gc_speed / mutator_speed;
|
||||
const double mu = kTargetMutatorUtilization;
|
||||
|
||||
const double a = speed_ratio * (1 - mu);
|
||||
const double b = speed_ratio * (1 - mu) - mu;
|
||||
|
||||
// The factor is a / b, but we need to check for small b first.
|
||||
double factor = (a < b * max_factor) ? a / b : max_factor;
|
||||
factor = Min(factor, max_factor);
|
||||
factor = Max(factor, kMinHeapGrowingFactor);
|
||||
return factor;
|
||||
}
|
||||
|
||||
double HeapController::MaxHeapGrowingFactor(size_t max_old_generation_size) {
|
||||
const double min_small_factor = 1.3;
|
||||
const double max_small_factor = 2.0;
|
||||
const double high_factor = 4.0;
|
||||
|
||||
size_t max_old_generation_size_in_mb = max_old_generation_size / MB;
|
||||
max_old_generation_size_in_mb =
|
||||
Max(max_old_generation_size_in_mb,
|
||||
static_cast<size_t>(kMinOldGenerationSize));
|
||||
|
||||
// If we are on a device with lots of memory, we allow a high heap
|
||||
// growing factor.
|
||||
if (max_old_generation_size_in_mb >= kMaxOldGenerationSize) {
|
||||
return high_factor;
|
||||
}
|
||||
|
||||
DCHECK_GE(max_old_generation_size_in_mb, kMinOldGenerationSize);
|
||||
DCHECK_LT(max_old_generation_size_in_mb, kMaxOldGenerationSize);
|
||||
|
||||
// On smaller devices we linearly scale the factor: (X-A)/(B-A)*(D-C)+C
|
||||
double factor = (max_old_generation_size_in_mb - kMinOldGenerationSize) *
|
||||
(max_small_factor - min_small_factor) /
|
||||
(kMaxOldGenerationSize - kMinOldGenerationSize) +
|
||||
min_small_factor;
|
||||
return factor;
|
||||
}
|
||||
|
||||
size_t HeapController::ComputeOldGenerationAllocationLimit(
|
||||
size_t old_gen_size, size_t max_old_generation_size, double gc_speed,
|
||||
double mutator_speed) {
|
||||
double max_factor = MaxHeapGrowingFactor(max_old_generation_size);
|
||||
double factor = HeapGrowingFactor(gc_speed, mutator_speed, max_factor);
|
||||
size_t limit;
|
||||
|
||||
if (FLAG_trace_gc_verbose) {
|
||||
heap_->isolate()->PrintWithTimestamp(
|
||||
"Heap growing factor %.1f based on mu=%.3f, speed_ratio=%.f "
|
||||
"(gc=%.f, mutator=%.f)\n",
|
||||
factor, kTargetMutatorUtilization, gc_speed / mutator_speed, gc_speed,
|
||||
mutator_speed);
|
||||
}
|
||||
|
||||
if (heap_->memory_reducer()->ShouldGrowHeapSlowly() ||
|
||||
heap_->ShouldOptimizeForMemoryUsage()) {
|
||||
factor = Min(factor, kConservativeHeapGrowingFactor);
|
||||
}
|
||||
|
||||
if (FLAG_stress_compaction || heap_->ShouldReduceMemory()) {
|
||||
factor = kMinHeapGrowingFactor;
|
||||
}
|
||||
|
||||
if (FLAG_heap_growing_percent > 0) {
|
||||
factor = 1.0 + FLAG_heap_growing_percent / 100.0;
|
||||
}
|
||||
|
||||
limit = CalculateOldGenerationAllocationLimit(factor, old_gen_size,
|
||||
max_old_generation_size);
|
||||
|
||||
if (FLAG_trace_gc_verbose) {
|
||||
heap_->isolate()->PrintWithTimestamp(
|
||||
"Grow: old size: %" PRIuS " KB, new limit: %" PRIuS " KB (%.1f)\n",
|
||||
old_gen_size / KB, limit / KB, factor);
|
||||
}
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
||||
size_t HeapController::DampenOldGenerationAllocationLimit(
|
||||
size_t old_gen_size, size_t max_old_generation_size, double gc_speed,
|
||||
double mutator_speed) {
|
||||
double max_factor = MaxHeapGrowingFactor(max_old_generation_size);
|
||||
double factor = HeapGrowingFactor(gc_speed, mutator_speed, max_factor);
|
||||
size_t limit = CalculateOldGenerationAllocationLimit(factor, old_gen_size,
|
||||
max_old_generation_size);
|
||||
if (limit < heap_->old_generation_allocation_limit()) {
|
||||
if (FLAG_trace_gc_verbose) {
|
||||
heap_->isolate()->PrintWithTimestamp(
|
||||
"Dampen: old size: %" PRIuS " KB, old limit: %" PRIuS
|
||||
" KB, "
|
||||
"new limit: %" PRIuS " KB (%.1f)\n",
|
||||
old_gen_size / KB, heap_->old_generation_allocation_limit() / KB,
|
||||
limit / KB, factor);
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
return heap_->old_generation_allocation_limit();
|
||||
}
|
||||
|
||||
size_t HeapController::CalculateOldGenerationAllocationLimit(
|
||||
double factor, size_t old_gen_size, size_t max_old_generation_size) {
|
||||
CHECK_LT(1.0, factor);
|
||||
CHECK_LT(0, old_gen_size);
|
||||
uint64_t limit = static_cast<uint64_t>(old_gen_size * factor);
|
||||
limit = Max(limit, static_cast<uint64_t>(old_gen_size) +
|
||||
MinimumAllocationLimitGrowingStep());
|
||||
limit += heap_->new_space()->Capacity();
|
||||
uint64_t halfway_to_the_max =
|
||||
(static_cast<uint64_t>(old_gen_size) + max_old_generation_size) / 2;
|
||||
return static_cast<size_t>(Min(limit, halfway_to_the_max));
|
||||
}
|
||||
|
||||
size_t HeapController::MinimumAllocationLimitGrowingStep() {
|
||||
const size_t kRegularAllocationLimitGrowingStep = 8;
|
||||
const size_t kLowMemoryAllocationLimitGrowingStep = 2;
|
||||
size_t limit = (Page::kPageSize > MB ? Page::kPageSize : MB);
|
||||
return limit * (heap_->ShouldOptimizeForMemoryUsage()
|
||||
? kLowMemoryAllocationLimitGrowingStep
|
||||
: kRegularAllocationLimitGrowingStep);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
72
src/heap/heap-controller.h
Normal file
72
src/heap/heap-controller.h
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_HEAP_HEAP_CONTROLLER_H_
|
||||
#define V8_HEAP_HEAP_CONTROLLER_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include "src/allocation.h"
|
||||
#include "src/heap/heap.h"
|
||||
#include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class Heap;
|
||||
|
||||
class HeapController {
|
||||
public:
|
||||
explicit HeapController(Heap* heap) : heap_(heap) {}
|
||||
|
||||
// Computes the allocation limit to trigger the next full garbage collection.
|
||||
size_t ComputeOldGenerationAllocationLimit(size_t old_gen_size,
|
||||
size_t max_old_generation_size,
|
||||
double gc_speed,
|
||||
double mutator_speed);
|
||||
|
||||
// Decrease the allocation limit if the new limit based on the given
|
||||
// parameters is lower than the current limit.
|
||||
size_t DampenOldGenerationAllocationLimit(size_t old_gen_size,
|
||||
size_t max_old_generation_size,
|
||||
double gc_speed,
|
||||
double mutator_speed);
|
||||
|
||||
size_t MinimumAllocationLimitGrowingStep();
|
||||
|
||||
// The old space size has to be a multiple of Page::kPageSize.
|
||||
// Sizes are in MB.
|
||||
static const size_t kMinOldGenerationSize = 128 * Heap::kPointerMultiplier;
|
||||
static const size_t kMaxOldGenerationSize = 1024 * Heap::kPointerMultiplier;
|
||||
|
||||
private:
|
||||
FRIEND_TEST(HeapController, HeapGrowingFactor);
|
||||
FRIEND_TEST(HeapController, MaxHeapGrowingFactor);
|
||||
FRIEND_TEST(HeapController, OldGenerationSize);
|
||||
|
||||
V8_EXPORT_PRIVATE static const double kMinHeapGrowingFactor;
|
||||
V8_EXPORT_PRIVATE static const double kMaxHeapGrowingFactor;
|
||||
V8_EXPORT_PRIVATE static double MaxHeapGrowingFactor(
|
||||
size_t max_old_generation_size);
|
||||
V8_EXPORT_PRIVATE static double HeapGrowingFactor(double gc_speed,
|
||||
double mutator_speed,
|
||||
double max_factor);
|
||||
|
||||
// Calculates the allocation limit based on a given growing factor and a
|
||||
// given old generation size.
|
||||
size_t CalculateOldGenerationAllocationLimit(double factor,
|
||||
size_t old_gen_size,
|
||||
size_t max_old_generation_size);
|
||||
|
||||
Heap* heap_;
|
||||
|
||||
static const double kMaxHeapGrowingFactorMemoryConstrained;
|
||||
static const double kMaxHeapGrowingFactorIdle;
|
||||
static const double kConservativeHeapGrowingFactor;
|
||||
static const double kTargetMutatorUtilization;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_HEAP_HEAP_CONTROLLER_H_
|
197
src/heap/heap.cc
197
src/heap/heap.cc
@ -30,6 +30,7 @@
|
||||
#include "src/heap/embedder-tracing.h"
|
||||
#include "src/heap/gc-idle-time-handler.h"
|
||||
#include "src/heap/gc-tracer.h"
|
||||
#include "src/heap/heap-controller.h"
|
||||
#include "src/heap/incremental-marking.h"
|
||||
#include "src/heap/item-parallel-job.h"
|
||||
#include "src/heap/mark-compact-inl.h"
|
||||
@ -252,6 +253,15 @@ size_t Heap::MaxReserved() {
|
||||
(2 * max_semi_space_size_ + max_old_generation_size_) * kFactor);
|
||||
}
|
||||
|
||||
size_t Heap::ComputeMaxOldGenerationSize(uint64_t physical_memory) {
|
||||
const size_t old_space_physical_memory_factor = 4;
|
||||
size_t computed_size = static_cast<size_t>(physical_memory / i::MB /
|
||||
old_space_physical_memory_factor *
|
||||
kPointerMultiplier);
|
||||
return Max(Min(computed_size, HeapController::kMaxOldGenerationSize),
|
||||
HeapController::kMinOldGenerationSize);
|
||||
}
|
||||
|
||||
size_t Heap::Capacity() {
|
||||
if (!HasBeenSetUp()) return 0;
|
||||
|
||||
@ -1794,12 +1804,16 @@ bool Heap::PerformGarbageCollection(
|
||||
// Register the amount of external allocated memory.
|
||||
external_memory_at_last_mark_compact_ = external_memory_;
|
||||
external_memory_limit_ = external_memory_ + kExternalAllocationSoftLimit;
|
||||
SetOldGenerationAllocationLimit(old_gen_size, gc_speed, mutator_speed);
|
||||
old_generation_allocation_limit_ =
|
||||
heap_controller()->ComputeOldGenerationAllocationLimit(
|
||||
old_gen_size, max_old_generation_size_, gc_speed, mutator_speed);
|
||||
CheckIneffectiveMarkCompact(
|
||||
old_gen_size, tracer()->AverageMarkCompactMutatorUtilization());
|
||||
} else if (HasLowYoungGenerationAllocationRate() &&
|
||||
old_generation_size_configured_) {
|
||||
DampenOldGenerationAllocationLimit(old_gen_size, gc_speed, mutator_speed);
|
||||
old_generation_allocation_limit_ =
|
||||
heap_controller()->DampenOldGenerationAllocationLimit(
|
||||
old_gen_size, max_old_generation_size_, gc_speed, mutator_speed);
|
||||
}
|
||||
|
||||
{
|
||||
@ -2565,7 +2579,7 @@ void Heap::UnregisterArrayBuffer(JSArrayBuffer* buffer) {
|
||||
void Heap::ConfigureInitialOldGenerationSize() {
|
||||
if (!old_generation_size_configured_ && tracer()->SurvivalEventsRecorded()) {
|
||||
old_generation_allocation_limit_ =
|
||||
Max(MinimumAllocationLimitGrowingStep(),
|
||||
Max(heap_controller()->MinimumAllocationLimitGrowingStep(),
|
||||
static_cast<size_t>(
|
||||
static_cast<double>(old_generation_allocation_limit_) *
|
||||
(tracer()->AverageSurvivalRatio() / 100)));
|
||||
@ -4259,176 +4273,6 @@ uint64_t Heap::PromotedExternalMemorySize() {
|
||||
external_memory_at_last_mark_compact_);
|
||||
}
|
||||
|
||||
|
||||
const double Heap::kMinHeapGrowingFactor = 1.1;
|
||||
const double Heap::kMaxHeapGrowingFactor = 4.0;
|
||||
const double Heap::kMaxHeapGrowingFactorMemoryConstrained = 2.0;
|
||||
const double Heap::kMaxHeapGrowingFactorIdle = 1.5;
|
||||
const double Heap::kConservativeHeapGrowingFactor = 1.3;
|
||||
const double Heap::kTargetMutatorUtilization = 0.97;
|
||||
|
||||
// Given GC speed in bytes per ms, the allocation throughput in bytes per ms
|
||||
// (mutator speed), this function returns the heap growing factor that will
|
||||
// achieve the kTargetMutatorUtilisation if the GC speed and the mutator speed
|
||||
// remain the same until the next GC.
|
||||
//
|
||||
// For a fixed time-frame T = TM + TG, the mutator utilization is the ratio
|
||||
// TM / (TM + TG), where TM is the time spent in the mutator and TG is the
|
||||
// time spent in the garbage collector.
|
||||
//
|
||||
// Let MU be kTargetMutatorUtilisation, the desired mutator utilization for the
|
||||
// time-frame from the end of the current GC to the end of the next GC. Based
|
||||
// on the MU we can compute the heap growing factor F as
|
||||
//
|
||||
// F = R * (1 - MU) / (R * (1 - MU) - MU), where R = gc_speed / mutator_speed.
|
||||
//
|
||||
// This formula can be derived as follows.
|
||||
//
|
||||
// F = Limit / Live by definition, where the Limit is the allocation limit,
|
||||
// and the Live is size of live objects.
|
||||
// Let’s assume that we already know the Limit. Then:
|
||||
// TG = Limit / gc_speed
|
||||
// TM = (TM + TG) * MU, by definition of MU.
|
||||
// TM = TG * MU / (1 - MU)
|
||||
// TM = Limit * MU / (gc_speed * (1 - MU))
|
||||
// On the other hand, if the allocation throughput remains constant:
|
||||
// Limit = Live + TM * allocation_throughput = Live + TM * mutator_speed
|
||||
// Solving it for TM, we get
|
||||
// TM = (Limit - Live) / mutator_speed
|
||||
// Combining the two equation for TM:
|
||||
// (Limit - Live) / mutator_speed = Limit * MU / (gc_speed * (1 - MU))
|
||||
// (Limit - Live) = Limit * MU * mutator_speed / (gc_speed * (1 - MU))
|
||||
// substitute R = gc_speed / mutator_speed
|
||||
// (Limit - Live) = Limit * MU / (R * (1 - MU))
|
||||
// substitute F = Limit / Live
|
||||
// F - 1 = F * MU / (R * (1 - MU))
|
||||
// F - F * MU / (R * (1 - MU)) = 1
|
||||
// F * (1 - MU / (R * (1 - MU))) = 1
|
||||
// F * (R * (1 - MU) - MU) / (R * (1 - MU)) = 1
|
||||
// F = R * (1 - MU) / (R * (1 - MU) - MU)
|
||||
double Heap::HeapGrowingFactor(double gc_speed, double mutator_speed,
|
||||
double max_factor) {
|
||||
DCHECK_LE(kMinHeapGrowingFactor, max_factor);
|
||||
DCHECK_GE(kMaxHeapGrowingFactor, max_factor);
|
||||
if (gc_speed == 0 || mutator_speed == 0) return max_factor;
|
||||
|
||||
const double speed_ratio = gc_speed / mutator_speed;
|
||||
const double mu = kTargetMutatorUtilization;
|
||||
|
||||
const double a = speed_ratio * (1 - mu);
|
||||
const double b = speed_ratio * (1 - mu) - mu;
|
||||
|
||||
// The factor is a / b, but we need to check for small b first.
|
||||
double factor = (a < b * max_factor) ? a / b : max_factor;
|
||||
factor = Min(factor, max_factor);
|
||||
factor = Max(factor, kMinHeapGrowingFactor);
|
||||
return factor;
|
||||
}
|
||||
|
||||
double Heap::MaxHeapGrowingFactor(size_t max_old_generation_size) {
|
||||
const double min_small_factor = 1.3;
|
||||
const double max_small_factor = 2.0;
|
||||
const double high_factor = 4.0;
|
||||
|
||||
size_t max_old_generation_size_in_mb = max_old_generation_size / MB;
|
||||
max_old_generation_size_in_mb =
|
||||
Max(max_old_generation_size_in_mb,
|
||||
static_cast<size_t>(kMinOldGenerationSize));
|
||||
|
||||
// If we are on a device with lots of memory, we allow a high heap
|
||||
// growing factor.
|
||||
if (max_old_generation_size_in_mb >= kMaxOldGenerationSize) {
|
||||
return high_factor;
|
||||
}
|
||||
|
||||
DCHECK_GE(max_old_generation_size_in_mb, kMinOldGenerationSize);
|
||||
DCHECK_LT(max_old_generation_size_in_mb, kMaxOldGenerationSize);
|
||||
|
||||
// On smaller devices we linearly scale the factor: (X-A)/(B-A)*(D-C)+C
|
||||
double factor = (max_old_generation_size_in_mb - kMinOldGenerationSize) *
|
||||
(max_small_factor - min_small_factor) /
|
||||
(kMaxOldGenerationSize - kMinOldGenerationSize) +
|
||||
min_small_factor;
|
||||
return factor;
|
||||
}
|
||||
|
||||
size_t Heap::CalculateOldGenerationAllocationLimit(double factor,
|
||||
size_t old_gen_size) {
|
||||
CHECK_LT(1.0, factor);
|
||||
CHECK_LT(0, old_gen_size);
|
||||
uint64_t limit = static_cast<uint64_t>(old_gen_size * factor);
|
||||
limit = Max(limit, static_cast<uint64_t>(old_gen_size) +
|
||||
MinimumAllocationLimitGrowingStep());
|
||||
limit += new_space_->Capacity();
|
||||
uint64_t halfway_to_the_max =
|
||||
(static_cast<uint64_t>(old_gen_size) + max_old_generation_size_) / 2;
|
||||
return static_cast<size_t>(Min(limit, halfway_to_the_max));
|
||||
}
|
||||
|
||||
size_t Heap::MinimumAllocationLimitGrowingStep() {
|
||||
const size_t kRegularAllocationLimitGrowingStep = 8;
|
||||
const size_t kLowMemoryAllocationLimitGrowingStep = 2;
|
||||
size_t limit = (Page::kPageSize > MB ? Page::kPageSize : MB);
|
||||
return limit * (ShouldOptimizeForMemoryUsage()
|
||||
? kLowMemoryAllocationLimitGrowingStep
|
||||
: kRegularAllocationLimitGrowingStep);
|
||||
}
|
||||
|
||||
void Heap::SetOldGenerationAllocationLimit(size_t old_gen_size, double gc_speed,
|
||||
double mutator_speed) {
|
||||
double max_factor = MaxHeapGrowingFactor(max_old_generation_size_);
|
||||
double factor = HeapGrowingFactor(gc_speed, mutator_speed, max_factor);
|
||||
|
||||
if (FLAG_trace_gc_verbose) {
|
||||
isolate_->PrintWithTimestamp(
|
||||
"Heap growing factor %.1f based on mu=%.3f, speed_ratio=%.f "
|
||||
"(gc=%.f, mutator=%.f)\n",
|
||||
factor, kTargetMutatorUtilization, gc_speed / mutator_speed, gc_speed,
|
||||
mutator_speed);
|
||||
}
|
||||
|
||||
if (memory_reducer_->ShouldGrowHeapSlowly() ||
|
||||
ShouldOptimizeForMemoryUsage()) {
|
||||
factor = Min(factor, kConservativeHeapGrowingFactor);
|
||||
}
|
||||
|
||||
if (FLAG_stress_compaction || ShouldReduceMemory()) {
|
||||
factor = kMinHeapGrowingFactor;
|
||||
}
|
||||
|
||||
if (FLAG_heap_growing_percent > 0) {
|
||||
factor = 1.0 + FLAG_heap_growing_percent / 100.0;
|
||||
}
|
||||
|
||||
old_generation_allocation_limit_ =
|
||||
CalculateOldGenerationAllocationLimit(factor, old_gen_size);
|
||||
|
||||
if (FLAG_trace_gc_verbose) {
|
||||
isolate_->PrintWithTimestamp(
|
||||
"Grow: old size: %" PRIuS " KB, new limit: %" PRIuS " KB (%.1f)\n",
|
||||
old_gen_size / KB, old_generation_allocation_limit_ / KB, factor);
|
||||
}
|
||||
}
|
||||
|
||||
void Heap::DampenOldGenerationAllocationLimit(size_t old_gen_size,
|
||||
double gc_speed,
|
||||
double mutator_speed) {
|
||||
double max_factor = MaxHeapGrowingFactor(max_old_generation_size_);
|
||||
double factor = HeapGrowingFactor(gc_speed, mutator_speed, max_factor);
|
||||
size_t limit = CalculateOldGenerationAllocationLimit(factor, old_gen_size);
|
||||
if (limit < old_generation_allocation_limit_) {
|
||||
if (FLAG_trace_gc_verbose) {
|
||||
isolate_->PrintWithTimestamp(
|
||||
"Dampen: old size: %" PRIuS " KB, old limit: %" PRIuS
|
||||
" KB, "
|
||||
"new limit: %" PRIuS " KB (%.1f)\n",
|
||||
old_gen_size / KB, old_generation_allocation_limit_ / KB, limit / KB,
|
||||
factor);
|
||||
}
|
||||
old_generation_allocation_limit_ = limit;
|
||||
}
|
||||
}
|
||||
|
||||
bool Heap::ShouldOptimizeForLoadTime() {
|
||||
return isolate()->rail_mode() == PERFORMANCE_LOAD &&
|
||||
!AllocationLimitOvershotByLargeMargin() &&
|
||||
@ -4690,6 +4534,8 @@ bool Heap::SetUp() {
|
||||
|
||||
store_buffer_ = new StoreBuffer(this);
|
||||
|
||||
heap_controller_ = new HeapController(this);
|
||||
|
||||
mark_compact_collector_ = new MarkCompactCollector(this);
|
||||
incremental_marking_ =
|
||||
new IncrementalMarking(this, mark_compact_collector_->marking_worklist(),
|
||||
@ -4939,6 +4785,11 @@ void Heap::TearDown() {
|
||||
stress_scavenge_observer_ = nullptr;
|
||||
}
|
||||
|
||||
if (heap_controller_ != nullptr) {
|
||||
delete heap_controller_;
|
||||
heap_controller_ = nullptr;
|
||||
}
|
||||
|
||||
if (mark_compact_collector_ != nullptr) {
|
||||
mark_compact_collector_->TearDown();
|
||||
delete mark_compact_collector_;
|
||||
|
@ -436,6 +436,7 @@ class GCIdleTimeAction;
|
||||
class GCIdleTimeHandler;
|
||||
class GCIdleTimeHeapState;
|
||||
class GCTracer;
|
||||
class HeapController;
|
||||
class HeapObjectAllocationTracker;
|
||||
class HeapObjectsFilter;
|
||||
class HeapStats;
|
||||
@ -652,21 +653,9 @@ class Heap {
|
||||
static const size_t kMaxSemiSpaceSizeInKB =
|
||||
16 * kPointerMultiplier * ((1 << kPageSizeBits) / KB);
|
||||
|
||||
// The old space size has to be a multiple of Page::kPageSize.
|
||||
// Sizes are in MB.
|
||||
static const size_t kMinOldGenerationSize = 128 * kPointerMultiplier;
|
||||
static const size_t kMaxOldGenerationSize = 1024 * kPointerMultiplier;
|
||||
|
||||
static const int kTraceRingBufferSize = 512;
|
||||
static const int kStacktraceBufferSize = 512;
|
||||
|
||||
V8_EXPORT_PRIVATE static const double kMinHeapGrowingFactor;
|
||||
V8_EXPORT_PRIVATE static const double kMaxHeapGrowingFactor;
|
||||
static const double kMaxHeapGrowingFactorMemoryConstrained;
|
||||
static const double kMaxHeapGrowingFactorIdle;
|
||||
static const double kConservativeHeapGrowingFactor;
|
||||
static const double kTargetMutatorUtilization;
|
||||
|
||||
static const int kNoGCFlags = 0;
|
||||
static const int kReduceMemoryFootprintMask = 1;
|
||||
static const int kAbortIncrementalMarkingMask = 2;
|
||||
@ -746,12 +735,6 @@ class Heap {
|
||||
return "Unknown collector";
|
||||
}
|
||||
|
||||
V8_EXPORT_PRIVATE static double MaxHeapGrowingFactor(
|
||||
size_t max_old_generation_size);
|
||||
V8_EXPORT_PRIVATE static double HeapGrowingFactor(double gc_speed,
|
||||
double mutator_speed,
|
||||
double max_factor);
|
||||
|
||||
// Copy block of memory from src to dst. Size of block should be aligned
|
||||
// by pointer size.
|
||||
static inline void CopyBlock(Address dst, Address src, int byte_size);
|
||||
@ -1447,14 +1430,8 @@ class Heap {
|
||||
size_t InitialSemiSpaceSize() { return initial_semispace_size_; }
|
||||
size_t MaxOldGenerationSize() { return max_old_generation_size_; }
|
||||
|
||||
static size_t ComputeMaxOldGenerationSize(uint64_t physical_memory) {
|
||||
const size_t old_space_physical_memory_factor = 4;
|
||||
size_t computed_size = static_cast<size_t>(
|
||||
physical_memory / i::MB / old_space_physical_memory_factor *
|
||||
kPointerMultiplier);
|
||||
return Max(Min(computed_size, kMaxOldGenerationSize),
|
||||
kMinOldGenerationSize);
|
||||
}
|
||||
V8_EXPORT_PRIVATE static size_t ComputeMaxOldGenerationSize(
|
||||
uint64_t physical_memory);
|
||||
|
||||
static size_t ComputeMaxSemiSpaceSize(uint64_t physical_memory) {
|
||||
const uint64_t min_physical_memory = 512 * MB;
|
||||
@ -2100,6 +2077,9 @@ class Heap {
|
||||
// Growing strategy. =========================================================
|
||||
// ===========================================================================
|
||||
|
||||
HeapController* heap_controller() { return heap_controller_; }
|
||||
MemoryReducer* memory_reducer() { return memory_reducer_; }
|
||||
|
||||
// For some webpages RAIL mode does not switch from PERFORMANCE_LOAD.
|
||||
// This constant limits the effect of load RAIL mode on GC.
|
||||
// The value is arbitrary and chosen as the largest load time observed in
|
||||
@ -2108,22 +2088,6 @@ class Heap {
|
||||
|
||||
bool ShouldOptimizeForLoadTime();
|
||||
|
||||
// Decrease the allocation limit if the new limit based on the given
|
||||
// parameters is lower than the current limit.
|
||||
void DampenOldGenerationAllocationLimit(size_t old_gen_size, double gc_speed,
|
||||
double mutator_speed);
|
||||
|
||||
// Calculates the allocation limit based on a given growing factor and a
|
||||
// given old generation size.
|
||||
size_t CalculateOldGenerationAllocationLimit(double factor,
|
||||
size_t old_gen_size);
|
||||
|
||||
// Sets the allocation limit to trigger the next full garbage collection.
|
||||
void SetOldGenerationAllocationLimit(size_t old_gen_size, double gc_speed,
|
||||
double mutator_speed);
|
||||
|
||||
size_t MinimumAllocationLimitGrowingStep();
|
||||
|
||||
size_t old_generation_allocation_limit() const {
|
||||
return old_generation_allocation_limit_;
|
||||
}
|
||||
@ -2418,6 +2382,8 @@ class Heap {
|
||||
|
||||
StoreBuffer* store_buffer_;
|
||||
|
||||
HeapController* heap_controller_;
|
||||
|
||||
IncrementalMarking* incremental_marking_;
|
||||
ConcurrentMarking* concurrent_marking_;
|
||||
|
||||
@ -2525,6 +2491,7 @@ class Heap {
|
||||
friend class ConcurrentMarking;
|
||||
friend class GCCallbacksScope;
|
||||
friend class GCTracer;
|
||||
friend class HeapController;
|
||||
friend class HeapIterator;
|
||||
friend class IdleScavengeObserver;
|
||||
friend class IncrementalMarking;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "src/heap/array-buffer-tracker.h"
|
||||
#include "src/heap/concurrent-marking.h"
|
||||
#include "src/heap/gc-tracer.h"
|
||||
#include "src/heap/heap-controller.h"
|
||||
#include "src/heap/incremental-marking.h"
|
||||
#include "src/heap/mark-compact.h"
|
||||
#include "src/heap/slot-set.h"
|
||||
|
@ -150,6 +150,7 @@ v8_source_set("unittests_sources") {
|
||||
"heap/embedder-tracing-unittest.cc",
|
||||
"heap/gc-idle-time-handler-unittest.cc",
|
||||
"heap/gc-tracer-unittest.cc",
|
||||
"heap/heap-controller-unittest.cc",
|
||||
"heap/heap-unittest.cc",
|
||||
"heap/item-parallel-job-unittest.cc",
|
||||
"heap/marking-unittest.cc",
|
||||
|
80
test/unittests/heap/heap-controller-unittest.cc
Normal file
80
test/unittests/heap/heap-controller-unittest.cc
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects.h"
|
||||
|
||||
#include "src/handles-inl.h"
|
||||
#include "src/handles.h"
|
||||
|
||||
#include "src/heap/heap-controller.h"
|
||||
#include "src/heap/heap.h"
|
||||
#include "test/unittests/test-utils.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
double Round(double x) {
|
||||
// Round to three digits.
|
||||
return floor(x * 1000 + 0.5) / 1000;
|
||||
}
|
||||
|
||||
void CheckEqualRounded(double expected, double actual) {
|
||||
expected = Round(expected);
|
||||
actual = Round(actual);
|
||||
EXPECT_DOUBLE_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST(HeapController, HeapGrowingFactor) {
|
||||
CheckEqualRounded(HeapController::kMaxHeapGrowingFactor,
|
||||
HeapController::HeapGrowingFactor(34, 1, 4.0));
|
||||
CheckEqualRounded(3.553, HeapController::HeapGrowingFactor(45, 1, 4.0));
|
||||
CheckEqualRounded(2.830, HeapController::HeapGrowingFactor(50, 1, 4.0));
|
||||
CheckEqualRounded(1.478, HeapController::HeapGrowingFactor(100, 1, 4.0));
|
||||
CheckEqualRounded(1.193, HeapController::HeapGrowingFactor(200, 1, 4.0));
|
||||
CheckEqualRounded(1.121, HeapController::HeapGrowingFactor(300, 1, 4.0));
|
||||
CheckEqualRounded(HeapController::HeapGrowingFactor(300, 1, 4.0),
|
||||
HeapController::HeapGrowingFactor(600, 2, 4.0));
|
||||
CheckEqualRounded(HeapController::kMinHeapGrowingFactor,
|
||||
HeapController::HeapGrowingFactor(400, 1, 4.0));
|
||||
}
|
||||
|
||||
TEST(HeapController, MaxHeapGrowingFactor) {
|
||||
CheckEqualRounded(1.3, HeapController::MaxHeapGrowingFactor(
|
||||
HeapController::kMinOldGenerationSize * MB));
|
||||
CheckEqualRounded(1.600, HeapController::MaxHeapGrowingFactor(
|
||||
HeapController::kMaxOldGenerationSize / 2 * MB));
|
||||
CheckEqualRounded(1.999, HeapController::MaxHeapGrowingFactor(
|
||||
(HeapController::kMaxOldGenerationSize -
|
||||
Heap::kPointerMultiplier) *
|
||||
MB));
|
||||
CheckEqualRounded(
|
||||
4.0,
|
||||
HeapController::MaxHeapGrowingFactor(
|
||||
static_cast<size_t>(HeapController::kMaxOldGenerationSize) * MB));
|
||||
}
|
||||
|
||||
TEST(HeapController, OldGenerationSize) {
|
||||
uint64_t configurations[][2] = {
|
||||
{0, HeapController::kMinOldGenerationSize},
|
||||
{512, HeapController::kMinOldGenerationSize},
|
||||
{1 * GB, 256 * Heap::kPointerMultiplier},
|
||||
{2 * static_cast<uint64_t>(GB), 512 * Heap::kPointerMultiplier},
|
||||
{4 * static_cast<uint64_t>(GB), HeapController::kMaxOldGenerationSize},
|
||||
{8 * static_cast<uint64_t>(GB), HeapController::kMaxOldGenerationSize}};
|
||||
|
||||
for (auto configuration : configurations) {
|
||||
ASSERT_EQ(configuration[1],
|
||||
static_cast<uint64_t>(
|
||||
Heap::ComputeMaxOldGenerationSize(configuration[0])));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -21,47 +21,6 @@ namespace internal {
|
||||
|
||||
typedef TestWithIsolate HeapTest;
|
||||
|
||||
double Round(double x) {
|
||||
// Round to three digits.
|
||||
return floor(x * 1000 + 0.5) / 1000;
|
||||
}
|
||||
|
||||
|
||||
void CheckEqualRounded(double expected, double actual) {
|
||||
expected = Round(expected);
|
||||
actual = Round(actual);
|
||||
EXPECT_DOUBLE_EQ(expected, actual);
|
||||
}
|
||||
|
||||
|
||||
TEST(Heap, HeapGrowingFactor) {
|
||||
CheckEqualRounded(Heap::kMaxHeapGrowingFactor,
|
||||
Heap::HeapGrowingFactor(34, 1, 4.0));
|
||||
CheckEqualRounded(3.553, Heap::HeapGrowingFactor(45, 1, 4.0));
|
||||
CheckEqualRounded(2.830, Heap::HeapGrowingFactor(50, 1, 4.0));
|
||||
CheckEqualRounded(1.478, Heap::HeapGrowingFactor(100, 1, 4.0));
|
||||
CheckEqualRounded(1.193, Heap::HeapGrowingFactor(200, 1, 4.0));
|
||||
CheckEqualRounded(1.121, Heap::HeapGrowingFactor(300, 1, 4.0));
|
||||
CheckEqualRounded(Heap::HeapGrowingFactor(300, 1, 4.0),
|
||||
Heap::HeapGrowingFactor(600, 2, 4.0));
|
||||
CheckEqualRounded(Heap::kMinHeapGrowingFactor,
|
||||
Heap::HeapGrowingFactor(400, 1, 4.0));
|
||||
}
|
||||
|
||||
TEST(Heap, MaxHeapGrowingFactor) {
|
||||
CheckEqualRounded(
|
||||
1.3, Heap::MaxHeapGrowingFactor(Heap::kMinOldGenerationSize * MB));
|
||||
CheckEqualRounded(
|
||||
1.600, Heap::MaxHeapGrowingFactor(Heap::kMaxOldGenerationSize / 2 * MB));
|
||||
CheckEqualRounded(
|
||||
1.999,
|
||||
Heap::MaxHeapGrowingFactor(
|
||||
(Heap::kMaxOldGenerationSize - Heap::kPointerMultiplier) * MB));
|
||||
CheckEqualRounded(4.0,
|
||||
Heap::MaxHeapGrowingFactor(
|
||||
static_cast<size_t>(Heap::kMaxOldGenerationSize) * MB));
|
||||
}
|
||||
|
||||
TEST(Heap, SemiSpaceSize) {
|
||||
const size_t KB = static_cast<size_t>(i::KB);
|
||||
const size_t MB = static_cast<size_t>(i::MB);
|
||||
@ -73,22 +32,6 @@ TEST(Heap, SemiSpaceSize) {
|
||||
ASSERT_EQ(8u * pm * MB, i::Heap::ComputeMaxSemiSpaceSize(4095u * MB) * KB);
|
||||
}
|
||||
|
||||
TEST(Heap, OldGenerationSize) {
|
||||
uint64_t configurations[][2] = {
|
||||
{0, i::Heap::kMinOldGenerationSize},
|
||||
{512, i::Heap::kMinOldGenerationSize},
|
||||
{1 * i::GB, 256 * i::Heap::kPointerMultiplier},
|
||||
{2 * static_cast<uint64_t>(i::GB), 512 * i::Heap::kPointerMultiplier},
|
||||
{4 * static_cast<uint64_t>(i::GB), i::Heap::kMaxOldGenerationSize},
|
||||
{8 * static_cast<uint64_t>(i::GB), i::Heap::kMaxOldGenerationSize}};
|
||||
|
||||
for (auto configuration : configurations) {
|
||||
ASSERT_EQ(configuration[1],
|
||||
static_cast<uint64_t>(
|
||||
i::Heap::ComputeMaxOldGenerationSize(configuration[0])));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(HeapTest, ASLR) {
|
||||
#if V8_TARGET_ARCH_X64
|
||||
#if V8_OS_MACOSX
|
||||
|
Loading…
Reference in New Issue
Block a user