From 481f1f9d3079717880679940daa7641a940a5247 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 8 Sep 2025 10:49:09 +0800 Subject: [PATCH] add full gc scheduler --- app/core/config.py | 2 + app/scheduler.py | 26 ++++++++++ app/utils/gc.py | 125 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 app/utils/gc.py diff --git a/app/core/config.py b/app/core/config.py index 5c34955c..313e059e 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -364,6 +364,8 @@ class ConfigModel(BaseModel): ENCODING_DETECTION_PERFORMANCE_MODE: bool = True # 编码探测的最低置信度阈值 ENCODING_DETECTION_MIN_CONFIDENCE: float = 0.8 + # 主动内存回收时间间隔(分钟),0为不启用 + MEMORY_GC_INTERVAL: int = 30 # ==================== 安全配置 ==================== # 允许的图片缓存域名 diff --git a/app/scheduler.py b/app/scheduler.py index 45de5fbb..7f6a1182 100644 --- a/app/scheduler.py +++ b/app/scheduler.py @@ -30,6 +30,7 @@ from app.helper.wallpaper import WallpaperHelper from app.log import logger from app.schemas import Notification, NotificationType, Workflow, ConfigChangeEventData from app.schemas.types import EventType, SystemConfigKey +from app.utils.gc import auto_gc from app.utils.singleton import SingletonClass from app.utils.timer import TimerUtils @@ -181,6 +182,11 @@ class Scheduler(metaclass=SingletonClass): "name": "订阅日历缓存", "func": SubscribeChain().cache_calendar, "running": False + }, + "full_gc": { + "name": "主动内存回收", + "func": self.full_gc, + "running": False } } @@ -413,6 +419,19 @@ class Scheduler(metaclass=SingletonClass): } ) + # 主动内存回收 + if settings.MEMORY_GC_INTERVAL: + self._scheduler.add_job( + self.start, + "interval", + id="full_gc", + name="主动内存回收", + hours=settings.MEMORY_GC_INTERVAL, + kwargs={ + 'job_id': 'full_gc' + } + ) + # 初始化工作流服务 self.init_workflow_jobs() @@ -747,6 +766,13 @@ class Scheduler(metaclass=SingletonClass): """ SchedulerChain().clear_cache() + @auto_gc + def full_gc(self): + """ + 主动内存回收 + """ + pass + def user_auth(self): """ 用户认证检查 diff --git a/app/utils/gc.py b/app/utils/gc.py new file mode 100644 index 00000000..11e88798 --- /dev/null +++ b/app/utils/gc.py @@ -0,0 +1,125 @@ +""" +内存回收装饰器模块 +提供装饰器用于在函数执行后立即回收内存 +""" +import gc +import functools +import psutil +import os +from typing import Callable, Any, Optional + +from app.log import logger + + +def memory_gc(force_collect: bool = True, + log_memory_usage: bool = False) -> Callable: + """ + 内存回收装饰器 + + Args: + force_collect: 是否强制执行垃圾回收,默认True + log_memory_usage: 是否记录内存使用日志,默认False + + Returns: + 装饰器函数 + """ + def decorator(func: Callable) -> Callable: + @functools.wraps(func) + def wrapper(*args, **kwargs) -> Any: + # 记录函数执行前的内存使用情况 + memory_before = None + memory_after = None + if log_memory_usage: + memory_before = get_memory_usage() + logger.info(f"函数 {func.__name__} 执行前内存使用: {memory_before}") + + try: + # 执行原函数 + result = func(*args, **kwargs) + + # 记录函数执行后的内存使用情况 + if log_memory_usage: + memory_after = get_memory_usage() + logger.info(f"函数 {func.__name__} 执行后内存使用: {memory_after}") + if memory_before: + memory_diff = memory_after - memory_before + logger.info(f"函数 {func.__name__} 内存变化: {memory_diff} MB") + + return result + + finally: + # 强制垃圾回收 + if force_collect: + collected = gc.collect() + if log_memory_usage: + logger.info(f"函数 {func.__name__} 垃圾回收完成,回收对象数: {collected}") + + # 记录垃圾回收后的内存使用情况 + if log_memory_usage: + memory_after_gc = get_memory_usage() + logger.info(f"函数 {func.__name__} 垃圾回收后内存使用: {memory_after_gc}") + if memory_after: + memory_freed = memory_after - memory_after_gc + logger.info(f"函数 {func.__name__} 释放内存: {memory_freed} MB") + + return wrapper + return decorator + + +def get_memory_usage() -> float: + """ + 获取当前进程的内存使用情况(MB) + + Returns: + 内存使用量(MB) + """ + try: + process = psutil.Process(os.getpid()) + memory_info = process.memory_info() + return memory_info.rss / 1024 / 1024 # 转换为MB + except Exception as e: + logger.warning(f"获取内存使用情况失败: {e}") + return 0.0 + + +def memory_monitor(threshold_mb: Optional[float] = None) -> Callable: + """ + 内存监控装饰器,当内存使用超过阈值时自动触发垃圾回收 + + Args: + threshold_mb: 内存阈值(MB),超过此值将触发垃圾回收 + + Returns: + 装饰器函数 + """ + def decorator(func: Callable) -> Callable: + @functools.wraps(func) + def wrapper(*args, **kwargs) -> Any: + # 检查内存使用情况 + current_memory = get_memory_usage() + + if threshold_mb and current_memory > threshold_mb: + logger.warning(f"内存使用超过阈值 {threshold_mb}MB,当前使用: {current_memory}MB") + collected = gc.collect() + logger.info(f"自动垃圾回收完成,回收对象数: {collected}") + + # 执行原函数 + result = func(*args, **kwargs) + + # 执行后再次检查并回收 + if threshold_mb: + memory_after = get_memory_usage() + if memory_after > threshold_mb: + collected = gc.collect() + logger.info(f"函数执行后垃圾回收完成,回收对象数: {collected}") + + return result + + return wrapper + return decorator + + +# 便捷的装饰器别名 +memory_cleanup = memory_gc +auto_gc = memory_gc(force_collect=True, log_memory_usage=True) +memory_watch = memory_monitor