mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
[VM/Developer] Expose through the developer API a gc count for the isolate group that could potentially trigger execution of associated finalizers of objects that were collected.
TEST=reachability_test.dart Change-Id: I27bdac49a4b7069ca428282d55d8ec572c9fc0e0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/266424 Reviewed-by: Ben Konyi <bkonyi@google.com> Commit-Queue: Siva Annamalai <asiva@google.com>
This commit is contained in:
parent
f915ad9e87
commit
b464175a29
12 changed files with 120 additions and 5 deletions
|
@ -9,6 +9,8 @@
|
|||
#include "vm/debugger.h"
|
||||
#include "vm/exceptions.h"
|
||||
#include "vm/flags.h"
|
||||
#include "vm/heap/heap.h"
|
||||
#include "vm/isolate.h"
|
||||
#include "vm/message.h"
|
||||
#include "vm/native_entry.h"
|
||||
#include "vm/object.h"
|
||||
|
@ -164,4 +166,16 @@ DEFINE_NATIVE_ENTRY(Developer_getIsolateIDFromSendPort, 0, 1) {
|
|||
#endif
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Developer_reachability_barrier, 0, 0) {
|
||||
#if defined(PRODUCT)
|
||||
return Smi::New(0);
|
||||
#else
|
||||
IsolateGroup* isolate_group = thread->isolate_group();
|
||||
ASSERT(isolate_group != nullptr);
|
||||
Heap* heap = isolate_group->heap();
|
||||
ASSERT(heap != nullptr);
|
||||
return Integer::New(heap->ReachabilityBarrier());
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
|
37
runtime/tests/vm/dart/reachability_test.dart
Normal file
37
runtime/tests/vm/dart/reachability_test.dart
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
//
|
||||
// VMOptions=--new_gen_semi_max_size=1
|
||||
|
||||
import 'dart:_internal' show VMInternalsForTesting;
|
||||
import 'dart:developer';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
var g_arr = List<List<int>?>.filled(1000, null);
|
||||
void main() {
|
||||
var count = reachabilityBarrier;
|
||||
var l_arr = List<List<int>?>.filled(1000, null);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
l_arr[i] = List<int>.filled(100, 0);
|
||||
}
|
||||
for (int j = 0; j < 1000; j++) {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
g_arr[i] = l_arr[i];
|
||||
}
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
l_arr[i] = List<int>.filled(100, 0);
|
||||
}
|
||||
}
|
||||
VMInternalsForTesting.collectAllGarbage();
|
||||
for (int j = 0; j < 1000; j++) {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
g_arr[i] = l_arr[i];
|
||||
}
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
l_arr[i] = List<int>.filled(100, 0);
|
||||
}
|
||||
}
|
||||
VMInternalsForTesting.collectAllGarbage();
|
||||
Expect.isTrue(reachabilityBarrier > count);
|
||||
}
|
|
@ -80,6 +80,7 @@ namespace dart {
|
|||
V(Developer_log, 8) \
|
||||
V(Developer_postEvent, 2) \
|
||||
V(Developer_webServerControl, 3) \
|
||||
V(Developer_reachability_barrier, 0) \
|
||||
V(Double_hashCode, 1) \
|
||||
V(Double_getIsNegative, 1) \
|
||||
V(Double_getIsInfinite, 1) \
|
||||
|
|
|
@ -58,6 +58,8 @@ Heap::Heap(IsolateGroup* isolate_group,
|
|||
old_weak_tables_[sel] = new WeakTable();
|
||||
}
|
||||
stats_.num_ = 0;
|
||||
stats_.state_ = kInitial;
|
||||
stats_.reachability_barrier_ = 0;
|
||||
}
|
||||
|
||||
Heap::~Heap() {
|
||||
|
|
|
@ -51,6 +51,20 @@ class Heap {
|
|||
kNumWeakSelectors
|
||||
};
|
||||
|
||||
// States for a state machine that represents the worst-case set of GCs
|
||||
// that an unreachable object could survive before begin collected:
|
||||
// a new-space object that is involved with a cycle with an old-space object
|
||||
// is copied to survivor space, then promoted during concurrent marking,
|
||||
// and finally proven unreachable in the next round of old-gen marking.
|
||||
// We ignore the case of unreachable-but-not-yet-collected objects being
|
||||
// made reachable again by allInstances.
|
||||
enum LeakCountState {
|
||||
kInitial = 0,
|
||||
kFirstScavenge,
|
||||
kSecondScavenge,
|
||||
kMarkingStart,
|
||||
};
|
||||
|
||||
// Pattern for unused new space and swept old space.
|
||||
static const uint8_t kZapByte = 0xf3;
|
||||
|
||||
|
@ -273,6 +287,8 @@ class Heap {
|
|||
JSONStream* stream) {
|
||||
old_space_.PrintHeapMapToJSONStream(isolate_group, stream);
|
||||
}
|
||||
|
||||
intptr_t ReachabilityBarrier() { return stats_.reachability_barrier_; }
|
||||
#endif // PRODUCT
|
||||
|
||||
IsolateGroup* isolate_group() const { return isolate_group_; }
|
||||
|
@ -296,6 +312,8 @@ class Heap {
|
|||
intptr_t num_;
|
||||
GCType type_;
|
||||
GCReason reason_;
|
||||
LeakCountState state_; // State to track finalization of GCed object.
|
||||
intptr_t reachability_barrier_; // Tracks reachability of GCed objects.
|
||||
|
||||
class Data : public ValueObject {
|
||||
public:
|
||||
|
|
|
@ -479,9 +479,17 @@ class MarkingWeakVisitor : public HandleVisitor {
|
|||
|
||||
void GCMarker::Prologue() {
|
||||
isolate_group_->ReleaseStoreBuffers();
|
||||
if (heap_->stats_.state_ == Heap::kSecondScavenge) {
|
||||
heap_->stats_.state_ = Heap::kMarkingStart;
|
||||
}
|
||||
}
|
||||
|
||||
void GCMarker::Epilogue() {}
|
||||
void GCMarker::Epilogue() {
|
||||
if (heap_->stats_.state_ == Heap::kMarkingStart) {
|
||||
heap_->stats_.state_ = Heap::kInitial;
|
||||
heap_->stats_.reachability_barrier_ += 1;
|
||||
}
|
||||
}
|
||||
|
||||
enum RootSlices {
|
||||
kIsolate = 0,
|
||||
|
|
|
@ -1696,10 +1696,17 @@ void Scavenger::Scavenge(Thread* thread, GCType type, GCReason reason) {
|
|||
if (abort_) {
|
||||
ReverseScavenge(&from);
|
||||
bytes_promoted = 0;
|
||||
} else if ((CapacityInWords() - UsedInWords()) < KBInWords) {
|
||||
// Don't scavenge again until the next old-space GC has occurred. Prevents
|
||||
// performing one scavenge per allocation as the heap limit is approached.
|
||||
heap_->assume_scavenge_will_fail_ = true;
|
||||
} else {
|
||||
if (heap_->stats_.state_ == Heap::kInitial) {
|
||||
heap_->stats_.state_ = Heap::kFirstScavenge;
|
||||
} else if (heap_->stats_.state_ == Heap::kFirstScavenge) {
|
||||
heap_->stats_.state_ = Heap::kSecondScavenge;
|
||||
}
|
||||
if ((CapacityInWords() - UsedInWords()) < KBInWords) {
|
||||
// Don't scavenge again until the next old-space GC has occurred. Prevents
|
||||
// performing one scavenge per allocation as the heap limit is approached.
|
||||
heap_->assume_scavenge_will_fail_ = true;
|
||||
}
|
||||
}
|
||||
ASSERT(promotion_stack_.IsEmpty());
|
||||
MournWeakHandles();
|
||||
|
|
|
@ -60,6 +60,9 @@ void log(String message,
|
|||
JS('', 'console.debug("dart.developer.log", #)', items);
|
||||
}
|
||||
|
||||
@patch
|
||||
int get reachabilityBarrier => 0;
|
||||
|
||||
final _extensions = <String, ServiceExtensionHandler>{};
|
||||
|
||||
@patch
|
||||
|
|
|
@ -35,6 +35,9 @@ void log(String message,
|
|||
// TODO.
|
||||
}
|
||||
|
||||
@patch
|
||||
int get reachabilityBarrier => 0;
|
||||
|
||||
final _extensions = <String, ServiceExtensionHandler>{};
|
||||
|
||||
@patch
|
||||
|
|
|
@ -50,6 +50,10 @@ void log(String message,
|
|||
error, stackTrace);
|
||||
}
|
||||
|
||||
@patch
|
||||
@pragma("vm:external-name", "Developer_reachability_barrier")
|
||||
external int get reachabilityBarrier;
|
||||
|
||||
int _nextSequenceNumber = 0;
|
||||
|
||||
@pragma("vm:external-name", "Developer_log")
|
||||
|
|
|
@ -26,6 +26,9 @@ void log(String message,
|
|||
Object? error,
|
||||
StackTrace? stackTrace}) {}
|
||||
|
||||
@patch
|
||||
int get reachabilityBarrier => 0;
|
||||
|
||||
@patch
|
||||
bool get extensionStreamHasListener => false;
|
||||
|
||||
|
|
|
@ -67,3 +67,18 @@ external void log(
|
|||
Object? error,
|
||||
StackTrace? stackTrace,
|
||||
});
|
||||
|
||||
/// Current reachability barrier state.
|
||||
///
|
||||
/// A reachability barrier state that provides a way to synchronize on
|
||||
/// reachability. At value 'x', any object that became unreachable during
|
||||
/// 'value' < 'x' has been collected and any associated finalizers have been
|
||||
/// scheduled for execution, i.e. the non-execution of a finalizer reliably
|
||||
/// indicates the object is still reachable in the previous barrier state.
|
||||
///
|
||||
/// Objects that became unreachable in the current barrier state may have not
|
||||
/// yet been collected or finalized.
|
||||
///
|
||||
/// NOTE: There are no guarantees of forward progress. An implementation may
|
||||
/// return the same value forever for this barrier state.
|
||||
external int get reachabilityBarrier;
|
||||
|
|
Loading…
Reference in a new issue