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/debugger.h"
|
||||||
#include "vm/exceptions.h"
|
#include "vm/exceptions.h"
|
||||||
#include "vm/flags.h"
|
#include "vm/flags.h"
|
||||||
|
#include "vm/heap/heap.h"
|
||||||
|
#include "vm/isolate.h"
|
||||||
#include "vm/message.h"
|
#include "vm/message.h"
|
||||||
#include "vm/native_entry.h"
|
#include "vm/native_entry.h"
|
||||||
#include "vm/object.h"
|
#include "vm/object.h"
|
||||||
|
@ -164,4 +166,16 @@ DEFINE_NATIVE_ENTRY(Developer_getIsolateIDFromSendPort, 0, 1) {
|
||||||
#endif
|
#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
|
} // 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_log, 8) \
|
||||||
V(Developer_postEvent, 2) \
|
V(Developer_postEvent, 2) \
|
||||||
V(Developer_webServerControl, 3) \
|
V(Developer_webServerControl, 3) \
|
||||||
|
V(Developer_reachability_barrier, 0) \
|
||||||
V(Double_hashCode, 1) \
|
V(Double_hashCode, 1) \
|
||||||
V(Double_getIsNegative, 1) \
|
V(Double_getIsNegative, 1) \
|
||||||
V(Double_getIsInfinite, 1) \
|
V(Double_getIsInfinite, 1) \
|
||||||
|
|
|
@ -58,6 +58,8 @@ Heap::Heap(IsolateGroup* isolate_group,
|
||||||
old_weak_tables_[sel] = new WeakTable();
|
old_weak_tables_[sel] = new WeakTable();
|
||||||
}
|
}
|
||||||
stats_.num_ = 0;
|
stats_.num_ = 0;
|
||||||
|
stats_.state_ = kInitial;
|
||||||
|
stats_.reachability_barrier_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Heap::~Heap() {
|
Heap::~Heap() {
|
||||||
|
|
|
@ -51,6 +51,20 @@ class Heap {
|
||||||
kNumWeakSelectors
|
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.
|
// Pattern for unused new space and swept old space.
|
||||||
static const uint8_t kZapByte = 0xf3;
|
static const uint8_t kZapByte = 0xf3;
|
||||||
|
|
||||||
|
@ -273,6 +287,8 @@ class Heap {
|
||||||
JSONStream* stream) {
|
JSONStream* stream) {
|
||||||
old_space_.PrintHeapMapToJSONStream(isolate_group, stream);
|
old_space_.PrintHeapMapToJSONStream(isolate_group, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
intptr_t ReachabilityBarrier() { return stats_.reachability_barrier_; }
|
||||||
#endif // PRODUCT
|
#endif // PRODUCT
|
||||||
|
|
||||||
IsolateGroup* isolate_group() const { return isolate_group_; }
|
IsolateGroup* isolate_group() const { return isolate_group_; }
|
||||||
|
@ -296,6 +312,8 @@ class Heap {
|
||||||
intptr_t num_;
|
intptr_t num_;
|
||||||
GCType type_;
|
GCType type_;
|
||||||
GCReason reason_;
|
GCReason reason_;
|
||||||
|
LeakCountState state_; // State to track finalization of GCed object.
|
||||||
|
intptr_t reachability_barrier_; // Tracks reachability of GCed objects.
|
||||||
|
|
||||||
class Data : public ValueObject {
|
class Data : public ValueObject {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -479,9 +479,17 @@ class MarkingWeakVisitor : public HandleVisitor {
|
||||||
|
|
||||||
void GCMarker::Prologue() {
|
void GCMarker::Prologue() {
|
||||||
isolate_group_->ReleaseStoreBuffers();
|
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 {
|
enum RootSlices {
|
||||||
kIsolate = 0,
|
kIsolate = 0,
|
||||||
|
|
|
@ -1696,10 +1696,17 @@ void Scavenger::Scavenge(Thread* thread, GCType type, GCReason reason) {
|
||||||
if (abort_) {
|
if (abort_) {
|
||||||
ReverseScavenge(&from);
|
ReverseScavenge(&from);
|
||||||
bytes_promoted = 0;
|
bytes_promoted = 0;
|
||||||
} else if ((CapacityInWords() - UsedInWords()) < KBInWords) {
|
} else {
|
||||||
// Don't scavenge again until the next old-space GC has occurred. Prevents
|
if (heap_->stats_.state_ == Heap::kInitial) {
|
||||||
// performing one scavenge per allocation as the heap limit is approached.
|
heap_->stats_.state_ = Heap::kFirstScavenge;
|
||||||
heap_->assume_scavenge_will_fail_ = true;
|
} 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());
|
ASSERT(promotion_stack_.IsEmpty());
|
||||||
MournWeakHandles();
|
MournWeakHandles();
|
||||||
|
|
|
@ -60,6 +60,9 @@ void log(String message,
|
||||||
JS('', 'console.debug("dart.developer.log", #)', items);
|
JS('', 'console.debug("dart.developer.log", #)', items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@patch
|
||||||
|
int get reachabilityBarrier => 0;
|
||||||
|
|
||||||
final _extensions = <String, ServiceExtensionHandler>{};
|
final _extensions = <String, ServiceExtensionHandler>{};
|
||||||
|
|
||||||
@patch
|
@patch
|
||||||
|
|
|
@ -35,6 +35,9 @@ void log(String message,
|
||||||
// TODO.
|
// TODO.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@patch
|
||||||
|
int get reachabilityBarrier => 0;
|
||||||
|
|
||||||
final _extensions = <String, ServiceExtensionHandler>{};
|
final _extensions = <String, ServiceExtensionHandler>{};
|
||||||
|
|
||||||
@patch
|
@patch
|
||||||
|
|
|
@ -50,6 +50,10 @@ void log(String message,
|
||||||
error, stackTrace);
|
error, stackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@patch
|
||||||
|
@pragma("vm:external-name", "Developer_reachability_barrier")
|
||||||
|
external int get reachabilityBarrier;
|
||||||
|
|
||||||
int _nextSequenceNumber = 0;
|
int _nextSequenceNumber = 0;
|
||||||
|
|
||||||
@pragma("vm:external-name", "Developer_log")
|
@pragma("vm:external-name", "Developer_log")
|
||||||
|
|
|
@ -26,6 +26,9 @@ void log(String message,
|
||||||
Object? error,
|
Object? error,
|
||||||
StackTrace? stackTrace}) {}
|
StackTrace? stackTrace}) {}
|
||||||
|
|
||||||
|
@patch
|
||||||
|
int get reachabilityBarrier => 0;
|
||||||
|
|
||||||
@patch
|
@patch
|
||||||
bool get extensionStreamHasListener => false;
|
bool get extensionStreamHasListener => false;
|
||||||
|
|
||||||
|
|
|
@ -67,3 +67,18 @@ external void log(
|
||||||
Object? error,
|
Object? error,
|
||||||
StackTrace? stackTrace,
|
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