[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:
Rodrigo Bruno 2018-06-07 14:00:16 +02:00 committed by Commit Bot
parent e838e75e39
commit db4b7e7598
9 changed files with 378 additions and 272 deletions

View File

@ -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
View 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.
// Lets 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

View 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_

View File

@ -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.
// Lets 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_;

View File

@ -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;

View File

@ -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"

View File

@ -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",

View 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

View File

@ -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