feat: 增加每日内存回收与 malloc arena 限制

流媒体会话会在 glibc malloc arena 中留下大量不归还操作系统的冷匿名内存,实测单个
服务可累积到 2.5 GB 并占满整个 swap 文件(33 个 64/128 MB 匿名段,空闲时不增长,
随会话单调抬升)。本次从根因与兜底两侧处理:

- systemd/maa-online-server.service: 设置 MALLOC_ARENA_MAX=2,把线程 arena 数量
  从 8xCPU 压到 2,抑制 arena 膨胀
- systemd/maa-daily.service: 单元结束时经 ExecStopPost 重启 API 服务,回收当次会话残留
- systemd/maa-online-recycle.service, maa-online-recycle.timer: 新增每日 03:59(游戏
  每日刷新前)独立回收定时器,错开更新与日常任务窗口,Persistent=false 避免补跑撞车
- bin/maa-online-can-recycle: 回收守卫,检测日常任务(systemd 单元或手工运行)是否
  进行中,是则跳过回收,避免重启打断任务
- bin/maa-online-install-systemd: 安装并启用新增的回收单元
- README.md, user_config/README.md: 补充内存回收机制与定时器说明
This commit is contained in:
RRRosmontis
2026-09-19 23:58:54 +08:00
parent 0264053b08
commit 7806524279
8 changed files with 84 additions and 10 deletions
+6 -2
View File
@@ -9,6 +9,10 @@ ExecStart=__ROOT__/bin/maa-online-daily-run
# A normal run takes about 25 minutes; the runner's own per-attempt watchdog
# aborts a hung session well before this ceiling.
TimeoutStartSec=7200
# Safety net: whatever stops the unit (success, failure, timeout), make sure
# the remote cloud session is released.
# Safety net: whatever stops the unit (success, failure, timeout), release the
# remote cloud session first...
ExecStopPost=/bin/sh -c "/usr/bin/curl --noproxy '*' -fsS -X POST http://127.0.0.1:22888/exit || true"
# ...then recycle the API server. A streaming session leaves gigabytes of cold
# anonymous memory in glibc malloc arenas that is never returned to the OS;
# without a recycle it accumulates until the whole swap file is used up.
ExecStopPost=/bin/systemctl --no-block restart maa-online-server.service
+12
View File
@@ -0,0 +1,12 @@
[Unit]
Description=Recycle the MAA Online cloud game API server (reclaim swapped anonymous memory)
[Service]
Type=oneshot
# Never recycle while a daily run is in progress: the timer fires next to the
# daily window, so a run can already be underway.
ExecCondition=__ROOT__/bin/maa-online-can-recycle
# Streaming sessions churn large temporaries across the asyncio/aiohttp/aiortc
# thread pool; glibc keeps the freed arenas, so a restart is what actually
# returns the memory (including anything parked in swap) to the OS.
ExecStart=/bin/systemctl restart maa-online-server.service
+16
View File
@@ -0,0 +1,16 @@
[Unit]
Description=Daily recycle of the MAA Online API server just before the Arknights daily reset
[Timer]
# 03:59 sits immediately ahead of the 04:00 in-game daily reset and of the
# maa-daily.timer window (04:00 + up to 2h), so the API server is freshly
# recycled exactly when the daily run starts. Adjust both if the game server's
# reset time differs.
OnCalendar=*-*-* 03:59:00
RandomizedDelaySec=0
# No catch-up run: a late recycle could otherwise land inside the daily window.
Persistent=false
Unit=maa-online-recycle.service
[Install]
WantedBy=timers.target
+6
View File
@@ -11,6 +11,12 @@ RestartSec=5
# Belt and braces: never let a proxy environment leak into the cloud gaming
# server (bin/maa-online-server already unsets these).
Environment=NO_PROXY=localhost,127.0.0.1,::1
# Streaming sessions churn large temporaries (video frames, PNG buffers) across
# the asyncio/aiohttp/aiortc thread pool. glibc gives each thread its own 64 MB
# malloc arena and never returns it to the OS, so the service used to ratchet up
# to gigabytes of cold anonymous memory and eventually fill the swap file.
# Capping the arena count keeps the footprint flat.
Environment=MALLOC_ARENA_MAX=2
[Install]
WantedBy=multi-user.target