From 81bc51c97256317d39ebd6f764311d1f391dba61 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 8 Jun 2025 19:02:25 +0800 Subject: [PATCH] fix pympler --- app/helper/memory.py | 146 ++++++++++++++++++++++++++++++++----------- requirements.in | 2 +- 2 files changed, 112 insertions(+), 36 deletions(-) diff --git a/app/helper/memory.py b/app/helper/memory.py index 5c960c5f..19481128 100644 --- a/app/helper/memory.py +++ b/app/helper/memory.py @@ -127,9 +127,16 @@ class MemoryHelper(metaclass=Singleton): f.write("最大内存占用对象详情:\n") f.write("-" * 80 + "\n") - largest_objects = self._get_largest_objects() - for i, obj_info in enumerate(largest_objects[:10], 1): - f.write(f"{i:2d}. {obj_info['type']:<30} {obj_info['size_mb']:>8.2f} MB - {obj_info['description']}\n") + try: + largest_objects = self._get_largest_objects() + if largest_objects: + for i, obj_info in enumerate(largest_objects[:10], 1): + f.write(f"{i:2d}. {obj_info['type']:<30} {obj_info['size_mb']:>8.2f} MB - {obj_info['description']}\n") + else: + f.write("未找到大于1KB的对象或分析失败\n") + except Exception as e: + f.write(f"大对象分析失败: {e}\n") + logger.warning(f"大对象分析失败,但快照生成继续: {e}") logger.info(f"内存快照已保存: {snapshot_file}, 当前内存使用: {memory_usage / 1024 / 1024:.2f} MB") @@ -163,28 +170,51 @@ class MemoryHelper(metaclass=Singleton): try: # 获取所有对象 all_objects = muppy.get_objects() + logger.debug(f"开始分析 {len(all_objects)} 个对象") # 计算每个对象的大小并收集信息 object_sizes = [] - for obj in all_objects: + failed_count = 0 + skip_modules = {'tkinter', 'matplotlib', 'PIL', 'cv2'} # 可能导致GUI库问题的模块 + + for i, obj in enumerate(all_objects): try: + # 检查对象类型,跳过可能有问题的模块 + obj_module = getattr(type(obj), '__module__', '') + if any(skip_mod in obj_module for skip_mod in skip_modules): + continue + # 使用asizeof计算对象真实大小 size = asizeof.asizeof(obj) if size > 1024: # 只关注大于1KB的对象 obj_type = type(obj).__name__ - obj_module = getattr(type(obj), '__module__', 'unknown') # 生成对象描述 description = self._generate_object_description(obj) object_sizes.append({ 'size': size, - 'type': f"{obj_module}.{obj_type}" if obj_module != 'builtins' else obj_type, + 'type': f"{obj_module}.{obj_type}" if obj_module and obj_module != 'builtins' else obj_type, 'description': description }) - except (TypeError, AttributeError, RuntimeError): - # 某些对象可能无法计算大小,跳过 + + except (TypeError, AttributeError, RuntimeError, OSError, ImportError) as e: + # 扩展异常处理,包括共享库错误 + failed_count += 1 + if failed_count <= 5: # 只记录前几个错误,避免日志泛滥 + logger.debug(f"跳过对象分析 (第{i+1}个): {type(e).__name__}: {str(e)[:100]}") continue + except Exception as e: + # 处理其他未预期的异常 + failed_count += 1 + if failed_count <= 5: + logger.debug(f"跳过对象分析 (第{i+1}个): 未知错误: {str(e)[:100]}") + continue + + if failed_count > 5: + logger.debug(f"总共跳过了 {failed_count} 个无法分析的对象") + + logger.debug(f"成功分析了 {len(object_sizes)} 个大对象") # 按大小排序并取前N个 object_sizes.sort(key=lambda x: x['size'], reverse=True) @@ -212,46 +242,88 @@ class MemoryHelper(metaclass=Singleton): :return: 对象描述字符串 """ try: + # 获取对象类型名称,避免访问可能有问题的属性 + obj_type_name = type(obj).__name__ + # 根据对象类型生成不同的描述 if isinstance(obj, (list, tuple)): - length = len(obj) - first_type = type(obj[0]).__name__ if obj else 'empty' - return f"长度={length}, 示例元素类型={first_type}" + try: + length = len(obj) + first_type = type(obj[0]).__name__ if obj else 'empty' + return f"长度={length}, 示例元素类型={first_type}" + except (IndexError, TypeError): + return f"{obj_type_name}(长度未知)" elif isinstance(obj, dict): - length = len(obj) - if obj: - first_key = next(iter(obj)) - key_type = type(first_key).__name__ - return f"键值对数={length}, 示例键类型={key_type}" - return f"键值对数={length}, 示例键类型=empty" + try: + length = len(obj) + if obj: + first_key = next(iter(obj)) + key_type = type(first_key).__name__ + return f"键值对数={length}, 示例键类型={key_type}" + return f"键值对数={length}, 示例键类型=empty" + except (StopIteration, TypeError): + return f"{obj_type_name}(大小未知)" elif isinstance(obj, set): - length = len(obj) - if obj: - first_item = next(iter(obj)) - item_type = type(first_item).__name__ - return f"元素数={length}, 示例元素类型={item_type}" - return f"元素数={length}, 示例元素类型=empty" + try: + length = len(obj) + if obj: + first_item = next(iter(obj)) + item_type = type(first_item).__name__ + return f"元素数={length}, 示例元素类型={item_type}" + return f"元素数={length}, 示例元素类型=empty" + except (StopIteration, TypeError): + return f"{obj_type_name}(大小未知)" elif isinstance(obj, str): - length = len(obj) - preview = obj[:50] + '...' if length > 50 else obj - return f"长度={length}, 内容='{preview}'" + try: + length = len(obj) + # 限制预览长度,避免显示问题字符 + safe_preview = ''.join(c for c in obj[:30] if c.isprintable()) + preview = safe_preview + '...' if length > 30 else safe_preview + return f"长度={length}, 内容='{preview}'" + except (UnicodeError, TypeError): + return f"{obj_type_name}(字符串,长度未知)" - elif hasattr(obj, '__name__'): - return f"名称={obj.__name__}" + elif isinstance(obj, bytes): + try: + length = len(obj) + return f"字节数据,长度={length}" + except TypeError: + return f"{obj_type_name}(字节数据,长度未知)" - elif hasattr(obj, '__dict__'): - attrs_count = len(obj.__dict__) - return f"实例属性数={attrs_count}" + # 谨慎处理可能有问题的属性访问 + try: + if hasattr(obj, '__name__') and callable(getattr(obj, '__name__', None)): + name = str(obj.__name__)[:50] + return f"名称={name}" + except (AttributeError, TypeError, OSError): + pass - else: + try: + if hasattr(obj, '__dict__'): + attrs_count = len(obj.__dict__) + return f"实例属性数={attrs_count}" + except (AttributeError, TypeError, OSError): + pass + + # 最后的安全转换 + try: obj_str = str(obj)[:50] - return f"对象={obj_str}" + # 确保字符串是可打印的 + safe_str = ''.join(c for c in obj_str if c.isprintable()) + return f"对象={safe_str}" if safe_str else f"{obj_type_name}对象" + except (UnicodeError, TypeError, OSError): + return f"{obj_type_name}对象" except Exception as e: - return f"无法获取描述:{e}" + # 最后的异常处理,确保总是返回有用信息 + try: + obj_type_name = type(obj).__name__ + return f"{obj_type_name}(描述生成失败):{e}" + except Exception as e: + return f"未知对象(描述生成失败):{e}" def get_current_memory_info(self) -> dict: """ @@ -305,7 +377,11 @@ class MemoryHelper(metaclass=Singleton): break # 获取最大对象信息 - memory_info["largest_objects"] = self._get_largest_objects(10) + try: + memory_info["largest_objects"] = self._get_largest_objects(10) + except Exception as e: + logger.warning(f"获取大对象列表失败: {e}") + memory_info["largest_objects"] = [] return memory_info except Exception as e: diff --git a/requirements.in b/requirements.in index 867ecf94..e23fe0b4 100644 --- a/requirements.in +++ b/requirements.in @@ -70,4 +70,4 @@ cf_clearance~=0.31.0 oss2~=2.19.1 tqdm~=4.67.1 setuptools~=78.1.0 -pympler~=0.9 +pympler~=1.1