По данным официальной документации Ollama API, параметр keep_alive: 0 должен немедленно выгружать модель после запроса. Но это не доказывает, что освободится вся память Runner или Metal. Поэтому для Ollama 0.34.0 и DeepSeek-R1 мы рекомендуем не увеличивать лимит памяти и не сокращать контекст вслепую: сначала зафиксировать процесс, давление на память и результат следующей холодной загрузки, затем использовать выгрузку только на границе задания. Если Runner продолжает расти от запроса к запросу, keep_alive=0 — временное средство остановить накопление, а не исправление утечки.
Последнее обновление: 15 сентября 2026 года. Данные сверены с официальным релизным статусом Ollama, документацией API, материалами Apple по Unified Memory и Metal, а также с открытым сообществом Issue.
Эта статья предназначена для трёх групп:
- разработчиков пакетной обработки, которым нужно включить возврат модели в жизненный цикл задания;
- инженеров AI Agent, поддерживающих постоянный сервис с диалогами, инструментами и несколькими вызывающими сторонами;
- технических руководителей, выбирающих между холодной загрузкой, изоляцией процессов и отдельным Mac-узлом.
Сначала разделите штатное удержание модели и настоящий рост
После ответа Ollama может продолжать занимать память по совершенно нормальной причине: модель ещё находится в периоде удержания, заданном keep_alive. Это сделано для того, чтобы следующий запрос не начинался с повторной загрузки весов. При этом список запущенных моделей может быть корректным, а Activity Monitor всё равно будет показывать существенное потребление памяти.
Условно занятое пространство состоит из нескольких разных объектов:
- веса модели, размещённые в памяти процесса Runner;
- KV Cache, который зависит от длины контекста и количества параллельных последовательностей;
- отображения файлов, которые не всегда сразу исчезают из системной картины памяти;
- собственная куча Runner и вспомогательные структуры Metal;
- временные буферы конкретного запроса.
Одна цифра в Activity Monitor не показывает, какой именно компонент продолжает жить. Apple отдельно объясняет, что показатель давления на память оценивает доступность ресурсов для системы, а не просто складывает объём одного процесса. Поэтому описание давления на память в Activity Monitor полезнее одиночного снимка столбца «Память».
Почему после завершения запроса Ollama всё ещё занимает много памяти на Mac?
Если модель отображается среди работающих, а память перестаёт расти и давление остаётся зелёным, наиболее вероятно штатное удержание или файловое отображение, а не доказанная утечка. Если же один и тот же Runner увеличивается после одинаковых запросов, модель исчезает из списка, но процесс не уменьшается, либо давление переходит в жёлтую или красную зону, это уже отдельный сценарий для воспроизведения.
Для проверки нужно сопоставить как минимум три источника:
- список активных моделей через API или команду Ollama;
- журналы службы;
- идентификатор Runner и системные показатели памяти.
Сведения о macOS-журналах и стандартном поиске причин собраны в официальном руководстве Ollama по устранению неполадок. Не следует объявлять проблему утечкой только потому, что после окончания ответа память не вернулась к исходной отметке.
Наблюдайте за переменной, которая действительно вызывает рост
Сокращение контекста помогает только тогда, когда рост связан с KV Cache. Если память изменяется вместе с количеством запросов при одинаковой длине контекста, источник может находиться в куче Runner, очереди запросов, обработке ответов или конкретном пути Metal.
Перед изменением параметров зафиксируйте три независимые переменные:
- длину входного контекста;
- число последовательных запросов;
- количество одновременных вызовов.
Минимальный тест должен использовать один и тот же вариант DeepSeek-R1, одинаковый шаблон запроса и одинаковый порядок вызовов. Сначала выполните один запрос, затем несколько запросов последовательно, после чего повторите серию с параллельностью. После каждой серии сохраните время, состояние модели, PID Runner, давление на память и наличие swap-активности.
В открытом Issue сообщества Ollama о росте кучи Runner на macOS и Metal описан случай, где API показывал стабильное потребление модели, а процесс продолжал увеличиваться при росте объёма запросов. Важно не расширять этот вывод: пример относится к Ollama 0.32.15 и другой модели, не к DeepSeek-R1 и не к официально подтверждённой проблеме Ollama 0.34.0. Это полезная линия расследования, но не доказательство для текущей нагрузки.
Может ли уменьшение контекста не дать результата?
Да. Если память растёт от числа запросов или параллельности, а не от длины каждого контекста, уменьшение контекстного окна лишь изменит время наступления проблемы. В таком случае нужно проверить жизненный цикл Runner и границы пакетной обработки, а не бесконечно снижать качество ответа ради меньшего буфера.
Отдельно учитывайте Apple Silicon. Unified Memory означает, что CPU и GPU используют общую физическую память, однако это не превращает любой показатель Metal в прямой эквивалент свободной оперативной памяти. Apple предоставляет отдельное описание свойства Unified Memory у Metal-устройства, а также документацию по рекомендуемому размеру рабочего набора Metal. Эти параметры помогают оценивать рабочий набор графической подсистемы, но не заменяют наблюдение за Runner и давлением памяти macOS.
Безопасная выгрузка начинается с различия между API и службой
В API Ollama keep_alive: 0 относится к жизненному циклу конкретной модели после конкретного запроса. Пример запроса можно оформить так:
curl http://127.0.0.1:11434/api/generate \
-H 'Content-Type: application/json' \
-d '{
"model": "deepseek-r1",
"prompt": "Короткая проверочная задача",
"keep_alive": 0,
"stream": false
}'
Точный смысл параметра следует сверять с описанием API и поля keep_alive. Команда остановки модели действует иначе: она обращается к уже загруженной модели через интерфейс Ollama и не является универсальной командой завершения всех процессов службы.
Разделяйте три ситуации:
- Ollama запущена как приложение macOS;
- служба запущена вручную в терминале;
- запросы приходят в HTTP API от пакетного обработчика или Agent.
Для приложения нельзя автоматически предполагать, что закрытие одного окна завершило все Runner-процессы. Для ручного запуска нужно сохранять терминал и журнал, из которого видно, когда создавался и завершался Runner. Для API-сервиса важно не отправлять остановку из каждого обработчика запроса: один вызов может ещё использовать модель, пока другой уже решил, что её нужно выгрузить.
Перед выгрузкой зафиксируйте:
pgrep -af 'ollama|runner'
Затем проверьте список моделей штатной командой Ollama, сохраните журнал и отправьте запрос с keep_alive: 0 либо используйте остановку модели в предусмотренном интерфейсом режиме. После этого повторите проверку процесса и списка. Для каждого наблюдения запишите:
- PID Runner до и после операции;
- наличие модели в списке;
- давление на память;
- активность swap;
- время следующей загрузки;
- факт успешного или прерванного задания.
Как убедиться, что после выгрузки память действительно вернулась?
Исчезновение модели из списка подтверждает изменение состояния Ollama, но не доказывает немедленное восстановление всей памяти macOS. Надёжная проверка состоит из четырёх частей: старый Runner завершился или изменился, давление на память улучшилось, swap не продолжает расти, а повторный запрос выполняется с новой холодной загрузкой. Если старый процесс остался, а Metal уже находится в ошибочном состоянии, нужно сохранить журналы и перейти к полному перезапуску службы, не маскируя результат повторными вызовами API.
Не добавляйте непроверенные значения
sysctlи не объявляйте их универсальным лечением. В рассматриваемом сценарии проблема может быть в физическом исчерпании Unified Memory, а может — в логическом росте процесса после запросов; один системный параметр не различает эти причины.
Постоянная выгрузка меняет профиль стоимости задания
Выгрузка после каждого запроса кажется безопасной, но она может превратить проблему удержания памяти в проблему холодной загрузки. Перед каждым новым ответом модель должна снова подготовить веса и рабочие буферы, а Agent может потерять ожидаемое состояние сессии или задержать вызов инструмента.
Поэтому границу освобождения нужно выбирать не по привычке, а по структуре нагрузки:
- для короткой пакетной серии — после завершения всей серии;
- для нескольких независимых проектов — после завершения проекта;
- для очереди — когда очередь пуста и установлен короткий период покоя;
- для непрерывного диалога — не после каждого сообщения;
- для общего Agent-сервиса — только после согласованного drain-режима.
Все значения времени загрузки и объёма возвращённой памяти должны быть получены из собственного воспроизводимого теста. Универсального порога, после которого следует обязательно перезапускать Runner, нет: он зависит от модели, контекста, параллельности, свободной памяти и конкретного Mac.
Удобная последовательность из пяти этапов выглядит так:
- Снять базовую точку. Записать PID, список моделей, давление памяти, swap и состояние службы до первого запроса.
- Выполнить контролируемую серию. Использовать одинаковые запросы, заранее зафиксировать контекст и число параллельных вызовов.
- Закрыть границу работы. Для пакетной задачи дождаться завершения всех ответов и только затем отправить
keep_alive: 0. - Проверить выгрузку. Сопоставить список моделей, PID Runner, журналы и показатели macOS; не считать один исчезнувший элемент достаточным доказательством.
- Проверить холодный старт. Выполнить новый запрос, измерить фактическую задержку и убедиться, что прошлое задание не было прервано.
После этого полезно поставить оценку варианту по четырём критериям: стабильность, стоимость холодных загрузок, безопасность параллельных вызовов и возможность быстро заменить узел. Оценка должна быть внутренним инструментом конкретной команды, а не универсальным показателем производительности.
Параллельные вызовы требуют блокировки, очереди и проверки здоровья
Если несколько пользователей или инструментов обращаются к одной модели, один из них не должен самостоятельно выгружать её по окончании своей операции. Иначе второй запрос может получить прерывание, повторную холодную загрузку или непредсказуемое состояние соединения.
Для API-сервиса безопаснее использовать следующий порядок:
- перевести узел в режим drain, чтобы новые задания не принимались;
- дождаться завершения активных вызовов;
- получить эксклюзивную блокировку на выгрузку;
- отправить
keep_alive: 0или команду остановки; - проверить PID, список моделей, журналы и давление памяти;
- выполнить health check;
- снять drain и разрешить новые запросы;
- при ошибке повторить запрос с ограниченным числом повторов, а не запускать бесконечные рестарты.
Для планового пакетного запуска контроль проще: планировщик должен считать задание завершённым только после сохранения результата, закрытия всех дочерних процессов и успешной проверки освобождения памяти. Если проверка не пройдена, узел следует пометить для ручного осмотра, а не немедленно возвращать в очередь.
Периодическое принудительное завершение процесса нельзя объявлять стандартным исправлением для любой производственной среды. Оно может потерять активные ответы, оборвать вызовы инструментов и скрыть воспроизводимость дефекта. Такой шаг допустим только при заранее определённых условиях, наличии повторного запуска и сохранённых логах.
Как часто перезапускать Runner при постоянной работе DeepSeek-R1?
Не по календарю и не по одной цифре использования памяти. Частоту нужно выводить из собственного теста: после какого числа серий растёт процесс, сколько заданий завершается успешно, сколько стоит холодный старт и восстанавливается ли сервис без ручного вмешательства. Если перезапуск требуется всё чаще, это уже аргумент в пользу изоляции, а не повод бесконечно менять параметры.
Сравнение вариантов перед переходом к другой архитектуре
Перед решением о смене среды нужно оценить не только пиковое потребление, но и последствия сбоя. В таблице ниже знак «+» означает относительное преимущество для соответствующей задачи, а не гарантированную производительность.
| Вариант | Стабильность при росте Runner | Холодная загрузка | Работа с параллельными Agent | Когда выбирать |
|---|---|---|---|---|
| Одна Ollama-служба на рабочем Mac | Средняя | Низкая стоимость повторного вызова | Требует собственной блокировки | Небольшие контролируемые серии |
| Выгрузка на границе каждой серии | Выше при ограниченной памяти | Выше стоимость простоя | Безопасна после drain | Независимые пакетные задания |
| Отдельный процессный изолированный узел | Выше | Зависит от схемы замены | Проще локализовать сбой | Долгоживущий Agent и несколько клиентов |
| Временный удалённый Mac-узел | Зависит от провайдера и проверки | Оплачивается вместе с запуском среды | Удобнее заменять узел | Тестирование, пиковые и сменные нагрузки |
Критически важно не путать «модель исчезла из списка» с «узел снова готов к любой нагрузке». Готовность подтверждается повторным health check, отсутствием старого Runner и успешным завершением тестового запроса.
Решение с оценкой по четырём критериям
| Условие наблюдения | Продолжать на локальном Mac | Перейти к изоляции | Оценка риска |
|---|---|---|---|
| Рост связан только с удержанием модели, давление остаётся зелёным | Да, выгружать на границе серии | Не требуется | Низкий |
| Рост повторяется от числа запросов, но задания можно поставить в очередь | Да, после drain и проверки | Желательно для рабочих сервисов | Средний |
| Runner остаётся после выгрузки, Metal-состояние ошибочное | Только для воспроизведения | Да, с заменяемым процессом или узлом | Высокий |
| Общий Agent не выдерживает остановку одного вызывающего процесса | Не использовать одиночную выгрузку | Да, с блокировкой и отдельным узлом | Высокий |
| Холодная загрузка регулярно срывает SLA | Только для коротких тестов | Рассмотреть постоянную изолированную среду | Высокий |
Чек-лист перед окончательным решением
- [ ] Зафиксированы версия Ollama 0.34.0, модель DeepSeek-R1 и способ запуска службы.
- [ ] Сохранены PID Runner, состояние списка моделей и показатели давления памяти до теста.
- [ ] Разделены длина контекста, число запросов и параллельность.
- [ ] Выполнена серия с одинаковыми запросами и сохранены журналы Ollama.
- [ ] Проверено значение
keep_aliveв фактическом JSON-запросе. - [ ] Выгрузка выполняется только после завершения всех активных вызовов.
- [ ] После выгрузки проверены старый PID, список моделей, давление памяти и swap.
- [ ] Отдельно измерен следующий холодный запуск, без обобщения его результата на все Mac.
- [ ] Для API включены drain, блокировка выгрузки, health check и ограниченный повтор.
- [ ] Определено условие выхода из экспериментов с параметрами: рост Runner, сбои заданий или неприемлемая стоимость восстановления.
- [ ] Решено, что именно заменяется при сбое: запрос, Runner, служба или весь Mac-узел.
Когда настройка уже не решает проблему
Продолжать локальный запуск разумно, если рост прекращается после штатной выгрузки, задания не прерываются, а холодная загрузка не разрушает экономику очереди. Разделить задания по процессам стоит тогда, когда разные клиенты имеют разные сроки выполнения или один сбой не должен затрагивать остальных.
Переход к отдельному Mac-узлу становится обоснованным, когда выполнение требует регулярного принудительного перезапуска, Runner не возвращается в исходное состояние, параллельные вызовы конфликтуют, а команда уже тратит больше времени на ручное восстановление, чем на полезную обработку. Для предварительной оценки доступных сценариев можно изучить сравнение конфигураций Mac для Xcode и CI, а вопросы подключения и рабочего процесса сверить с руководством по аренде Mac.
На обычном локальном Mac текущая схема часто проигрывает по трём причинам: память разделяется с другими задачами разработчика, один Runner может затронуть весь рабочий узел, а ручная выгрузка плохо сочетается с несколькими Agent-клиентами. Самостоятельная покупка мощной машины решает часть проблемы, но фиксирует капитальные затраты и не устраняет ошибку жизненного цикла процесса. Если требуется временно проверить реальную серию запросов, безопаснее рассмотреть аренду Mac у JexMac как отдельный заменяемый контур: там можно оценить холодную загрузку, восстановление и стабильность без немедленного вмешательства в основной компьютер. Варианты условий и доступные способы подключения собраны на странице аренды Mac JexMac.
Начинать стоит с одного собственного прогона «запуск — выгрузка — повторная загрузка» на той же последовательности запросов. Если для успешного завершения приходится регулярно очищать Runner, а изоляция снижает ущерб от сбоя, это уже не задача подбора ещё одного параметра памяти, а решение о границах вычислительного узла.
Перенесите ресурсоёмные задачи на выделенный Mac от JexMac
Запускайте локальные модели на отдельном Mac mini M4 с 16 ГБ Unified Memory без конкуренции за ресурсы с другими пользователями.