Как освободить память, выделенную для какой-либо структуры в Crystal - вручную? - PullRequest
0 голосов
/ 09 июня 2018

У меня есть веб-сервис RESTful на основе Kemal, который возвращает «очень большие» (размером от 10 до 17M) порции данных JSON, которые создаются методом to_json из «большой» структуры Hash.

Согласно предупреждающим сообщениям GC, мой код "может привести к утечкам памяти", а мои собственные измерения показывают, что память "протекает" во время выполнения приложения.

Так что, я думаю, это будет хорошоосвободить память, выделенную для хэша, и ее JSON-строки для представления вручную, но я не знаю, как это сделать: мои эксперименты с плохо документированным методом GC.free не увенчались успехом, и я не знаю, в каком направлении продолжать свои исследования...

Скажите, пожалуйста, что я могу сделать, чтобы избежать утечек памяти?

Вы можете посмотреть не очень свежую, но в целом актуальную версию моего очень простого приложения(фактически он был разработан в закрытом сегменте корпоративной сети) здесь https://github.com/DRVTiny/Druid/blob/master/src/druid_mp.cr

Код, который приводит к утечкам памяти:

get "/service/:serviceid" do |env|
        if (svcid = env.params.url["serviceid"]) && svcid.is_a?(String) && svcid =~ /^s?\d+$/
          druid.svc_branch_get((svcid[0] == 's' ? svcid[1..-1] : svcid).to_i).to_json
        else
          halt env, status_code: 404, response: %q({"error": "Wrong service identificator"})
        end
  rescue ex
        halt env, status_code: 503, response: {"error": "Unhandled exception #{ex.message}"}.to_json
  end

PS Я вставил ловушку after_all, выполняющую GC.collect послекаждый запрос пользователя.Не знаю, возможно, это может решить мою проблему (но я думаю, что это совсем не так).

UPD: После того, как я добавил GC.collect в ловушку after_all Kemal - утечки памяти исчезают.Но глобальный GC.collect, вероятно, слишком медленный и, как я знаю, он блокирует все волокна и socket.accept ().Пожалуйста, дайте мне знать, если я ошибаюсь.

1 Ответ

0 голосов
/ 10 июня 2018

Да, вы не должны вызывать GC.collect после каждого запроса.

Помимо улучшений в GC (которые будут в конечном итоге), самый простой способ - избежать бесполезного выделения строк.Судя по вашему примеру кода, вам не нужен результат вызова to_json в памяти в виде строки.Вы можете просто сериализовать его прямо в поток ввода-вывода, например to_json(env.response).Это быстрее всего и не выделяет дополнительную память, полностью исключая проблему с освобождением памяти.

...