我正在尝试模拟 Streams
的内存泄漏(仅用于教育目的),但如果垃圾回收器还没运行,我无法确定内存是否实际泄漏。
在纯 Dart 测试中,我可以强制进行 GC 扫描吗?
我正在尝试模拟 Streams
的内存泄漏(仅用于教育目的),但如果垃圾回收器还没运行,我无法确定内存是否实际泄漏。
在纯 Dart 测试中,我可以强制进行 GC 扫描吗?
dart:developer
API与Dart VM通信。这也是新的Dart DevTools从VM获取数据并执行操作(例如触发垃圾回收)的方式,您仍然可以通过进入Web界面手动执行此操作。vm_service
包,该包使得从Dart VM服务协议交互变得容易。import 'dart:developer';
import 'dart:isolate';
import 'package:vm_service/vm_service_io.dart';
Future<void> main(List<String> args) async {
final serverUri = (await Service.getInfo()).serverUri;
if (serverUri == null) {
print('Please run the application with the --observe parameter!');
return;
}
final isolateId = Service.getIsolateID(Isolate.current)!;
final vmService = await vmServiceConnectUri(_toWebSocket(serverUri));
final profile = await vmService.getAllocationProfile(isolateId, gc: true);
print(profile.memoryUsage?.heapUsage);
}
List<String> _cleanupPathSegments(Uri uri) {
final pathSegments = <String>[];
if (uri.pathSegments.isNotEmpty) {
pathSegments.addAll(uri.pathSegments.where(
(s) => s.isNotEmpty,
));
}
return pathSegments;
}
String _toWebSocket(Uri uri) {
final pathSegments = _cleanupPathSegments(uri);
pathSegments.add('ws');
return uri.replace(scheme: 'ws', pathSegments: pathSegments).toString();
}
getAllocationProfile
方法有一个可选参数gc
,文档中说明如下:
所以这确实是你能做的最好的事情。此解决方案仅在使用如果提供了
gc
并将其设置为true,则在收集分配信息之前将尝试进行垃圾回收。不能保证实际执行垃圾回收。
--observe
参数启动程序时才有效。因此,除了调试目的外,您不应该使用此功能。对于那些想在 Dart/Flutter 测试中触发 GC(例如 dart test my_file.dart
),以下是基于 @julemand101 的解决方案:
import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'dart:isolate';
import 'package:common_dart/utils/processes.dart';
import 'package:front_log/front_log.dart';
import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart' hide Isolate, Log;
import 'package:vm_service/vm_service.dart' as vm_service;
import 'package:vm_service/vm_service_io.dart';
const _kTag = 'vm_services';
FutureOr<void> runTestsInVmService(
FutureOr<void> Function(VmServiceUtil) body, {
required String selfFilePath,
}) async {
Log.d(_kTag, 'runInVmService selfFilePath=$selfFilePath Platform.script.path=${Platform.script.path}');
if (Platform.script.path == selfFilePath) {
final vmService = await VmServiceUtil.create();
tearDownAll(vmService.dispose);
await body(vmService);
} else {
test('run all tests in subprocess', () async {
await executeProcess('dart', ['run', '--enable-vm-service', selfFilePath]);
});
}
}
class VmServiceUtil {
static const _kTag = 'VmServiceUtil';
final VmService vmService;
VmServiceUtil._(this.vmService);
static Future<VmServiceUtil> create() async {
final serverUri = (await Service.getInfo()).serverUri;
if (serverUri == null) {
throw Exception('Cannot find serverUri for VmService. '
'Ensure you run like `dart run --enable-vm-service path/to/your/file.dart`');
}
final vmService = await vmServiceConnectUri(_toWebSocket(serverUri), log: _Log());
return VmServiceUtil._(vmService);
}
void dispose() {
vmService.dispose();
}
Future<void> gc() async {
final isolateId = Service.getIsolateID(Isolate.current)!;
final profile = await vmService.getAllocationProfile(isolateId, gc: true);
Log.d(_kTag, 'gc triggered (heapUsage=${profile.memoryUsage?.heapUsage})');
}
}
String _toWebSocket(Uri uri) {
final pathSegments = [...uri.pathSegments.where((s) => s.isNotEmpty), 'ws'];
return uri.replace(scheme: 'ws', pathSegments: pathSegments).toString();
}
class _Log extends vm_service.Log {
static const _kTag = 'vm_services';
@override
void warning(String message) => Log.w(_kTag, message);
@override
void severe(String message) => Log.e(_kTag, message);
}
Future<void> executeProcess(String executable, List<String> arguments) async {
Log.d(_kTag, 'executeProcess start `$executable ${arguments.join(" ")}`');
final process = await Process.start(executable, arguments);
process.stdout.listen((e) => Log.d(_kTag, String.fromCharCodes(e)));
process.stderr.listen((e) => Log.d(_kTag, '[STDERR] ${String.fromCharCodes(e)}'));
// stdout.addStream(process.stdout);
// stderr.addStream(process.stderr);
final exitCode = await process.exitCode;
Log.d(_kTag, 'executeProcess end exitCode=$exitCode');
if (exitCode != 0) {
throw Exception('Process execution failed (exitCode=$exitCode)');
}
}
如何使用它:
void main() {
runTestsInVmService(
_core,
selfFilePath: 'path/to/my/file.dart',
);
}
void _core(VmServiceUtil vmService) {
test('hello', () async {
do_something();
await vmService.gc();
do_another_thing();
});
}
dart compile aot my.dart
编译时,我不能这样做?还有其他的方法吗?谢谢! - ch271828nWeakReference
清除是否被正确处理)。还要注意,在测试中不需要检查serverUri
是否为空。 - f-person