memory_monitor.dart 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // memory_monitor.dart
  2. // 强化版内存监控工具类
  3. import 'dart:async';
  4. import 'dart:io';
  5. import 'package:flutter/foundation.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:logging/logging.dart';
  8. import 'package:puzzleweave/firebase/firebase_helper.dart';
  9. import 'package:puzzleweave/models/download.dart';
  10. import '../config/config.dart';
  11. final Logger _log = Logger('MemoryMonitor');
  12. /// 内存阶段定义
  13. enum MemoryState { normal, warning, critical }
  14. class MemoryMonitor with WidgetsBindingObserver {
  15. static final MemoryMonitor _instance = MemoryMonitor._internal();
  16. factory MemoryMonitor() => _instance;
  17. MemoryMonitor._internal();
  18. Timer? _monitorTimer;
  19. int _warningCount = 0;
  20. // 防止系统内存压力信号死循环
  21. DateTime? _lastSystemPressureTime;
  22. static const _systemPressureCooldown = Duration(seconds: 10);
  23. // 动态生成的阈值
  24. double _warningThreshold = 180.0;
  25. double _criticalThreshold = 250.0;
  26. VoidCallback? _onHighMemory;
  27. VoidCallback? _onCriticalMemory;
  28. // Release模式下的内存显示
  29. static final ValueNotifier<String> memoryDisplay = ValueNotifier<String>('Memory: 0MB');
  30. /// 初始化并启动监控
  31. void startMonitoring({Duration interval = const Duration(seconds: 30), VoidCallback? onHighMemory, VoidCallback? onCriticalMemory}) {
  32. _onHighMemory = onHighMemory;
  33. _onCriticalMemory = onCriticalMemory;
  34. // 1. 注册系统内存压力监听 (WidgetsBindingObserver)
  35. WidgetsBinding.instance.addObserver(this);
  36. // 2. 根据设备情况初始化动态阈值
  37. _initializeDynamicThresholds();
  38. // 3. 启动定时轮询
  39. _monitorTimer?.cancel();
  40. _monitorTimer = Timer.periodic(interval, (_) => _checkMemory());
  41. _log.info('Memory monitoring started. Warning: ${_warningThreshold}MB, Critical: ${_criticalThreshold}MB');
  42. }
  43. /// 停止监控
  44. void stopMonitoring() {
  45. WidgetsBinding.instance.removeObserver(this);
  46. _monitorTimer?.cancel();
  47. _log.info('Memory monitoring stopped');
  48. }
  49. /// 这是进程被杀前的最后一次清理机会
  50. @override
  51. void didHaveMemoryPressure() {
  52. final now = DateTime.now();
  53. // 防止频繁触发,增加冷却时间
  54. if (_lastSystemPressureTime != null && now.difference(_lastSystemPressureTime!) < _systemPressureCooldown) {
  55. _log.info('System memory pressure ignored (cooldown)');
  56. return;
  57. }
  58. _lastSystemPressureTime = now;
  59. _log.severe('!!! SYSTEM MEMORY PRESSURE: ${getCurrentMemoryMB().toStringAsFixed(1)}MB !!!');
  60. _emergencyCleanup(reason: 'system_pressure');
  61. _reportMemoryEvent('system_low_memory_signal', getCurrentMemoryMB());
  62. }
  63. /// 动态计算阈值:防止在低端机上设置过高,或在高端机上太敏感
  64. void _initializeDynamicThresholds() {
  65. if (Platform.isAndroid) {
  66. if (kDebugMode) {
  67. // Debug模式更保守,避免频繁清理
  68. _warningThreshold = 400.0;
  69. _criticalThreshold = 500.0;
  70. } else {
  71. // Release模式预计内存减半
  72. _warningThreshold = 120.0;
  73. _criticalThreshold = 160.0;
  74. }
  75. } else if (Platform.isIOS) {
  76. if (kDebugMode) {
  77. _warningThreshold = 300.0;
  78. _criticalThreshold = 400.0;
  79. } else {
  80. _warningThreshold = 150.0;
  81. _criticalThreshold = 200.0;
  82. }
  83. } else {
  84. _warningThreshold = 500.0;
  85. _criticalThreshold = 700.0;
  86. }
  87. }
  88. void _checkMemory() {
  89. if (!Platform.isAndroid && !Platform.isIOS) return;
  90. try {
  91. final memoryMB = getCurrentMemoryMB();
  92. // 更新UI显示
  93. memoryDisplay.value = 'Memory: ${memoryMB.toStringAsFixed(1)}MB';
  94. // 只在超过warning时才记录,减少日志噪音
  95. if (memoryMB > _warningThreshold) {
  96. _log.info('Memory: ${memoryMB.toStringAsFixed(1)} MB');
  97. }
  98. if (memoryMB > _criticalThreshold) {
  99. _handleCriticalMemory(memoryMB);
  100. } else if (memoryMB > _warningThreshold) {
  101. _handleHighMemory(memoryMB);
  102. } else {
  103. _warningCount = 0;
  104. }
  105. } catch (e) {
  106. _log.warning('Memory check failed: $e');
  107. }
  108. }
  109. void _handleCriticalMemory(double memoryMB) {
  110. _log.severe('CRITICAL memory usage: ${memoryMB.toStringAsFixed(1)} MB');
  111. _warningCount++;
  112. _onCriticalMemory?.call();
  113. _emergencyCleanup(reason: 'critical_threshold');
  114. _reportMemoryEvent('critical_memory_usage', memoryMB);
  115. }
  116. void _handleHighMemory(double memoryMB) {
  117. _log.warning('HIGH memory usage: ${memoryMB.toStringAsFixed(1)} MB');
  118. _warningCount++;
  119. _onHighMemory?.call();
  120. _reportMemoryEvent('high_memory_usage', memoryMB);
  121. }
  122. /// 核心清理逻辑:物理释放显存和缓存
  123. void _emergencyCleanup({String reason = 'unknown'}) {
  124. final beforeMB = getCurrentMemoryMB();
  125. _log.warning('Emergency cleanup: ${beforeMB.toStringAsFixed(1)}MB ($reason)');
  126. try {
  127. // 清理顺序很重要,先清理大头
  128. Download().clearAllData();
  129. PaintingBinding.instance.imageCache.clear();
  130. PaintingBinding.instance.imageCache.clearLiveImages();
  131. PaintingBinding.instance.handleMemoryPressure();
  132. // 立即检查效果
  133. final afterMB = getCurrentMemoryMB();
  134. final freedMB = beforeMB - afterMB;
  135. _log.info('Cleanup result: ${afterMB.toStringAsFixed(1)}MB (${freedMB > 0 ? "freed" : "increased"}: ${freedMB.abs().toStringAsFixed(1)}MB)');
  136. } catch (e) {
  137. _log.severe('Emergency cleanup failed: $e');
  138. }
  139. }
  140. void _reportMemoryEvent(String eventName, double memoryMB) {
  141. if (kReleaseMode) {
  142. FirebaseHelper.logEvent(eventName, {'memory_mb': memoryMB.round(), 'warning_count': _warningCount, 'threshold_c': _criticalThreshold.round()});
  143. }
  144. }
  145. static double getCurrentMemoryMB() {
  146. try {
  147. // RSS 包含代码段、栈、堆以及共享库。
  148. // 在三星等低端机上,这是判断 OOM 最直接的指标。
  149. return ProcessInfo.currentRss / (1024 * 1024);
  150. } catch (e) {
  151. return 0;
  152. }
  153. }
  154. /// 供外部在关键节点(如播广告前)手动清理
  155. void manualCleanup() {
  156. if (!Config.isDebug) return;
  157. _emergencyCleanup(reason: 'manual_call');
  158. }
  159. static void logMemoryUsage(String label) {
  160. if (!Config.isDebug) return;
  161. final memoryMB = getCurrentMemoryMB();
  162. memoryDisplay.value = '[$label] ${memoryMB.toStringAsFixed(1)}MB';
  163. _log.info('[$label] RSS Memory: ${memoryMB.toStringAsFixed(1)} MB');
  164. }
  165. /// 获取内存显示组件(供Release模式使用)
  166. static Widget getMemoryWidget() {
  167. return ValueListenableBuilder<String>(
  168. valueListenable: memoryDisplay,
  169. builder: (context, value, child) {
  170. return Container(
  171. padding: EdgeInsets.all(4),
  172. decoration: BoxDecoration(color: Colors.black54, borderRadius: BorderRadius.circular(4)),
  173. child: Text(value, style: TextStyle(color: Colors.white, fontSize: 12)),
  174. );
  175. },
  176. );
  177. }
  178. }