From d17e85217b456e8c62877d2c9880599f936a7b66 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 9 Jul 2025 07:47:23 +0000 Subject: [PATCH 1/3] Enhance memory analysis with detailed tracking, leak detection, and system insights Co-authored-by: jxxghp --- app/helper/memory.py | 573 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 556 insertions(+), 17 deletions(-) diff --git a/app/helper/memory.py b/app/helper/memory.py index 57e7aeec..7cb0d76c 100644 --- a/app/helper/memory.py +++ b/app/helper/memory.py @@ -2,8 +2,10 @@ import gc import sys import threading import time +import os +import tracemalloc from datetime import datetime -from typing import Optional +from typing import Optional, Dict, List, Tuple import psutil from pympler import muppy, summary, asizeof @@ -30,6 +32,10 @@ class MemoryHelper(metaclass=Singleton): self._memory_snapshot_dir = settings.LOG_PATH / "memory_snapshots" # 保留的快照文件数量 self._keep_count = settings.MEMORY_SNAPSHOT_KEEP_COUNT + + # 启用tracemalloc以获得更详细的内存信息 + if not tracemalloc.is_tracing(): + tracemalloc.start(25) # 保留25个帧 @eventmanager.register(EventType.ConfigChanged) def handle_config_changed(self, event: Event): @@ -108,15 +114,21 @@ class MemoryHelper(metaclass=Singleton): logger.info(f"开始创建内存快照: {snapshot_file}") - # 第一步:写入基本信息和对象类型统计 - self._write_basic_info(snapshot_file, memory_usage) + # 第一步:写入基本信息和系统内存统计 + self._write_system_memory_info(snapshot_file, memory_usage) - # 第二步:分析并写入类实例内存使用情况 + # 第二步:写入Python对象类型统计 + self._write_python_objects_info(snapshot_file) + + # 第三步:分析并写入类实例内存使用情况 self._append_class_analysis(snapshot_file) - # 第三步:分析并写入大内存变量详情 + # 第四步:分析并写入大内存变量详情 self._append_variable_analysis(snapshot_file) + # 第五步:分析内存泄漏和增长趋势 + self._append_memory_leak_analysis(snapshot_file) + logger.info(f"内存快照已保存: {snapshot_file}, 当前内存使用: {memory_usage / 1024 / 1024:.2f} MB") # 清理过期的快照文件(保留最近30个) @@ -125,30 +137,450 @@ class MemoryHelper(metaclass=Singleton): except Exception as e: logger.error(f"创建内存快照失败: {e}") - @staticmethod - def _write_basic_info(snapshot_file, memory_usage): + def _write_system_memory_info(self, snapshot_file, memory_usage): """ - 写入基本信息和对象类型统计 + 写入系统内存信息 """ - # 获取当前进程的内存使用情况 - all_objects = muppy.get_objects() - sum1 = summary.summarize(all_objects) - + process = psutil.Process() + memory_info = process.memory_info() + memory_percent = process.memory_percent() + + # 获取系统总内存信息 + system_memory = psutil.virtual_memory() + + # 获取内存映射信息 + memory_maps = process.memory_maps() + with open(snapshot_file, 'w', encoding='utf-8') as f: f.write(f"内存快照时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") - f.write(f"当前进程内存使用: {memory_usage / 1024 / 1024:.2f} MB\n") f.write("=" * 80 + "\n") - f.write("对象类型统计:\n") + f.write("系统内存使用情况:\n") f.write("-" * 80 + "\n") + f.write(f"当前进程内存使用: {memory_usage / 1024 / 1024:.2f} MB\n") + f.write(f"进程内存使用率: {memory_percent:.2f}%\n") + f.write(f"系统总内存: {system_memory.total / 1024 / 1024 / 1024:.2f} GB\n") + f.write(f"系统可用内存: {system_memory.available / 1024 / 1024 / 1024:.2f} GB\n") + f.write(f"系统内存使用率: {system_memory.percent:.2f}%\n") + f.write(f"进程RSS内存: {memory_info.rss / 1024 / 1024:.2f} MB\n") + f.write(f"进程VMS内存: {memory_info.vms / 1024 / 1024:.2f} MB\n") + f.write(f"进程共享内存: {memory_info.shared / 1024 / 1024:.2f} MB\n") + f.write(f"进程文本段: {memory_info.text / 1024 / 1024:.2f} MB\n") + f.write(f"进程数据段: {memory_info.data / 1024 / 1024:.2f} MB\n") + + # 分析内存映射 + f.write("\n内存映射分析:\n") + f.write("-" * 80 + "\n") + memory_regions = self._analyze_memory_maps(memory_maps) + for region_type, size_mb in memory_regions.items(): + f.write(f"{region_type}: {size_mb:.2f} MB\n") + + f.flush() + def _analyze_memory_maps(self, memory_maps) -> Dict[str, float]: + """ + 分析内存映射,按类型分类统计 + """ + regions = {} + for mmap in memory_maps: + size_mb = mmap.size / 1024 / 1024 + perms = mmap.perms + + if 'r' in perms and 'w' in perms: + region_type = "读写内存" + elif 'r' in perms and 'x' in perms: + region_type = "代码段" + elif 'r' in perms: + region_type = "只读内存" + else: + region_type = "其他内存" + + if region_type in regions: + regions[region_type] += size_mb + else: + regions[region_type] = size_mb + + return regions + + def _write_python_objects_info(self, snapshot_file): + """ + 写入Python对象类型统计信息 + """ + # 获取当前tracemalloc统计 + current, peak = tracemalloc.get_traced_memory() + + # 获取所有对象 + all_objects = muppy.get_objects() + sum1 = summary.summarize(all_objects) + + # 计算Python对象总内存 + python_total_mb = 0 + for line in summary.format_(sum1): + if '|' in line and line.strip() and not line.startswith('=') and not line.startswith('-'): + parts = line.split('|') + if len(parts) >= 3: + try: + size_str = parts[2].strip() + if 'MB' in size_str: + size_mb = float(size_str.replace('MB', '').strip()) + python_total_mb += size_mb + except: + pass + + with open(snapshot_file, 'a', encoding='utf-8') as f: + f.write("\n" + "=" * 80 + "\n") + f.write("Python内存使用情况:\n") + f.write("-" * 80 + "\n") + f.write(f"tracemalloc当前内存: {current / 1024 / 1024:.2f} MB\n") + f.write(f"tracemalloc峰值内存: {peak / 1024 / 1024:.2f} MB\n") + f.write(f"Python对象总内存: {python_total_mb:.2f} MB\n") + f.write(f"未统计内存(可能为C扩展): {self._get_unaccounted_memory():.2f} MB\n") + + f.write("\n对象类型统计:\n") + f.write("-" * 80 + "\n") # 写入对象统计信息 for line in summary.format_(sum1): f.write(line + "\n") - - # 立即刷新到磁盘 + f.flush() - logger.debug("基本信息已写入快照文件") + def _get_unaccounted_memory(self) -> float: + """ + 计算未统计的内存(可能是C扩展、系统缓存等) + """ + try: + # 获取进程总内存 + process = psutil.Process() + total_memory = process.memory_info().rss / 1024 / 1024 # MB + + # 获取Python对象总内存 + all_objects = muppy.get_objects() + sum1 = summary.summarize(all_objects) + + python_total_mb = 0 + for line in summary.format_(sum1): + if '|' in line and line.strip() and not line.startswith('=') and not line.startswith('-'): + parts = line.split('|') + if len(parts) >= 3: + try: + size_str = parts[2].strip() + if 'MB' in size_str: + size_mb = float(size_str.replace('MB', '').strip()) + python_total_mb += size_mb + except: + pass + + return max(0, total_memory - python_total_mb) + except: + return 0.0 + + def _append_memory_leak_analysis(self, snapshot_file): + """ + 分析内存泄漏和增长趋势 + """ + with open(snapshot_file, 'a', encoding='utf-8') as f: + f.write("\n" + "=" * 80 + "\n") + f.write("内存泄漏分析:\n") + f.write("-" * 80 + "\n") + + # 获取tracemalloc统计 + current, peak = tracemalloc.get_traced_memory() + f.write(f"当前tracemalloc内存: {current / 1024 / 1024:.2f} MB\n") + f.write(f"tracemalloc峰值内存: {peak / 1024 / 1024:.2f} MB\n") + + # 获取内存分配统计 + try: + stats = tracemalloc.get_traced_memory() + f.write(f"内存分配统计: {stats}\n") + + # 获取前10个内存分配最多的位置 + snapshot = tracemalloc.take_snapshot() + top_stats = snapshot.statistics('lineno') + + f.write("\n内存分配最多的位置 (前10个):\n") + f.write("-" * 80 + "\n") + for i, stat in enumerate(top_stats[:10], 1): + f.write(f"{i:2d}. {stat.count:>8} 个对象, {stat.size / 1024 / 1024:>8.2f} MB\n") + f.write(f" {stat.traceback.format()}\n") + + except Exception as e: + f.write(f"获取内存分配统计失败: {e}\n") + + # 垃圾回收统计 + f.write("\n垃圾回收统计:\n") + f.write("-" * 80 + "\n") + for i in range(3): + count = gc.get_count()[i] + f.write(f"GC代 {i}: {count} 次\n") + + # 获取不可达对象数量 + unreachable = len(gc.garbage) + f.write(f"不可达对象数量: {unreachable}\n") + + f.flush() + + logger.debug("内存泄漏分析已完成并写入") + + def create_detailed_memory_analysis(self): + """ + 创建详细的内存分析报告,专门用于诊断内存问题 + """ + try: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + analysis_file = self._memory_snapshot_dir / f"detailed_memory_analysis_{timestamp}.txt" + + logger.info(f"开始创建详细内存分析: {analysis_file}") + + with open(analysis_file, 'w', encoding='utf-8') as f: + f.write(f"详细内存分析报告 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") + f.write("=" * 100 + "\n\n") + + # 1. 系统级内存分析 + self._write_detailed_system_analysis(f) + + # 2. Python对象深度分析 + self._write_detailed_python_analysis(f) + + # 3. 内存映射详细分析 + self._write_detailed_memory_maps(f) + + # 4. 大对象分析 + self._write_detailed_large_objects(f) + + # 5. 内存泄漏检测 + self._write_memory_leak_detection(f) + + logger.info(f"详细内存分析已保存: {analysis_file}") + return analysis_file + + except Exception as e: + logger.error(f"创建详细内存分析失败: {e}") + return None + + def _write_detailed_system_analysis(self, f): + """ + 写入详细的系统内存分析 + """ + f.write("1. 系统级内存分析\n") + f.write("-" * 50 + "\n") + + process = psutil.Process() + memory_info = process.memory_info() + + f.write(f"进程ID: {process.pid}\n") + f.write(f"进程名称: {process.name()}\n") + f.write(f"进程命令行: {' '.join(process.cmdline())}\n\n") + + f.write("内存使用详情:\n") + f.write(f" RSS (物理内存): {memory_info.rss / 1024 / 1024:.2f} MB\n") + f.write(f" VMS (虚拟内存): {memory_info.vms / 1024 / 1024:.2f} MB\n") + f.write(f" 共享内存: {memory_info.shared / 1024 / 1024:.2f} MB\n") + f.write(f" 文本段: {memory_info.text / 1024 / 1024:.2f} MB\n") + f.write(f" 数据段: {memory_info.data / 1024 / 1024:.2f} MB\n") + f.write(f" 库内存: {memory_info.lib / 1024 / 1024:.2f} MB\n") + f.write(f" 脏页: {memory_info.dirty / 1024 / 1024:.2f} MB\n") + + # 系统内存信息 + system_memory = psutil.virtual_memory() + f.write(f"\n系统内存:\n") + f.write(f" 总内存: {system_memory.total / 1024 / 1024 / 1024:.2f} GB\n") + f.write(f" 可用内存: {system_memory.available / 1024 / 1024 / 1024:.2f} GB\n") + f.write(f" 使用率: {system_memory.percent:.2f}%\n") + f.write(f" 缓存: {system_memory.cached / 1024 / 1024 / 1024:.2f} GB\n") + f.write(f" 缓冲区: {system_memory.buffers / 1024 / 1024 / 1024:.2f} GB\n") + + f.write("\n" + "=" * 100 + "\n\n") + + def _write_detailed_python_analysis(self, f): + """ + 写入详细的Python对象分析 + """ + f.write("2. Python对象深度分析\n") + f.write("-" * 50 + "\n") + + # 强制垃圾回收 + collected = gc.collect() + f.write(f"垃圾回收清理对象数: {collected}\n\n") + + # 获取所有对象 + all_objects = muppy.get_objects() + f.write(f"总对象数: {len(all_objects):,}\n") + + # 按类型统计 + type_stats = {} + for obj in all_objects: + obj_type = type(obj).__name__ + if obj_type not in type_stats: + type_stats[obj_type] = {'count': 0, 'size': 0} + type_stats[obj_type]['count'] += 1 + type_stats[obj_type]['size'] += sys.getsizeof(obj) + + # 按大小排序 + sorted_types = sorted(type_stats.items(), key=lambda x: x[1]['size'], reverse=True) + + f.write("对象类型统计 (按内存大小排序):\n") + f.write(f"{'类型':<20} {'数量':<10} {'总大小(MB)':<12} {'平均大小(B)':<12}\n") + f.write("-" * 60 + "\n") + + total_python_memory = 0 + for obj_type, stats in sorted_types[:20]: # 只显示前20个 + size_mb = stats['size'] / 1024 / 1024 + avg_size = stats['size'] / stats['count'] if stats['count'] > 0 else 0 + total_python_memory += size_mb + f.write(f"{obj_type:<20} {stats['count']:<10,} {size_mb:<12.2f} {avg_size:<12.1f}\n") + + f.write(f"\nPython对象总内存: {total_python_memory:.2f} MB\n") + + # 计算未统计内存 + process = psutil.Process() + total_memory = process.memory_info().rss / 1024 / 1024 + unaccounted = total_memory - total_python_memory + f.write(f"未统计内存: {unaccounted:.2f} MB ({unaccounted/total_memory*100:.1f}%)\n") + + f.write("\n" + "=" * 100 + "\n\n") + + def _write_detailed_memory_maps(self, f): + """ + 写入详细的内存映射分析 + """ + f.write("3. 内存映射详细分析\n") + f.write("-" * 50 + "\n") + + process = psutil.Process() + memory_maps = process.memory_maps() + + # 按权限分类 + perm_stats = {} + file_stats = {} + + for mmap in memory_maps: + size_mb = mmap.size / 1024 / 1024 + perms = mmap.perms + + # 按权限统计 + if perms not in perm_stats: + perm_stats[perms] = {'count': 0, 'size': 0} + perm_stats[perms]['count'] += 1 + perm_stats[perms]['size'] += size_mb + + # 按文件统计 + if mmap.path: + if mmap.path not in file_stats: + file_stats[mmap.path] = {'count': 0, 'size': 0} + file_stats[mmap.path]['count'] += 1 + file_stats[mmap.path]['size'] += size_mb + + f.write("按权限分类的内存映射:\n") + f.write(f"{'权限':<10} {'数量':<8} {'大小(MB)':<12}\n") + f.write("-" * 35 + "\n") + for perms, stats in sorted(perm_stats.items(), key=lambda x: x[1]['size'], reverse=True): + f.write(f"{perms:<10} {stats['count']:<8} {stats['size']:<12.2f}\n") + + f.write(f"\n按文件分类的内存映射 (前10个):\n") + f.write(f"{'文件路径':<50} {'大小(MB)':<12}\n") + f.write("-" * 70 + "\n") + for path, stats in sorted(file_stats.items(), key=lambda x: x[1]['size'], reverse=True)[:10]: + if len(path) > 47: + path = path[:44] + "..." + f.write(f"{path:<50} {stats['size']:<12.2f}\n") + + f.write("\n" + "=" * 100 + "\n\n") + + def _write_detailed_large_objects(self, f): + """ + 写入大对象详细分析 + """ + f.write("4. 大对象详细分析\n") + f.write("-" * 50 + "\n") + + all_objects = muppy.get_objects() + large_objects = [] + + for obj in all_objects: + try: + size = asizeof.asizeof(obj) + if size > 1024 * 1024: # 大于1MB的对象 + large_objects.append((obj, size)) + except: + continue + + # 按大小排序 + large_objects.sort(key=lambda x: x[1], reverse=True) + + f.write(f"大对象 (>1MB) 数量: {len(large_objects)}\n\n") + + for i, (obj, size) in enumerate(large_objects[:20], 1): # 只显示前20个 + size_mb = size / 1024 / 1024 + obj_type = type(obj).__name__ + + f.write(f"{i:2d}. {obj_type} - {size_mb:.2f} MB\n") + + # 尝试获取更多信息 + try: + if isinstance(obj, dict): + f.write(f" 字典项数: {len(obj)}\n") + if obj: + sample_keys = list(obj.keys())[:3] + f.write(f" 示例键: {sample_keys}\n") + elif isinstance(obj, (list, tuple)): + f.write(f" 元素数量: {len(obj)}\n") + elif isinstance(obj, str): + f.write(f" 字符串长度: {len(obj)}\n") + if len(obj) > 100: + f.write(f" 内容预览: {obj[:100]}...\n") + else: + f.write(f" 内容: {obj}\n") + elif hasattr(obj, '__dict__'): + f.write(f" 属性数量: {len(obj.__dict__)}\n") + if hasattr(obj, '__class__'): + f.write(f" 类名: {obj.__class__.__name__}\n") + except: + pass + + f.write("\n") + + f.write("=" * 100 + "\n\n") + + def _write_memory_leak_detection(self, f): + """ + 写入内存泄漏检测 + """ + f.write("5. 内存泄漏检测\n") + f.write("-" * 50 + "\n") + + # tracemalloc分析 + current, peak = tracemalloc.get_traced_memory() + f.write(f"tracemalloc当前内存: {current / 1024 / 1024:.2f} MB\n") + f.write(f"tracemalloc峰值内存: {peak / 1024 / 1024:.2f} MB\n") + + try: + snapshot = tracemalloc.take_snapshot() + top_stats = snapshot.statistics('lineno') + + f.write(f"\n内存分配最多的位置 (前15个):\n") + f.write("-" * 50 + "\n") + for i, stat in enumerate(top_stats[:15], 1): + f.write(f"{i:2d}. {stat.count:>8} 个对象, {stat.size / 1024 / 1024:>8.2f} MB\n") + for line in stat.traceback.format(): + f.write(f" {line}\n") + f.write("\n") + except Exception as e: + f.write(f"获取tracemalloc统计失败: {e}\n") + + # 垃圾回收分析 + f.write("垃圾回收分析:\n") + f.write("-" * 50 + "\n") + gc_counts = gc.get_count() + f.write(f"GC计数: {gc_counts}\n") + + # 检查不可达对象 + unreachable = len(gc.garbage) + f.write(f"不可达对象数量: {unreachable}\n") + if unreachable > 0: + f.write("不可达对象详情:\n") + for i, obj in enumerate(gc.garbage[:5], 1): # 只显示前5个 + f.write(f" {i}. {type(obj).__name__} - {id(obj)}\n") + + f.write("\n" + "=" * 100 + "\n\n") def _append_class_analysis(self, snapshot_file): """ @@ -455,3 +887,110 @@ class MemoryHelper(metaclass=Singleton): except Exception as e: logger.debug(f"获取变量名失败: {e}") return f"{type(obj).__name__}_{id(obj)}" + + def get_memory_summary(self) -> Dict[str, float]: + """ + 获取内存使用摘要 + """ + try: + process = psutil.Process() + memory_info = process.memory_info() + + # 获取Python对象总内存 + all_objects = muppy.get_objects() + sum1 = summary.summarize(all_objects) + + python_total_mb = 0 + for line in summary.format_(sum1): + if '|' in line and line.strip() and not line.startswith('=') and not line.startswith('-'): + parts = line.split('|') + if len(parts) >= 3: + try: + size_str = parts[2].strip() + if 'MB' in size_str: + size_mb = float(size_str.replace('MB', '').strip()) + python_total_mb += size_mb + except: + pass + + total_memory = memory_info.rss / 1024 / 1024 + unaccounted = total_memory - python_total_mb + + return { + 'total_memory_mb': total_memory, + 'python_objects_mb': python_total_mb, + 'unaccounted_mb': unaccounted, + 'unaccounted_percent': (unaccounted / total_memory * 100) if total_memory > 0 else 0 + } + except Exception as e: + logger.error(f"获取内存摘要失败: {e}") + return {} + + def force_garbage_collection(self): + """ + 强制垃圾回收并返回清理的对象数量 + """ + try: + collected = gc.collect() + logger.info(f"强制垃圾回收完成,清理了 {collected} 个对象") + return collected + except Exception as e: + logger.error(f"强制垃圾回收失败: {e}") + return 0 + + def analyze_memory_growth(self, interval_seconds: int = 300) -> Dict[str, float]: + """ + 分析内存增长趋势 + :param interval_seconds: 分析间隔(秒) + :return: 内存增长信息 + """ + try: + # 获取当前内存使用 + current_summary = self.get_memory_summary() + + # 等待指定时间 + time.sleep(interval_seconds) + + # 获取新的内存使用 + new_summary = self.get_memory_summary() + + if current_summary and new_summary: + growth_info = { + 'total_growth_mb': new_summary['total_memory_mb'] - current_summary['total_memory_mb'], + 'python_growth_mb': new_summary['python_objects_mb'] - current_summary['python_objects_mb'], + 'unaccounted_growth_mb': new_summary['unaccounted_mb'] - current_summary['unaccounted_mb'], + 'growth_rate_mb_per_hour': (new_summary['total_memory_mb'] - current_summary['total_memory_mb']) * 3600 / interval_seconds + } + + logger.info(f"内存增长分析: 总增长 {growth_info['total_growth_mb']:.2f} MB, " + f"Python对象增长 {growth_info['python_growth_mb']:.2f} MB, " + f"未统计增长 {growth_info['unaccounted_growth_mb']:.2f} MB") + + return growth_info + + return {} + + except Exception as e: + logger.error(f"分析内存增长失败: {e}") + return {} + + +# 使用示例 +if __name__ == "__main__": + # 创建内存分析器实例 + memory_helper = MemoryHelper() + + # 获取内存摘要 + summary = memory_helper.get_memory_summary() + print("内存使用摘要:") + for key, value in summary.items(): + print(f" {key}: {value:.2f}") + + # 创建详细分析报告 + analysis_file = memory_helper.create_detailed_memory_analysis() + if analysis_file: + print(f"详细分析报告已保存到: {analysis_file}") + + # 强制垃圾回收 + collected = memory_helper.force_garbage_collection() + print(f"垃圾回收清理了 {collected} 个对象") From 3efbd47ffd94119793fa13dfc8ee08a4381ec831 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 9 Jul 2025 08:04:10 +0000 Subject: [PATCH 2/3] Add comprehensive memory analysis tool with guide and test script Co-authored-by: jxxghp --- MEMORY_ANALYSIS_GUIDE.md | 269 +++++++++++++++++++++++++++++++++++++++ app/helper/memory.py | 3 - test_memory_analysis.py | 131 +++++++++++++++++++ 3 files changed, 400 insertions(+), 3 deletions(-) create mode 100644 MEMORY_ANALYSIS_GUIDE.md create mode 100644 test_memory_analysis.py diff --git a/MEMORY_ANALYSIS_GUIDE.md b/MEMORY_ANALYSIS_GUIDE.md new file mode 100644 index 00000000..43457db2 --- /dev/null +++ b/MEMORY_ANALYSIS_GUIDE.md @@ -0,0 +1,269 @@ +# 内存分析工具使用指南 + +## 概述 + +这个增强版的内存分析工具专门用于诊断Python应用程序的内存问题,特别是解决"总内存比各对象占比内存大很多"的问题。 + +## 主要功能 + +### 1. 系统级内存分析 +- 进程内存使用详情(RSS、VMS、共享内存等) +- 系统内存状态 +- 内存映射分析 + +### 2. Python对象深度分析 +- 对象类型统计(按内存大小排序) +- Python对象总内存计算 +- 未统计内存识别 + +### 3. 内存映射详细分析 +- 按权限分类的内存映射 +- 按文件分类的内存映射 +- 识别C扩展和系统库占用的内存 + +### 4. 大对象分析 +- 识别大于1MB的对象 +- 对象详细信息(类型、大小、内容预览) + +### 5. 内存泄漏检测 +- tracemalloc内存分配统计 +- 内存分配最多的位置 +- 垃圾回收统计 +- 不可达对象检测 + +## 使用方法 + +### 基本使用 + +```python +from app.helper.memory import MemoryHelper + +# 创建内存分析器实例 +memory_helper = MemoryHelper() + +# 获取内存摘要 +summary = memory_helper.get_memory_summary() +print(f"总内存: {summary['total_memory_mb']:.2f} MB") +print(f"Python对象: {summary['python_objects_mb']:.2f} MB") +print(f"未统计内存: {summary['unaccounted_mb']:.2f} MB") + +# 强制垃圾回收 +collected = memory_helper.force_garbage_collection() +print(f"清理了 {collected} 个对象") + +# 创建详细分析报告 +analysis_file = memory_helper.create_detailed_memory_analysis() +print(f"详细报告已保存到: {analysis_file}") +``` + +### 内存增长分析 + +```python +# 分析内存增长趋势(5分钟间隔) +growth_info = memory_helper.analyze_memory_growth(300) +print(f"内存增长率: {growth_info['growth_rate_mb_per_hour']:.2f} MB/小时") +``` + +### 启动自动监控 + +```python +# 启动内存监控(需要配置MEMORY_ANALYSIS=True) +memory_helper.start_monitoring() + +# 停止监控 +memory_helper.stop_monitoring() +``` + +## 配置选项 + +在配置文件中设置以下选项: + +```python +# 启用内存分析 +MEMORY_ANALYSIS = True + +# 内存快照间隔(分钟) +MEMORY_SNAPSHOT_INTERVAL = 5 + +# 保留的快照文件数量 +MEMORY_SNAPSHOT_KEEP_COUNT = 30 +``` + +## 输出文件 + +### 1. 内存快照文件 +- 位置: `logs/memory_snapshots/memory_snapshot_YYYYMMDD_HHMMSS.txt` +- 内容: 基本的内存使用统计 + +### 2. 详细分析报告 +- 位置: `logs/memory_snapshots/detailed_memory_analysis_YYYYMMDD_HHMMSS.txt` +- 内容: 完整的内存分析报告 + +## 解决内存问题的步骤 + +### 1. 识别未统计内存 +```python +summary = memory_helper.get_memory_summary() +if summary['unaccounted_percent'] > 50: + print("警告: 超过50%的内存未被Python对象统计") + print("可能的原因: C扩展、系统缓存、内存碎片") +``` + +### 2. 分析内存映射 +详细分析报告中的"内存映射详细分析"部分会显示: +- 哪些文件占用了大量内存 +- 内存权限分布 +- 识别C扩展库 + +### 3. 检测内存泄漏 +```python +# 定期检查内存增长 +growth_info = memory_helper.analyze_memory_growth(300) +if growth_info['growth_rate_mb_per_hour'] > 100: + print("警告: 内存增长过快,可能存在内存泄漏") +``` + +### 4. 分析大对象 +详细分析报告会列出所有大于1MB的对象,帮助识别: +- 意外的内存占用 +- 缓存未清理 +- 数据结构过大 + +## 常见问题解决 + +### 问题1: 总内存比Python对象内存大很多 + +**原因**: +- C扩展库占用内存 +- 系统缓存 +- 内存碎片 +- 共享库 + +**解决方法**: +1. 查看内存映射分析 +2. 检查是否有大量C扩展 +3. 分析系统级内存使用 + +### 问题2: 内存持续增长 + +**原因**: +- 内存泄漏 +- 缓存未清理 +- 循环引用 + +**解决方法**: +1. 使用tracemalloc分析内存分配 +2. 检查垃圾回收统计 +3. 分析大对象列表 + +### 问题3: 特定对象类型占用过多内存 + +**解决方法**: +1. 查看对象类型统计 +2. 分析大对象详情 +3. 检查对象引用关系 + +## 性能注意事项 + +1. **详细分析耗时**: `create_detailed_memory_analysis()` 可能需要几秒到几分钟 +2. **内存开销**: 分析过程本身会消耗一些内存 +3. **建议频率**: 不要过于频繁地运行详细分析,建议间隔5分钟以上 + +## 调试技巧 + +### 1. 在关键点添加内存检查 +```python +def critical_function(): + memory_helper = MemoryHelper() + before = memory_helper.get_memory_summary() + + # 执行关键操作 + do_something() + + after = memory_helper.get_memory_summary() + growth = after['total_memory_mb'] - before['total_memory_mb'] + if growth > 10: + print(f"警告: 函数执行后内存增长 {growth:.2f} MB") +``` + +### 2. 监控特定操作 +```python +def monitor_operation(operation_name): + memory_helper = MemoryHelper() + before = memory_helper.get_memory_summary() + + # 执行操作 + result = perform_operation() + + after = memory_helper.get_memory_summary() + growth = after['total_memory_mb'] - before['total_memory_mb'] + + logger.info(f"{operation_name}: 内存增长 {growth:.2f} MB") + return result +``` + +## 示例输出 + +### 内存摘要示例 +``` +total_memory_mb: 708.20 +python_objects_mb: 130.45 +unaccounted_mb: 577.75 +unaccounted_percent: 81.6 +``` + +### 详细分析报告结构 +``` +详细内存分析报告 - 2025-07-09 14:26:00 +==================================================================================================== + +1. 系统级内存分析 +-------------------------------------------------- +进程ID: 12345 +进程名称: python +内存使用详情: + RSS (物理内存): 708.20 MB + VMS (虚拟内存): 1024.50 MB + 共享内存: 45.30 MB + ... + +2. Python对象深度分析 +-------------------------------------------------- +总对象数: 1,234,567 +对象类型统计 (按内存大小排序): +类型 数量 总大小(MB) 平均大小(B) +str 318,537 34.56 113.5 +dict 101,049 32.23 319.2 +... + +3. 内存映射详细分析 +-------------------------------------------------- +按权限分类的内存映射: +权限 数量 大小(MB) +r-xp 45 156.78 +rw-p 23 89.45 +... + +4. 大对象详细分析 +-------------------------------------------------- +大对象 (>1MB) 数量: 15 + + 1. dict - 45.67 MB + 字典项数: 125000 + 示例键: ['user_data', 'cache', 'config'] + + 2. list - 23.45 MB + 元素数量: 500000 + +5. 内存泄漏检测 +-------------------------------------------------- +tracemalloc当前内存: 125.67 MB +tracemalloc峰值内存: 145.23 MB + +内存分配最多的位置 (前15个): + 1. 1250 个对象, 45.67 MB + File "/app/core/cache.py", line 123 + cache_data = load_large_dataset() +``` + +这个工具将帮助你全面了解应用程序的内存使用情况,特别是找出那些"消失"的内存去向。 \ No newline at end of file diff --git a/app/helper/memory.py b/app/helper/memory.py index 7cb0d76c..bdf01596 100644 --- a/app/helper/memory.py +++ b/app/helper/memory.py @@ -289,9 +289,6 @@ class MemoryHelper(metaclass=Singleton): # 获取内存分配统计 try: - stats = tracemalloc.get_traced_memory() - f.write(f"内存分配统计: {stats}\n") - # 获取前10个内存分配最多的位置 snapshot = tracemalloc.take_snapshot() top_stats = snapshot.statistics('lineno') diff --git a/test_memory_analysis.py b/test_memory_analysis.py new file mode 100644 index 00000000..77322c3d --- /dev/null +++ b/test_memory_analysis.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +""" +内存分析工具测试脚本 +用于验证内存分析工具的功能和修复后的效果 +""" + +import sys +import os +import time +import gc + +# 添加项目路径 +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'app')) + +from app.helper.memory import MemoryHelper +from app.log import logger + +def test_memory_analysis(): + """测试内存分析功能""" + print("开始测试内存分析工具...") + + # 创建内存分析器实例 + memory_helper = MemoryHelper() + + # 1. 测试内存摘要 + print("\n1. 测试内存摘要:") + summary = memory_helper.get_memory_summary() + for key, value in summary.items(): + print(f" {key}: {value:.2f}") + + # 2. 测试强制垃圾回收 + print("\n2. 测试强制垃圾回收:") + collected = memory_helper.force_garbage_collection() + print(f" 清理了 {collected} 个对象") + + # 3. 创建一些测试数据来模拟内存使用 + print("\n3. 创建测试数据:") + test_data = { + 'large_list': [i for i in range(100000)], # 约2.4MB + 'large_dict': {f'key_{i}': f'value_{i}' for i in range(50000)}, # 约3MB + 'large_string': 'x' * 1000000, # 约1MB + } + print(f" 创建了测试数据,包含 {len(test_data)} 个大对象") + + # 4. 再次获取内存摘要 + print("\n4. 创建测试数据后的内存摘要:") + summary_after = memory_helper.get_memory_summary() + for key, value in summary_after.items(): + print(f" {key}: {value:.2f}") + + # 5. 计算内存增长 + print("\n5. 内存增长分析:") + if summary and summary_after: + total_growth = summary_after['total_memory_mb'] - summary['total_memory_mb'] + python_growth = summary_after['python_objects_mb'] - summary['python_objects_mb'] + unaccounted_growth = summary_after['unaccounted_mb'] - summary['unaccounted_mb'] + + print(f" 总内存增长: {total_growth:.2f} MB") + print(f" Python对象增长: {python_growth:.2f} MB") + print(f" 未统计内存增长: {unaccounted_growth:.2f} MB") + + # 6. 创建详细分析报告 + print("\n6. 创建详细分析报告:") + analysis_file = memory_helper.create_detailed_memory_analysis() + if analysis_file: + print(f" 详细分析报告已保存到: {analysis_file}") + + # 显示报告的前几行 + print("\n 报告预览:") + try: + with open(analysis_file, 'r', encoding='utf-8') as f: + lines = f.readlines()[:20] + for line in lines: + print(f" {line.rstrip()}") + print(" ...") + except Exception as e: + print(f" 读取报告失败: {e}") + + # 7. 清理测试数据 + print("\n7. 清理测试数据:") + del test_data + collected = memory_helper.force_garbage_collection() + print(f" 清理了 {collected} 个对象") + + # 8. 最终内存摘要 + print("\n8. 清理后的内存摘要:") + final_summary = memory_helper.get_memory_summary() + for key, value in final_summary.items(): + print(f" {key}: {value:.2f}") + + print("\n内存分析工具测试完成!") + +def test_memory_growth_detection(): + """测试内存增长检测功能""" + print("\n开始测试内存增长检测...") + + memory_helper = MemoryHelper() + + # 获取初始内存 + initial_summary = memory_helper.get_memory_summary() + print(f"初始内存: {initial_summary.get('total_memory_mb', 0):.2f} MB") + + # 创建一些数据 + data_list = [] + for i in range(10): + data_list.append([j for j in range(10000)]) # 每次约240KB + time.sleep(0.1) # 短暂延迟 + + # 获取最终内存 + final_summary = memory_helper.get_memory_summary() + print(f"最终内存: {final_summary.get('total_memory_mb', 0):.2f} MB") + + # 计算增长 + if initial_summary and final_summary: + growth = final_summary['total_memory_mb'] - initial_summary['total_memory_mb'] + print(f"内存增长: {growth:.2f} MB") + + # 清理 + del data_list + memory_helper.force_garbage_collection() + + print("内存增长检测测试完成!") + +if __name__ == "__main__": + try: + test_memory_analysis() + test_memory_growth_detection() + except Exception as e: + print(f"测试过程中出现错误: {e}") + import traceback + traceback.print_exc() \ No newline at end of file From 600767d2bf0079f7b261ef01639f3ce128e1cdd8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 9 Jul 2025 08:07:30 +0000 Subject: [PATCH 3/3] Remove memory analysis guide and test script Co-authored-by: jxxghp --- MEMORY_ANALYSIS_GUIDE.md | 269 --------------------------------------- test_memory_analysis.py | 131 ------------------- 2 files changed, 400 deletions(-) delete mode 100644 MEMORY_ANALYSIS_GUIDE.md delete mode 100644 test_memory_analysis.py diff --git a/MEMORY_ANALYSIS_GUIDE.md b/MEMORY_ANALYSIS_GUIDE.md deleted file mode 100644 index 43457db2..00000000 --- a/MEMORY_ANALYSIS_GUIDE.md +++ /dev/null @@ -1,269 +0,0 @@ -# 内存分析工具使用指南 - -## 概述 - -这个增强版的内存分析工具专门用于诊断Python应用程序的内存问题,特别是解决"总内存比各对象占比内存大很多"的问题。 - -## 主要功能 - -### 1. 系统级内存分析 -- 进程内存使用详情(RSS、VMS、共享内存等) -- 系统内存状态 -- 内存映射分析 - -### 2. Python对象深度分析 -- 对象类型统计(按内存大小排序) -- Python对象总内存计算 -- 未统计内存识别 - -### 3. 内存映射详细分析 -- 按权限分类的内存映射 -- 按文件分类的内存映射 -- 识别C扩展和系统库占用的内存 - -### 4. 大对象分析 -- 识别大于1MB的对象 -- 对象详细信息(类型、大小、内容预览) - -### 5. 内存泄漏检测 -- tracemalloc内存分配统计 -- 内存分配最多的位置 -- 垃圾回收统计 -- 不可达对象检测 - -## 使用方法 - -### 基本使用 - -```python -from app.helper.memory import MemoryHelper - -# 创建内存分析器实例 -memory_helper = MemoryHelper() - -# 获取内存摘要 -summary = memory_helper.get_memory_summary() -print(f"总内存: {summary['total_memory_mb']:.2f} MB") -print(f"Python对象: {summary['python_objects_mb']:.2f} MB") -print(f"未统计内存: {summary['unaccounted_mb']:.2f} MB") - -# 强制垃圾回收 -collected = memory_helper.force_garbage_collection() -print(f"清理了 {collected} 个对象") - -# 创建详细分析报告 -analysis_file = memory_helper.create_detailed_memory_analysis() -print(f"详细报告已保存到: {analysis_file}") -``` - -### 内存增长分析 - -```python -# 分析内存增长趋势(5分钟间隔) -growth_info = memory_helper.analyze_memory_growth(300) -print(f"内存增长率: {growth_info['growth_rate_mb_per_hour']:.2f} MB/小时") -``` - -### 启动自动监控 - -```python -# 启动内存监控(需要配置MEMORY_ANALYSIS=True) -memory_helper.start_monitoring() - -# 停止监控 -memory_helper.stop_monitoring() -``` - -## 配置选项 - -在配置文件中设置以下选项: - -```python -# 启用内存分析 -MEMORY_ANALYSIS = True - -# 内存快照间隔(分钟) -MEMORY_SNAPSHOT_INTERVAL = 5 - -# 保留的快照文件数量 -MEMORY_SNAPSHOT_KEEP_COUNT = 30 -``` - -## 输出文件 - -### 1. 内存快照文件 -- 位置: `logs/memory_snapshots/memory_snapshot_YYYYMMDD_HHMMSS.txt` -- 内容: 基本的内存使用统计 - -### 2. 详细分析报告 -- 位置: `logs/memory_snapshots/detailed_memory_analysis_YYYYMMDD_HHMMSS.txt` -- 内容: 完整的内存分析报告 - -## 解决内存问题的步骤 - -### 1. 识别未统计内存 -```python -summary = memory_helper.get_memory_summary() -if summary['unaccounted_percent'] > 50: - print("警告: 超过50%的内存未被Python对象统计") - print("可能的原因: C扩展、系统缓存、内存碎片") -``` - -### 2. 分析内存映射 -详细分析报告中的"内存映射详细分析"部分会显示: -- 哪些文件占用了大量内存 -- 内存权限分布 -- 识别C扩展库 - -### 3. 检测内存泄漏 -```python -# 定期检查内存增长 -growth_info = memory_helper.analyze_memory_growth(300) -if growth_info['growth_rate_mb_per_hour'] > 100: - print("警告: 内存增长过快,可能存在内存泄漏") -``` - -### 4. 分析大对象 -详细分析报告会列出所有大于1MB的对象,帮助识别: -- 意外的内存占用 -- 缓存未清理 -- 数据结构过大 - -## 常见问题解决 - -### 问题1: 总内存比Python对象内存大很多 - -**原因**: -- C扩展库占用内存 -- 系统缓存 -- 内存碎片 -- 共享库 - -**解决方法**: -1. 查看内存映射分析 -2. 检查是否有大量C扩展 -3. 分析系统级内存使用 - -### 问题2: 内存持续增长 - -**原因**: -- 内存泄漏 -- 缓存未清理 -- 循环引用 - -**解决方法**: -1. 使用tracemalloc分析内存分配 -2. 检查垃圾回收统计 -3. 分析大对象列表 - -### 问题3: 特定对象类型占用过多内存 - -**解决方法**: -1. 查看对象类型统计 -2. 分析大对象详情 -3. 检查对象引用关系 - -## 性能注意事项 - -1. **详细分析耗时**: `create_detailed_memory_analysis()` 可能需要几秒到几分钟 -2. **内存开销**: 分析过程本身会消耗一些内存 -3. **建议频率**: 不要过于频繁地运行详细分析,建议间隔5分钟以上 - -## 调试技巧 - -### 1. 在关键点添加内存检查 -```python -def critical_function(): - memory_helper = MemoryHelper() - before = memory_helper.get_memory_summary() - - # 执行关键操作 - do_something() - - after = memory_helper.get_memory_summary() - growth = after['total_memory_mb'] - before['total_memory_mb'] - if growth > 10: - print(f"警告: 函数执行后内存增长 {growth:.2f} MB") -``` - -### 2. 监控特定操作 -```python -def monitor_operation(operation_name): - memory_helper = MemoryHelper() - before = memory_helper.get_memory_summary() - - # 执行操作 - result = perform_operation() - - after = memory_helper.get_memory_summary() - growth = after['total_memory_mb'] - before['total_memory_mb'] - - logger.info(f"{operation_name}: 内存增长 {growth:.2f} MB") - return result -``` - -## 示例输出 - -### 内存摘要示例 -``` -total_memory_mb: 708.20 -python_objects_mb: 130.45 -unaccounted_mb: 577.75 -unaccounted_percent: 81.6 -``` - -### 详细分析报告结构 -``` -详细内存分析报告 - 2025-07-09 14:26:00 -==================================================================================================== - -1. 系统级内存分析 --------------------------------------------------- -进程ID: 12345 -进程名称: python -内存使用详情: - RSS (物理内存): 708.20 MB - VMS (虚拟内存): 1024.50 MB - 共享内存: 45.30 MB - ... - -2. Python对象深度分析 --------------------------------------------------- -总对象数: 1,234,567 -对象类型统计 (按内存大小排序): -类型 数量 总大小(MB) 平均大小(B) -str 318,537 34.56 113.5 -dict 101,049 32.23 319.2 -... - -3. 内存映射详细分析 --------------------------------------------------- -按权限分类的内存映射: -权限 数量 大小(MB) -r-xp 45 156.78 -rw-p 23 89.45 -... - -4. 大对象详细分析 --------------------------------------------------- -大对象 (>1MB) 数量: 15 - - 1. dict - 45.67 MB - 字典项数: 125000 - 示例键: ['user_data', 'cache', 'config'] - - 2. list - 23.45 MB - 元素数量: 500000 - -5. 内存泄漏检测 --------------------------------------------------- -tracemalloc当前内存: 125.67 MB -tracemalloc峰值内存: 145.23 MB - -内存分配最多的位置 (前15个): - 1. 1250 个对象, 45.67 MB - File "/app/core/cache.py", line 123 - cache_data = load_large_dataset() -``` - -这个工具将帮助你全面了解应用程序的内存使用情况,特别是找出那些"消失"的内存去向。 \ No newline at end of file diff --git a/test_memory_analysis.py b/test_memory_analysis.py deleted file mode 100644 index 77322c3d..00000000 --- a/test_memory_analysis.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env python3 -""" -内存分析工具测试脚本 -用于验证内存分析工具的功能和修复后的效果 -""" - -import sys -import os -import time -import gc - -# 添加项目路径 -sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'app')) - -from app.helper.memory import MemoryHelper -from app.log import logger - -def test_memory_analysis(): - """测试内存分析功能""" - print("开始测试内存分析工具...") - - # 创建内存分析器实例 - memory_helper = MemoryHelper() - - # 1. 测试内存摘要 - print("\n1. 测试内存摘要:") - summary = memory_helper.get_memory_summary() - for key, value in summary.items(): - print(f" {key}: {value:.2f}") - - # 2. 测试强制垃圾回收 - print("\n2. 测试强制垃圾回收:") - collected = memory_helper.force_garbage_collection() - print(f" 清理了 {collected} 个对象") - - # 3. 创建一些测试数据来模拟内存使用 - print("\n3. 创建测试数据:") - test_data = { - 'large_list': [i for i in range(100000)], # 约2.4MB - 'large_dict': {f'key_{i}': f'value_{i}' for i in range(50000)}, # 约3MB - 'large_string': 'x' * 1000000, # 约1MB - } - print(f" 创建了测试数据,包含 {len(test_data)} 个大对象") - - # 4. 再次获取内存摘要 - print("\n4. 创建测试数据后的内存摘要:") - summary_after = memory_helper.get_memory_summary() - for key, value in summary_after.items(): - print(f" {key}: {value:.2f}") - - # 5. 计算内存增长 - print("\n5. 内存增长分析:") - if summary and summary_after: - total_growth = summary_after['total_memory_mb'] - summary['total_memory_mb'] - python_growth = summary_after['python_objects_mb'] - summary['python_objects_mb'] - unaccounted_growth = summary_after['unaccounted_mb'] - summary['unaccounted_mb'] - - print(f" 总内存增长: {total_growth:.2f} MB") - print(f" Python对象增长: {python_growth:.2f} MB") - print(f" 未统计内存增长: {unaccounted_growth:.2f} MB") - - # 6. 创建详细分析报告 - print("\n6. 创建详细分析报告:") - analysis_file = memory_helper.create_detailed_memory_analysis() - if analysis_file: - print(f" 详细分析报告已保存到: {analysis_file}") - - # 显示报告的前几行 - print("\n 报告预览:") - try: - with open(analysis_file, 'r', encoding='utf-8') as f: - lines = f.readlines()[:20] - for line in lines: - print(f" {line.rstrip()}") - print(" ...") - except Exception as e: - print(f" 读取报告失败: {e}") - - # 7. 清理测试数据 - print("\n7. 清理测试数据:") - del test_data - collected = memory_helper.force_garbage_collection() - print(f" 清理了 {collected} 个对象") - - # 8. 最终内存摘要 - print("\n8. 清理后的内存摘要:") - final_summary = memory_helper.get_memory_summary() - for key, value in final_summary.items(): - print(f" {key}: {value:.2f}") - - print("\n内存分析工具测试完成!") - -def test_memory_growth_detection(): - """测试内存增长检测功能""" - print("\n开始测试内存增长检测...") - - memory_helper = MemoryHelper() - - # 获取初始内存 - initial_summary = memory_helper.get_memory_summary() - print(f"初始内存: {initial_summary.get('total_memory_mb', 0):.2f} MB") - - # 创建一些数据 - data_list = [] - for i in range(10): - data_list.append([j for j in range(10000)]) # 每次约240KB - time.sleep(0.1) # 短暂延迟 - - # 获取最终内存 - final_summary = memory_helper.get_memory_summary() - print(f"最终内存: {final_summary.get('total_memory_mb', 0):.2f} MB") - - # 计算增长 - if initial_summary and final_summary: - growth = final_summary['total_memory_mb'] - initial_summary['total_memory_mb'] - print(f"内存增长: {growth:.2f} MB") - - # 清理 - del data_list - memory_helper.force_garbage_collection() - - print("内存增长检测测试完成!") - -if __name__ == "__main__": - try: - test_memory_analysis() - test_memory_growth_detection() - except Exception as e: - print(f"测试过程中出现错误: {e}") - import traceback - traceback.print_exc() \ No newline at end of file