Home | History | Annotate | Download | only in heap
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/heap/scavenge-job.h"
      6 
      7 #include "src/base/platform/time.h"
      8 #include "src/heap/gc-tracer.h"
      9 #include "src/heap/heap-inl.h"
     10 #include "src/heap/heap.h"
     11 #include "src/isolate.h"
     12 #include "src/v8.h"
     13 #include "src/vm-state-inl.h"
     14 
     15 namespace v8 {
     16 namespace internal {
     17 
     18 
     19 const double ScavengeJob::kMaxAllocationLimitAsFractionOfNewSpace = 0.8;
     20 
     21 void ScavengeJob::IdleTask::RunInternal(double deadline_in_seconds) {
     22   VMState<GC> state(isolate());
     23   TRACE_EVENT_CALL_STATS_SCOPED(isolate(), "v8", "V8.Task");
     24   Heap* heap = isolate()->heap();
     25   double deadline_in_ms =
     26       deadline_in_seconds *
     27       static_cast<double>(base::Time::kMillisecondsPerSecond);
     28   double start_ms = heap->MonotonicallyIncreasingTimeInMs();
     29   double idle_time_in_ms = deadline_in_ms - start_ms;
     30   double scavenge_speed_in_bytes_per_ms =
     31       heap->tracer()->ScavengeSpeedInBytesPerMillisecond();
     32   size_t new_space_size = heap->new_space()->Size();
     33   size_t new_space_capacity = heap->new_space()->Capacity();
     34 
     35   job_->NotifyIdleTask();
     36 
     37   if (ReachedIdleAllocationLimit(scavenge_speed_in_bytes_per_ms, new_space_size,
     38                                  new_space_capacity)) {
     39     if (EnoughIdleTimeForScavenge(
     40             idle_time_in_ms, scavenge_speed_in_bytes_per_ms, new_space_size)) {
     41       heap->CollectGarbage(NEW_SPACE, GarbageCollectionReason::kIdleTask);
     42     } else {
     43       // Immediately request another idle task that can get larger idle time.
     44       job_->RescheduleIdleTask(heap);
     45     }
     46   }
     47 }
     48 
     49 bool ScavengeJob::ReachedIdleAllocationLimit(
     50     double scavenge_speed_in_bytes_per_ms, size_t new_space_size,
     51     size_t new_space_capacity) {
     52   if (scavenge_speed_in_bytes_per_ms == 0) {
     53     scavenge_speed_in_bytes_per_ms = kInitialScavengeSpeedInBytesPerMs;
     54   }
     55 
     56   // Set the allocation limit to the number of bytes we can scavenge in an
     57   // average idle task.
     58   double allocation_limit = kAverageIdleTimeMs * scavenge_speed_in_bytes_per_ms;
     59 
     60   // Keep the limit smaller than the new space capacity.
     61   allocation_limit =
     62       Min<double>(allocation_limit,
     63                   new_space_capacity * kMaxAllocationLimitAsFractionOfNewSpace);
     64   // Adjust the limit to take into account bytes that will be allocated until
     65   // the next check and keep the limit large enough to avoid scavenges in tiny
     66   // new space.
     67   allocation_limit =
     68       Max<double>(allocation_limit - kBytesAllocatedBeforeNextIdleTask,
     69                   kMinAllocationLimit);
     70 
     71   return allocation_limit <= new_space_size;
     72 }
     73 
     74 bool ScavengeJob::EnoughIdleTimeForScavenge(
     75     double idle_time_in_ms, double scavenge_speed_in_bytes_per_ms,
     76     size_t new_space_size) {
     77   if (scavenge_speed_in_bytes_per_ms == 0) {
     78     scavenge_speed_in_bytes_per_ms = kInitialScavengeSpeedInBytesPerMs;
     79   }
     80   return new_space_size <= idle_time_in_ms * scavenge_speed_in_bytes_per_ms;
     81 }
     82 
     83 
     84 void ScavengeJob::RescheduleIdleTask(Heap* heap) {
     85   // Make sure that we don't reschedule more than one time.
     86   // Otherwise, we might spam the scheduler with idle tasks.
     87   if (!idle_task_rescheduled_) {
     88     ScheduleIdleTask(heap);
     89     idle_task_rescheduled_ = true;
     90   }
     91 }
     92 
     93 
     94 void ScavengeJob::ScheduleIdleTaskIfNeeded(Heap* heap, int bytes_allocated) {
     95   bytes_allocated_since_the_last_task_ += bytes_allocated;
     96   if (bytes_allocated_since_the_last_task_ >=
     97       static_cast<int>(kBytesAllocatedBeforeNextIdleTask)) {
     98     ScheduleIdleTask(heap);
     99     bytes_allocated_since_the_last_task_ = 0;
    100     idle_task_rescheduled_ = false;
    101   }
    102 }
    103 
    104 
    105 void ScavengeJob::ScheduleIdleTask(Heap* heap) {
    106   if (!idle_task_pending_ && !heap->IsTearingDown()) {
    107     v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(heap->isolate());
    108     if (V8::GetCurrentPlatform()->IdleTasksEnabled(isolate)) {
    109       idle_task_pending_ = true;
    110       auto task = new IdleTask(heap->isolate(), this);
    111       V8::GetCurrentPlatform()->CallIdleOnForegroundThread(isolate, task);
    112     }
    113   }
    114 }
    115 }  // namespace internal
    116 }  // namespace v8
    117