diff --git a/raw/channel/1002102092834/msg-14.json b/raw/channel/1002102092834/msg-14.json new file mode 100644 index 0000000..c364a8d --- /dev/null +++ b/raw/channel/1002102092834/msg-14.json @@ -0,0 +1,54 @@ +{ + "message_id": 55, + "from": { + "id": 76226374, + "is_bot": false, + "first_name": "Alexander ƛrtemenko", + "last_name": "svetlyak40wt", + "username": "svetlyak40wt", + "language_code": "en", + "is_premium": true + }, + "date": 1738415033, + "chat": { + "id": 76226374, + "type": "private", + "username": "svetlyak40wt", + "first_name": "Alexander ƛrtemenko", + "last_name": "svetlyak40wt" + }, + "forward_origin": { + "type": "channel", + "date": 1737192361, + "chat": { + "id": -1002102092834, + "type": "channel", + "title": "40 Ants – новости про проекты студии, IT, программирование и много ❤️ к Common Lisp", + "username": "the40ants" + }, + "message_id": 14 + }, + "text": "Почему GC SBCL может не освобождать память?\n\nПоследнее время я работаю над новой версией фреймфорка для создания Telegram ботов. Этот фреймворк использует библиотеку реализующую акторы. И нем обнаружился досадный баг, который я намеревался исправить. Однако в процессе исправления оказалось, что оно может повлиять на производительность. Хорошо что у автора отыскался benchmark с помощью которого можно проверить скорость работы актора.\n\nК моему удивлению этому бенчмарку не хватало 4G памяти для хоть сколько-нибудь длительной работы. Более того, если бенчмарк запустить ненадолго, то оказывалось, что после его работы процесс \"толстел\" на 2.5G и не отпускал эту память до тех пор, пока не сделаешь вручную (sb-ext:gc :full t).\n\nЭто поведение показалось крайне странным. Как вообще можно использовать это язык в production, если он не отпускает память!?\n\nТак я оказался втянут в исследование того, почему garbage collector SBCL не очищает кучу мусора оставшуюся после теста.\n\nПрошло три дня.\n\nПосле некоторых исследований у меня появилась гипотеза, почему GC не очищает память.\n\nДело в том, что в бенчмарке N потоков генерят сообщения к одному актору. Если актор не успевает разгребать сообщения, то те накапливаются в очереди. Тест заканчивается, когда все сообщения в очереди обработаны.\n\nКогда в очереди много сообщений и срабатывает GC, то он видит, что на эти сообщения есть ссылки, и не может их подчистить, а потому перекладывает эти объекты в более старшее поколение. И чем дольше разгребается очередь в процессе генерации объектов, тем больше таких объектов оказывается в старших поколениях garbage collector.\n\nКогда тест заканчивается, то ссылок на сообщения уже нет, но из-за того, что GC поместил их в старшие поколения, при регулярных запусках он до этих объектов не добирается и они так и остаются висеть в памяти. А вот (gc :full t) их подбирает и подчищает.\n\nКак я это понял? Хотелось бы ответить: \"Очень просто!\", но нет \uD83D\uDE41\n\nСначала я решил поисследовать природу объектов, остающихся висеть в памяти после бенчмарка и написал вот такую функциюЖ\n\n\n(defun get-random-dynamic-object ()\n (let ((count 0))\n (sb-vm:map-allocated-objects (lambda (obj type size)\n (declare (ignore obj type size))\n (incf count))\n :dynamic)\n (let ((random-idx (random count))\n (found-obj nil)\n (current-idx 0))\n (sb-vm:map-allocated-objects (lambda (obj type size)\n (declare (ignore type size))\n (when (= current-idx random-idx)\n (setf found-obj\n (trivial-garbage:make-weak-pointer obj)))\n (incf current-idx))\n :dynamic)\n (values found-obj\n random-idx\n count))))\n\n\nона достает из памяти случайны объект и возвращает weak указатель на него. Почему weak указатель? Чтобы не возникло лишней ссылки\nна объект.\n\nВыяснилось, что значительная часть объектов, это сообщения из очереди актора:\n\n\n#<weak pointer: (#<ACTOR path: /user/actor-365, cell: #<ACTOR actor-365, running: NIL, state: NIL, message-box: #<MESSAGE-BOX/BT mesgb-366, processed messages: 8000001, max-queue-size: 0, queue: #<QUEUE-UNBOUNDED {70050E0113}>>>>\n NIL NIL)>\n\n\nДалее я попытался выяснить а не держит ли кто ссылки на эти объекты. Для этого в SBCL есть функция поиска корней:", + "entities": [ + { + "offset": 708, + "length": 19, + "type": "code" + }, + { + "offset": 1836, + "length": 12, + "type": "code" + }, + { + "offset": 2064, + "length": 876, + "type": "pre" + }, + { + "offset": 3163, + "length": 261, + "type": "pre" + } + ] +} \ No newline at end of file diff --git a/ru/posts/pochemu-gc-sbcl-mozhet-14.post b/ru/posts/pochemu-gc-sbcl-mozhet-14.post new file mode 100644 index 0000000..2a6e836 --- /dev/null +++ b/ru/posts/pochemu-gc-sbcl-mozhet-14.post @@ -0,0 +1,16 @@ +;;;;; +title: Почему GC SBCL может не освобождать память? +tags: +created-at: 2025-01-18 +format: md +tg-chat-id: -1002102092834 +tg-message-id: 14 +;;;;; + + + + + + + +**Обсудить пост в [Telegram канале](https://t.me/c/2102092834/14).**