diff --git a/modules/memory_debug.py b/modules/memory_debug.py index 20d8d1c..c5d7d1e 100644 --- a/modules/memory_debug.py +++ b/modules/memory_debug.py @@ -93,11 +93,40 @@ def _current_vms_mb() -> Optional[float]: return None -def get_memory_info() -> Dict[str, Optional[float]]: - return { +def _get_detailed_memory_info() -> Dict[str, Optional[float]]: + """获取更详细的内存指标,更接近 macOS 活动监视器。""" + result: Dict[str, Optional[float]] = { "rss_mb": _current_rss_mb(), "vms_mb": _current_vms_mb(), + "uss_mb": None, + "shared_mb": None, + "private_mb": None, + "data_mb": None, + "dirty_mb": None, + "compressed_mb": None, } + try: + import psutil + proc = psutil.Process() + info = proc.memory_info() + # uss 只在 memory_full_info() 中提供 + try: + full = proc.memory_full_info() + result["uss_mb"] = getattr(full, "uss", None) / (1024 * 1024) if getattr(full, "uss", None) else None + result["compressed_mb"] = getattr(full, "compressed", None) / (1024 * 1024) if getattr(full, "compressed", None) else None + except Exception: + pass + result["shared_mb"] = getattr(info, "shared", None) / (1024 * 1024) if getattr(info, "shared", None) else None + result["private_mb"] = getattr(info, "private", None) / (1024 * 1024) if getattr(info, "private", None) else None + result["data_mb"] = getattr(info, "data", None) / (1024 * 1024) if getattr(info, "data", None) else None + result["dirty_mb"] = getattr(info, "dirty", None) / (1024 * 1024) if getattr(info, "dirty", None) else None + except Exception: + pass + return result + + +def get_memory_info() -> Dict[str, Optional[float]]: + return _get_detailed_memory_info() def _estimate_text_size(text: Any) -> int: @@ -247,20 +276,21 @@ class TopAllocator: def _periodic_sampling( - interval_seconds: float = 1.0, - spike_threshold_mb: float = 100.0, - max_records: int = 3, + interval_seconds: float = 0.1, + spike_threshold_mb: float = 50.0, + max_records: int = 10, ) -> None: - """后台线程:每秒采样一次内存,发现快速增长或峰值时记录。""" + """后台线程:每 100ms 采样一次内存,发现快速增长或峰值时记录。""" global _last_sample_rss_mb, _peak_rss_mb records_since_spike = 0 + last_regular_log = 0.0 while ENABLED: try: info = get_memory_info() rss = info.get("rss_mb") - vms = info.get("vms_mb") if rss is not None: spike = False + delta = None if _last_sample_rss_mb is not None: delta = rss - _last_sample_rss_mb if delta >= spike_threshold_mb: @@ -271,19 +301,16 @@ def _periodic_sampling( records_since_spike -= 1 log_memory_event( "memory_periodic_spike", - rss_mb=rss, - vms_mb=vms, - peak_rss_mb=_peak_rss_mb, - delta_from_last_mb=(delta if spike else None), + **info, + delta_from_last_mb=delta, ) else: - # 每 5 秒记录一次常规采样,避免日志过多 - if int(time.time()) % 5 == 0: + now = time.time() + if now - last_regular_log >= 2.0: + last_regular_log = now log_memory_event( "memory_periodic_sample", - rss_mb=rss, - vms_mb=vms, - peak_rss_mb=_peak_rss_mb, + **info, ) except Exception: pass @@ -291,8 +318,8 @@ def _periodic_sampling( def start_periodic_sampling( - interval_seconds: float = 1.0, - spike_threshold_mb: float = 100.0, + interval_seconds: float = 0.1, + spike_threshold_mb: float = 50.0, ) -> None: """启动后台内存采样线程。幂等:重复调用不会启动多个线程。""" global _sample_thread