mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-14 05:49:02 +02:00
fix(memory): release retired runtimes and idle download heap
This commit is contained in:
@@ -1395,6 +1395,7 @@ class DownloadService : Service() {
|
||||
failed = counts.failed,
|
||||
)
|
||||
currentStatus = "finalizing"
|
||||
releaseIdleDownloadMemory()
|
||||
writeNativeWorkerSnapshot(
|
||||
isRunning = false,
|
||||
isPaused = false,
|
||||
@@ -1702,6 +1703,7 @@ class DownloadService : Service() {
|
||||
failed = counts.failed,
|
||||
)
|
||||
currentStatus = "finalizing"
|
||||
releaseIdleDownloadMemory()
|
||||
writeNativeWorkerSnapshot(
|
||||
isRunning = false,
|
||||
isPaused = false,
|
||||
@@ -1718,6 +1720,16 @@ class DownloadService : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun releaseIdleDownloadMemory() {
|
||||
try {
|
||||
// All workers and album tagging have finished. Return unused Go
|
||||
// heap without forcing a collection between individual tracks.
|
||||
Gobackend.releaseMemory()
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.w("SpotiFLAC", "Failed to release idle download memory: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun ensureWakeLock() {
|
||||
val existingWakeLock = wakeLock
|
||||
|
||||
@@ -201,6 +201,9 @@ func acquireIsolatedExtensionRuntime(ext *loadedExtension) (*goja.Runtime, *exte
|
||||
ext.isolatedPoolMu.Lock()
|
||||
if n := len(ext.isolatedPool); n > 0 {
|
||||
handle := ext.isolatedPool[n-1]
|
||||
// Reslicing alone retains this handle in the backing array. If the
|
||||
// borrowed VM is retired after an error, its heap would stay alive.
|
||||
ext.isolatedPool[n-1] = nil
|
||||
ext.isolatedPool = ext.isolatedPool[:n-1]
|
||||
ext.isolatedPoolMu.Unlock()
|
||||
return handle.vm, handle.runtime, nil
|
||||
|
||||
@@ -1,10 +1,39 @@
|
||||
package gobackend
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
"weak"
|
||||
)
|
||||
|
||||
func TestDiscardedPooledRuntimeCanBeCollected(t *testing.T) {
|
||||
ext := newTestLoadedExtension(t, ExtensionTypeDownloadProvider)
|
||||
discarded := func() weak.Pointer[extensionRuntime] {
|
||||
vm, rt, err := acquireIsolatedExtensionRuntime(ext)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := vm.Set("temporaryDownloadBytes", vm.NewArrayBuffer(make([]byte, 16<<20))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
releaseIsolatedExtensionRuntime(ext, vm, rt, true, true, nil)
|
||||
vm, rt, err = acquireIsolatedExtensionRuntime(ext)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pointer := weak.Make(rt)
|
||||
// An operation error retires the borrowed VM instead of pooling it.
|
||||
releaseIsolatedExtensionRuntime(ext, vm, rt, false, true, nil)
|
||||
return pointer
|
||||
}()
|
||||
runtime.GC()
|
||||
if discarded.Value() != nil {
|
||||
t.Fatal("retired runtime and its download buffer remain reachable from the idle pool")
|
||||
}
|
||||
runtime.KeepAlive(ext)
|
||||
}
|
||||
|
||||
func TestReleaseMemoryUnderPressureClearsDisposableCaches(t *testing.T) {
|
||||
clearCoverMemoryCache()
|
||||
coverCachePut("https://example.com/cover.jpg", []byte("cover"))
|
||||
|
||||
@@ -1893,6 +1893,10 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
_downloadCount = 0;
|
||||
}
|
||||
|
||||
if (!stoppedWhilePaused) {
|
||||
await PlatformBridge.releaseNativeMemory();
|
||||
}
|
||||
|
||||
_log.i(
|
||||
'Queue stats - completed: $_completedInSession, failed: $_failedInSession, totalAtStart: $_totalQueuedAtStart',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user