uttrflow / uttrflow/uttrflow-swift

leaks reports an OnboardingFlow retain cycle and MLX quantize array cycles on every launch

Open
#411 3 comments 0 reactions 1 assignee Claimed by @naveen-bhatt View on GitHub
area:ai-suggestions
Dominant language
Swift
Stars
4
Forks
17
Avg merge
3h 32m
Merged PRs (30d)
277

Description

**Severity guess: P2.** One-off at launch, not growing.

## What `leaks` reports

`leaks ` on the running app reports roughly 3,400 to 9,000 leaked nodes, 0.6 to 1.8 MB, within a minute of launch. Two groups are ours to look at:

1. **`OnboardingFlow` retain cycle, about 2.4 KB per launch.** Allocation stack from `MallocStackLogging=1`:
`AppDelegate.applicationDidFinishLaunching` (AppDelegate.swift:206) → `AppDelegate.presentOnboardingIfNeeded()` (AppDelegate.swift:316) → `OnboardingWindowController.init` → `OnboardingFlow`. The throwaway controller built only to read `isRequired` never deallocates, so something inside the flow holds itself.
2. **MLX array cycles from quantising the suggestion model, 0.4 to 1.7 MB per load.** Every root is a `std::shared_ptr` cycle allocated in `LLMModelFactory._load` → `loadWeights` (Load.swift:55) → `quantize(model:)` → `Linear.toQuantized` / `Embedding.toQuantized` → `mlx::core::affine_quantize` → `array::make_arrays`. The multi-output arrays hold each other as siblings. This is CPU-side bookkeeping. The GPU buffers are not in it: IOAccelerator memory settled at 2.49 GB and stayed there.

The rest are system frameworks (AppIntents `NSXPCConnection` cycles, 4.7 KB).

## Measured

Release build (`make app`) of `main` at 7274de2 plus the heads of #362, #363, #364 and #365.

| Time after launch | Leaks | Leaked bytes |
|---|---|---|
| 1 min | 3,420 | 582 KB |
| 30 min | 3,420 | 583 KB |
| 92 min | 3,419 | 583 KB |
| 125 min, after visiting every page and Settings section | 5,224 | 914 KB |
| a separate launch with `MallocStackLogging=1`, 1 min and then 11 min with suggestion passes running | 9,040 then 9,040 | 1.76 MB then 1.76 MB |

The single step up at 125 min was one more `ArrayDesc` cycle of 311 KB. Its stack was not captured.

## Repro

`open Uttrflow.app`, wait for the model to load, `leaks $(pgrep -x Uttrflow)`. For stacks, launch with `open --env MallocStackLogging=1 Uttrflow.app`.

## Why it matters

Only as a quantity per model load. If anything reloads the suggestion model (a settings change or a model swap), each reload leaks another 0.4 to 1.7 MB.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.