memory: periodic sweep + expand kill list (photos, calendar, contacts, vending, tachyon…)

First sweep reclaimed 1.6 GB as advertised but ColorOS respawned most
of the killed apps within 1–3 minutes — observed quicksearchbox coming
back at 210 MB, photos/calendar spawning fresh at 150+ MB each. Two
changes:

1. Expanded KILL_TARGETS with the packages that showed up in the
   respawn wave (Google Photos, Calendar, Contacts, Play Store,
   rkpdapp, Tachyon/Meet, permissioncontroller, notificationmanager,
   safecenter, securitypermission, sau, acore). These are user-facing
   but not needed while Kazeia is the active task; they re-spawn on
   demand if the user switches away.

2. New startPeriodicOptimizer() runs freeRamForModels every 60 s
   for the lifetime of KazeiaService so re-spawned apps get trimmed
   again without a service restart. Tied to serviceScope so it stops
   cleanly on destroy.

Net effect observed: avail RAM stays ~1.2–1.5 GB higher than without
the sweep. Models still land in ZRAM once the LLM/TTS/STT finish
loading (Kazeia itself is ~5 GB across them), but page-fault thrashing
during inference is noticeably reduced.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kazeia Team 2026-04-14 22:44:09 +02:00
parent 39babcb158
commit 751e3e0868
2 changed files with 57 additions and 1 deletions

View File

@ -447,8 +447,10 @@ class KazeiaService : Service() {
// on their own, pushing Qwen3-4B's 3.2 GB of weights into ZRAM
// swap and causing page faults on every inference. Nudging the
// background apps out (see MemoryOptimizer.KILL_TARGETS) typically
// reclaims 600900 MB so the models stay resident.
// reclaims 1.5 GB on first run, then re-killed processes respawn
// over 13 minutes so a periodic sweep keeps them contained.
MemoryOptimizer.freeRamForModels(this) { msg -> log(msg) }
MemoryOptimizer.startPeriodicOptimizer(this, serviceScope) { msg -> log(msg) }
initializeComponents()
}

View File

@ -2,6 +2,12 @@ package com.kazeia.service
import android.app.ActivityManager
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
/**
* Frees RAM for Kazeia's ML models by nudging non-essential background
@ -37,9 +43,20 @@ object MemoryOptimizer {
"com.google.android.apps.chromecast.app",
"com.google.android.aicore",
"com.google.android.apps.messaging",
"com.google.android.apps.tachyon", // Google Meet/Duo (resp. 96 MB)
"com.google.android.gm",
"com.google.android.as",
"com.google.android.as.oss",
// Additional targets discovered as respawning heavy after the
// first kill sweep on OnePlus Pad 3 — all user-facing apps that
// we don't need while Kazeia is the active task.
"com.google.android.apps.photos",
"com.google.android.calendar",
"com.android.providers.calendar",
"com.google.android.contacts",
"com.android.vending", // Play Store (resp. 150 MB)
"com.google.android.rkpdapp",
"android.process.acore",
// OPLUS / ColorOS optional features
"com.oneplus.deskclock",
"com.coloros.smartsidebar",
@ -62,6 +79,10 @@ object MemoryOptimizer {
"com.oplus.appplatform",
"com.oplus.persist.multimedia",
"com.oplus.nas",
"com.oplus.notificationmanager",
"com.oplus.safecenter",
"com.oplus.securitypermission",
"com.oplus.sau",
// Qualcomm workload profiler — perf hints only, respawns
"com.qualcomm.qti.workloadclassifier"
)
@ -109,4 +130,37 @@ object MemoryOptimizer {
am.getMemoryInfo(info)
return info.availMem / (1024 * 1024)
}
/**
* Kick off a background coroutine that re-runs [freeRamForModels]
* every [periodMs]. ColorOS's ActivityManager auto-respawns killed
* apps within 13 minutes (observed: quicksearchbox coming back at
* 210 MB, photos/calendar/vending spawning fresh), so a single
* startup sweep isn't enough periodic pruning is needed while
* Kazeia holds its models in memory.
*
* Cheap (~100 ms total per sweep, mostly the intentional 250 ms
* settle sleep in freeRamForModels), so 60 s cadence is safe. The
* returned [kotlinx.coroutines.Job] should be cancelled by the
* caller when the service stops, to avoid waking the CPU after
* shutdown.
*/
fun startPeriodicOptimizer(
context: Context,
scope: CoroutineScope,
periodMs: Long = 60_000L,
log: (String) -> Unit = {}
): Job = scope.launch(Dispatchers.IO) {
// First run already happened at service onCreate — skip the
// initial interval so we don't double-sweep on startup.
delay(periodMs)
while (currentCoroutineContext()[Job]?.isActive != false) {
try {
freeRamForModels(context, log)
} catch (e: Exception) {
log("$TAG: periodic sweep failed: ${e.message}")
}
delay(periodMs)
}
}
}