mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-04-13 17:52:28 +08:00
Fix memory analysis performance and timeout issues across platforms
Co-authored-by: jxxghp <jxxghp@163.com>
This commit is contained in:
@@ -1,112 +0,0 @@
|
||||
# 内存分析功能修复说明
|
||||
|
||||
## 问题描述
|
||||
|
||||
原始的内存分析功能存在严重的性能问题,导致:
|
||||
1. CPU占用100%,系统卡死
|
||||
2. 超时机制在守护线程中失效
|
||||
3. 跨平台兼容性问题(Windows不支持signal.SIGALRM)
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 超时机制重构
|
||||
|
||||
**问题**:
|
||||
- 使用 `signal.SIGALRM` 只在主线程中有效
|
||||
- 在守护线程中无法正常工作
|
||||
- Windows系统不支持 `signal.SIGALRM`
|
||||
|
||||
**解决方案**:
|
||||
- 使用 `concurrent.futures.ThreadPoolExecutor` 替代信号机制
|
||||
- 实现真正的跨平台超时控制
|
||||
- 在守护线程中也能正常工作
|
||||
|
||||
```python
|
||||
# 修复前(有问题)
|
||||
def _run_with_timeout(self, func, *args, **kwargs):
|
||||
signal.signal(signal.SIGALRM, self._timeout_handler)
|
||||
signal.alarm(self._analysis_timeout)
|
||||
# 只在主线程中有效
|
||||
|
||||
# 修复后(正确)
|
||||
def _run_with_timeout(self, func, *args, **kwargs):
|
||||
future = self._executor.submit(func, *args, **kwargs)
|
||||
result = future.result(timeout=self._analysis_timeout)
|
||||
# 跨平台,所有线程都有效
|
||||
```
|
||||
|
||||
### 2. 性能优化
|
||||
|
||||
**限制分析对象数量**:
|
||||
- 设置 `_max_objects_to_analyze = 50000`
|
||||
- 超过限制时使用随机采样
|
||||
- 避免分析数百万个对象
|
||||
|
||||
**优化大对象分析**:
|
||||
- 提高大对象阈值到1MB
|
||||
- 限制分析数量到30个
|
||||
- 使用简化的信息获取方法
|
||||
|
||||
**简化内存映射分析**:
|
||||
- 移除复杂的文件路径分析
|
||||
- 只保留基本权限分类
|
||||
- 减少数据处理量
|
||||
|
||||
### 3. 资源管理
|
||||
|
||||
**线程池管理**:
|
||||
- 添加 `__del__` 方法确保线程池正确关闭
|
||||
- 使用单线程池避免资源竞争
|
||||
- 设置线程名称便于调试
|
||||
|
||||
**文件操作优化**:
|
||||
- 减少文件读写次数
|
||||
- 使用更高效的文件更新方式
|
||||
- 添加异常处理和错误恢复
|
||||
|
||||
## 配置建议
|
||||
|
||||
为了进一步控制内存分析的影响,建议在配置文件中设置:
|
||||
|
||||
```env
|
||||
# 默认关闭内存分析,需要时手动开启
|
||||
MEMORY_ANALYSIS=false
|
||||
|
||||
# 增加快照间隔到10分钟
|
||||
MEMORY_SNAPSHOT_INTERVAL=10
|
||||
|
||||
# 减少保留的快照数量
|
||||
MEMORY_SNAPSHOT_KEEP_COUNT=5
|
||||
```
|
||||
|
||||
## 测试验证
|
||||
|
||||
创建了测试脚本 `test_timeout_fix.py` 来验证修复效果:
|
||||
|
||||
1. **守护线程超时测试**:验证超时机制在守护线程中的工作情况
|
||||
2. **性能测试**:监控CPU使用率,确保不会导致系统卡死
|
||||
3. **并发测试**:测试多个线程同时进行内存分析
|
||||
|
||||
## 预期效果
|
||||
|
||||
修复后的内存分析功能应该:
|
||||
- ✅ 不会导致CPU占用100%
|
||||
- ✅ 不会造成系统卡死
|
||||
- ✅ 在守护线程中正常工作
|
||||
- ✅ 跨平台兼容(Windows/Linux/macOS)
|
||||
- ✅ 有合理的超时保护机制
|
||||
- ✅ 保持核心分析功能完整
|
||||
|
||||
## 使用建议
|
||||
|
||||
1. **生产环境**:建议默认关闭内存分析功能
|
||||
2. **调试环境**:可以开启进行内存问题诊断
|
||||
3. **性能监控**:定期检查内存使用情况,避免内存泄漏
|
||||
4. **日志监控**:关注内存分析相关的日志信息
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 内存分析仍然会消耗一定的CPU和内存资源
|
||||
2. 建议在系统负载较低时进行详细分析
|
||||
3. 如果仍然遇到性能问题,可以进一步调整超时时间和对象数量限制
|
||||
4. 定期清理内存快照文件,避免占用过多磁盘空间
|
||||
@@ -1,148 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
测试修复后的超时机制
|
||||
验证ThreadPoolExecutor超时机制在守护线程中的工作情况
|
||||
"""
|
||||
|
||||
import time
|
||||
import threading
|
||||
import psutil
|
||||
from app.helper.memory import MemoryHelper
|
||||
|
||||
|
||||
def test_timeout_in_daemon_thread():
|
||||
"""测试在守护线程中的超时机制"""
|
||||
print("=== 测试守护线程中的超时机制 ===")
|
||||
|
||||
def long_running_task():
|
||||
"""模拟长时间运行的任务"""
|
||||
print("开始长时间运行的任务...")
|
||||
time.sleep(60) # 模拟60秒的长时间任务
|
||||
return "任务完成"
|
||||
|
||||
def worker():
|
||||
"""守护线程工作函数"""
|
||||
print(f"守护线程 {threading.current_thread().name} 开始工作")
|
||||
|
||||
memory_helper = MemoryHelper()
|
||||
|
||||
# 测试超时机制
|
||||
print("测试超时机制...")
|
||||
start_time = time.time()
|
||||
result = memory_helper._run_with_timeout(long_running_task)
|
||||
end_time = time.time()
|
||||
|
||||
print(f"任务执行时间: {end_time - start_time:.2f}秒")
|
||||
print(f"任务结果: {result}")
|
||||
|
||||
# 测试内存分析功能
|
||||
print("测试内存分析功能...")
|
||||
summary = memory_helper.get_memory_summary()
|
||||
print(f"内存摘要: {summary}")
|
||||
|
||||
# 创建守护线程
|
||||
daemon_thread = threading.Thread(target=worker, daemon=True, name="TestDaemonThread")
|
||||
daemon_thread.start()
|
||||
|
||||
# 等待线程完成或超时
|
||||
daemon_thread.join(timeout=40) # 给40秒时间
|
||||
|
||||
if daemon_thread.is_alive():
|
||||
print("守护线程仍在运行,但主线程继续执行")
|
||||
else:
|
||||
print("守护线程已完成")
|
||||
|
||||
|
||||
def test_memory_analysis_performance():
|
||||
"""测试内存分析性能"""
|
||||
print("\n=== 测试内存分析性能 ===")
|
||||
|
||||
memory_helper = MemoryHelper()
|
||||
|
||||
# 监控CPU使用率
|
||||
def monitor_cpu():
|
||||
cpu_samples = []
|
||||
for i in range(10):
|
||||
cpu_percent = psutil.cpu_percent(interval=1)
|
||||
cpu_samples.append(cpu_percent)
|
||||
print(f"CPU使用率: {cpu_percent:.1f}%")
|
||||
return sum(cpu_samples) / len(cpu_samples)
|
||||
|
||||
print("测试前CPU使用率:")
|
||||
avg_cpu_before = monitor_cpu()
|
||||
|
||||
# 执行内存分析
|
||||
print("\n执行内存分析...")
|
||||
start_time = time.time()
|
||||
|
||||
# 测试详细内存分析(应该会超时)
|
||||
analysis_file = memory_helper.create_detailed_memory_analysis()
|
||||
|
||||
end_time = time.time()
|
||||
print(f"内存分析耗时: {end_time - start_time:.2f}秒")
|
||||
|
||||
if analysis_file:
|
||||
print(f"分析报告已保存: {analysis_file}")
|
||||
else:
|
||||
print("内存分析超时或失败")
|
||||
|
||||
print("\n测试后CPU使用率:")
|
||||
avg_cpu_after = monitor_cpu()
|
||||
|
||||
print(f"\n性能对比:")
|
||||
print(f"测试前平均CPU: {avg_cpu_before:.1f}%")
|
||||
print(f"测试后平均CPU: {avg_cpu_after:.1f}%")
|
||||
|
||||
|
||||
def test_concurrent_analysis():
|
||||
"""测试并发内存分析"""
|
||||
print("\n=== 测试并发内存分析 ===")
|
||||
|
||||
def analysis_worker(worker_id):
|
||||
"""分析工作线程"""
|
||||
print(f"工作线程 {worker_id} 开始")
|
||||
memory_helper = MemoryHelper()
|
||||
|
||||
# 执行内存摘要
|
||||
summary = memory_helper.get_memory_summary()
|
||||
print(f"工作线程 {worker_id} 内存摘要: {summary}")
|
||||
|
||||
# 执行垃圾回收
|
||||
collected = memory_helper.force_garbage_collection()
|
||||
print(f"工作线程 {worker_id} 垃圾回收: {collected} 个对象")
|
||||
|
||||
print(f"工作线程 {worker_id} 完成")
|
||||
|
||||
# 创建多个工作线程
|
||||
threads = []
|
||||
for i in range(3):
|
||||
thread = threading.Thread(target=analysis_worker, args=(i,), daemon=True)
|
||||
threads.append(thread)
|
||||
thread.start()
|
||||
|
||||
# 等待所有线程完成
|
||||
for thread in threads:
|
||||
thread.join(timeout=30)
|
||||
|
||||
print("所有工作线程已完成")
|
||||
|
||||
|
||||
def main():
|
||||
"""主测试函数"""
|
||||
print("超时机制修复测试")
|
||||
print("=" * 50)
|
||||
|
||||
# 测试1: 守护线程中的超时机制
|
||||
test_timeout_in_daemon_thread()
|
||||
|
||||
# 测试2: 内存分析性能
|
||||
test_memory_analysis_performance()
|
||||
|
||||
# 测试3: 并发分析
|
||||
test_concurrent_analysis()
|
||||
|
||||
print("\n测试完成!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user