Эта статья — продолжение, начало здесь. Для тех, кто не кликнул на ссылку, краткая вводная:

Мы обсуждаем сборку мусора в операционной системе Фантом, то есть в среде виртуальной (байткод-) машины, работающей в персистентной оперативной памяти. Размер персистентной памяти — порядка размера диска, то есть единицы терабайт на сегодня и, потенциально, десятки и сотни терабайт завтра.

Поскольку речь идёт о виртуальной памяти, то существенная часть объектов в любом случае находится не в оперативной памяти, независимо от того, какой алгоритм и вообще подход мы избрали. То есть — стоимость доступа к объекту велика. Это, в общем случае, дисковая операция.

Кроме того, следует ожидать, что в сетевой среде совокупности таких виртуальных машин будут обмениваться прокси-объектами, то есть будет существовать граф объектов, растянутый на много машин в сети и, конечно, во всём этом кошмаре потенциально потребуется уметь собирать мусор не только на одной машине, но и по сети.

Принятая мной идея схемы сборки мусора в такой среде выглядит как совокупность двух сборщиков.

Первый — быстрый, детерминированный, но не гарантирующий 100% собираемость. В настоящее время он реализован на принципе подсчёта числа ссылок на объект. Неидеальная, но достаточно простая и предсказуемая модель.

Второй — медленный, недетерминированный (трудно предсказать время работы), но точный и бескомпромиссный. В традиционной среде такой сборщик требовал бы остановки мира (stop the world), но есть трюк — если сборку вести на полной копии состояния системы, то весь собранный в копии мусор будет мусором также и в более поздней версии того же состояния, что бы с ней ни происходило. Прелесть подхода в том, что Фантом реализует персистентность именно через создание «снапшотов» — полных копий состояния объектной памяти виртуальной машины. То есть — среда, в которой можно спокойно и рассудительно собирать мусор уже есть.

По сути дела в этот момент задача кажется тривиальной — на «обездвиженном пациенте» собирать мусор можно самым простым и незатейливым способом. Да? Похоже, что нет.

Вопрос первый — хранение промежуточной информации о состоянии сборки. Да и её объём. Мы должны предполагать, что ситуация ужасна и система сумела загнать себя в угол, истратив всё дисковое пространство. Сколько именно мы должны резервировать для сборщика, чтобы он смог закончить работу?

Вопрос второй — рестартуемость алгоритма. Было бы заманчиво реализовать сборщик как штатную программу под Фантом, которая, следовательно, живёт в персистентной памяти и перезапуски ОС для неё незаметны, всё состояние программы сохраняется и сборка мусора просто продолжается дальше после рестарта системы. Но в силу первого требования такая реализация может быть опасна — при нехватке памяти её может «доесть» пользовательский процесс и работа сборщика мусора будет остановлена. Это бы решалось через квотирование выделения памяти, но в текущей версии ОС его нет, ну и в целом решение выглядит очень изящно, но с точки зрения отладки окажется, скорее всего, похожим на ад.

Следовательно, хорошо бы опереть сборщик мусора на некоторую простую структуру данных, которую легко хранить и обрабатывать в линейном виде. Например — односвязный список, организованный как task list, из которого алгоритм вынимает атомарные задачи и в который добавляет задачи в процессе решения «вынутой» задачи.

При этом хорошо бы избежать модификации оригинального снапшота (копии памяти системы), и обновление самого списка сделать атомарным с тем, чтобы выключение алгоритма в любой точке не приводило к сбою.

В целом напрашивается такая наивная реализация: список необойдённых объектов roots, список посещённых объектов visited, и алгоритм, который сводится к:

  • Для пустого roots и visited положить в него объект по адесу ноль
  • Для непустого — прочитать и удалить адрес объекта, если его нет в visited — добавить его в visited, добавить в roots всех его детей
  • Для пустого roots и непустого visited — пройти линейно все объекты в адресном пространстве снапшота, если они не встречаются в visited — пометить их как мусор в актуальном адресном пространстве (этот процесс тоже можно сделать рестартуемым, если записывать время от времени адрес памяти, который мы прошли)

Естественно, это очень неэффективный алгоритм, но его можно оптимизировать довольно тривиальным путём. Например, так: roots делаем не очередью, а стеком и хвост этого стека храним в памяти, при этом большое количество обходов leaf objects будет происходить без модификации дисковой части этого стека. Важно лишь чтобы в дисковую копию объекты попадали только целиком и уходили из неё только после обхода всех детей.

Проблема в том, что каждый шаг этого алгоритма требует просмотра всего visited, что потенциально — крушение всех надежд, фиаско.

Беда ещё и в том, что visited нельзя кешировать в памяти — его обязательно надо просмотреть целиком. Напрашивается очевидная идея — сделать его не списком, а деревом, отсортированным по адресу объекта. Тогда поиск объекта в дереве сократится логарифмически, и, главное — фрагменты дерева можно кешировать, поскольку полный перебор не нужен.

Кстати, если у вас есть идеи по поводу такого алгоритма, напишите, пожалуйста.

Отдельный вопрос возникает в случае отказа дискового ввода-вывода для какой-либо страницы. Строго говоря, в такой ситуации сборка мусора вообще невозможна. Хотя для данного снапшота пропадание страницы (и, следовательно, находящихся в ней ссылок) делает часть объектов недоступными, то есть фактическим мусором, было бы недальновидно просто так взять и всё это уничтожить.

Во-первых, хорошо бы иметь некоторый аналог lost+found для таких ситуаций. Хотя и совершенно неясно, как его реализовать. Во-вторых, в актуальной работающей системе соответствующая страница может быть вполне жива. Поэтому справедливо было бы сделать вот как: проверить, есть ли эта страница в более поздних шотах или в памяти. Если есть в памяти — форсировать её помещение в снапшот, даже если она не менялась (обычно Фантом неизменившиеся страницы, конечно, не пишет повторно), остановить сборку и рестартовать её на более позднем снапшоте. Если же не повезло и страницы нигде нет, включить режим восстановления и по окончании обычной сборки эвристически поискать в мусоре поддеревья объектов ощутимого размера, которые из мусора исключить и «подвесить» в специальное место в объектной иерархии.

Что ещё важно?

В целом подсистема виртуальной персистентной памяти Фантома и его же виртуальная байткод-машина (объектная среда) ни черта друг про друга не знают. Вторая живёт в первой. И всё.

Но один достаточно типовой случай, который нуждается в связке между ними. Этот случай выглядит просто: между двумя снапшотами программа в ОС Фантом выделяет, использует и освобождает пару гигабайт объектов. Например, обсчитывает графику и в процессе создаёт временные данные. К началу снапшота они не нужны и неактуальны. Но память, в которой они лежали, «потрогана», модифицировалась. С точки зрения снапшоттера это — повод записать такую память в снапшот. В реальности её содержимое уже никому не интересно и, более того, должно быть обнулено от греха. Было бы логично при освобождении большого участка памяти сообщить пейджинговой подсистеме, что этот участок не только не dirty, а и вообще не нужен и сохранять его не нужно. А при восстановлении со снапшота его нужно читать как страницу нулей.

Это опять кажется тривиальным, но — нет. В предыдущей статье говорилось о проблеме удаления объектов по обнулению счётчика ссылок и о том, что делать это можно только после прохода всеми нитями границы инструкции виртуальной машины.

Технически самый простой способ это реализовать — сделать удаление после снапшота.

Почему? Потому что снапшот гарантированно приостанавливает все нити на границе инструкции байткода. Почему после, а не во время? Потому что синхронную часть снапшота надо делать бегом, чтобы она была незаметна для работающих программ.

А после — поздно, потому что тогда «ненужные» страницы, всё же, попадут в снапшот.

В итоге это означает, что нужно выполнить не очень очевидную цепочку операций:

  1. Приостановить все треды на границе инструкции и сразу «отпустить» их
  2. Провести освобождение объектов с нулевым счётчиком ссылок, которые были заявлены на удаление до этой остановки (проверяя, что счётчик всё ещё нулевой)
  3. Приостановить все треды на границе инструкции ещё раз
  4. Выполнить синхронную часть снапшота (в памяти)
  5. «Отпустить» остановленные треды
  6. Спокойно доделывать асинхронную часть снапшота (ввод-вывод)

Ко всему этому требуется ещё добавить блокировку того снапшота, на котором идёт сборка мусора, от удаления, а так же механизм, который позволяет держать не менее трёх частично перекрытых снапшотов —

  • Старого, на котором собираем мусор
  • Более актуального, который полон и пригоден к рестарту
  • Последнего, который находится в процессе формирования

А может быть и ещё нескольких, которые хранятся в режиме бекапа/time machine.

На сём позвольте поставить точку с запятой, и задать вопрос: какую статистику по состоянию объектной среды вы бы считали полезной собрать в процессе сборки мусора? Мы всё равно обходим объекты, можно провести тот или иной анализ относительно бесплатно.
Поделиться с друзьями
-->

Комментарии (44)


  1. msts2017
    30.06.2017 10:15
    +3

    все же надо попробовать отказаться от сборки мусора, имхо это более перспективно.


    1. ababo
      30.06.2017 10:35
      +1

      Как и от виртуальной машины.


  1. ababo
    30.06.2017 11:28
    +2

    Разовью свой тезис из прошлого комментария. На мой взгляд, идея виртуальной байт-код машины со сборкой мусора морально устарела после появления Rust. Этот язык демонстрирует альтернативный подход к управлению памятью и синхронизации, основанный на проверках во время компиляции.

    Идеальная система мне видится вот какой. Она чуть менее чем полностью написана на Rust. Исполняемые модули снабжаются манифестом с цифровой подписью. Манифест, в том числе, содержит информацию о том, использовалось ли в коде ключевое слово «unsafe». Модули простых смертных, собранные из кода, содержащего это ключевое слово, не могут быть запущенны. Таким образом нет нужды в жирных абстракциях со сборкой мусора.

    Если надумаете писать Фантом 2.0 без виртуальной машины, готов безвозмездно принимать участие как в проектировании, так и в реализации.


    1. dzavalishin
      30.06.2017 15:59

      Зачем ждать? Сделайте rust рантайм для фантома, ничто не мешает иметь две модели.


    1. dzavalishin
      30.06.2017 16:02
      +2

      Ну и про виртуальную машину — большинство критиков виртуальной машины смотрят на неё как на среду исполнения. Виртуальная машина — это среда исполнения только для кода, который исполняется один раз. Смотрите на неё как на промежуток между двумя фазами компиляции, и жизнь заиграет новыми красками.


      1. ababo
        30.06.2017 18:19

        Жизнь не заиграет красками, потому что мне навязывается объектно-ориентированная модель, которая не всегда уместна, а также сборка мусора, которая подходит далеко не ко всем приложениям.


        1. msts2017
          03.07.2017 10:22

          Ну так-то весь смысл ОС Фантом в обеспечении жизненного цикла объектов, более того как только в программе появляется код ожидающий от входящих данных какой-то структуры, так сразу начинается «объектно-ориентированная модель»


    1. ufm
      30.06.2017 18:09

      В Расте уже можно сделать реализацию двусвязного списка не используя unsafe?


      1. ababo
        30.06.2017 18:13

        Не имеет значения. Unsafe-модули должны обладать доверенным сертификатом для того, чтобы быть быть запущенными. Так что или библиотека от доверенного источника или наличие собственных прав на unsafe.


        1. ufm
          30.06.2017 18:22

          т.е. есть выбор.
          1. Система, в которой физически нельзя сделать unsafe, потому что такой возможности нет и он не нужен.
          2. Система, в которой я должен доверять коду, если он использует unsafe (а он вынужден использовать — одина из базовых структур требует).

          Что-ж выбрать-та. Прям не знаю.


      1. splav_asv
        30.06.2017 20:56

        А почему, собственно, нельзя? Weak же есть.


        1. ufm
          30.06.2017 23:01

          Прям с самого начала файла:

          pub fn new() -> Weak<T> {
                  unsafe {
                      Weak {
          

          Ну т.е. unsafe обёрнуто в обёртку.
          Еще раз, разница между «есть код кторому нужно верить» и «код не может сделать ничего противоестественного» — очень ощутимая.


          1. splav_asv
            30.06.2017 23:15
            +1

            Ну, так можно и дойти до недоверия ядру. Но да, аргумент в принципе засчитан.
            Хотя с такой позиции сомнительно, что можно вообще гарантировать безопасность доступа к памяти в общем случае. Для VM встаёт вопрос доверия к тому же GC — чем он лучше stdlib в плане доверия? stdlib rust можно включить прямо в дистр(сделав предварительно нормальную динамическую линковку).


  1. Revertis
    30.06.2017 14:35
    +1

    Полностью согласен с ababo, но хотел бы еще спросить: а не думали сделать отдельные сборщики на каждый процесс свой, плюс один системный, который будет чистить закончившиеся процессы?
    ИМХО, так было бы проще, чем городить «всемирный» сборщик. При этом, «процессный» сборщик и приостанавливать процесс может (отдельно от системы) и что угодно другое творить. Как в обычной Яве и других JVM.


    1. dzavalishin
      30.06.2017 15:56
      +1

      Виртуальная память глобальна.


    1. dzavalishin
      30.06.2017 16:00

      Ну и потом — это уже есть, в чём смысл повторять?


  1. qw1
    30.06.2017 22:31
    +1

    В настоящее время он реализован на принципе подсчёта числа ссылок на объект
    Это же ужас-ужас. Каждая передача указателя куда-либо увеличивает/уменьшает счётчик ссылок, что приводит к засорению L1 кешей процессоров. Надеюсь, этот алгоритм управления памятью не навязывается системой?

    А вообще, присоединяюсь к вышесказанному. Зачем сборщик? Если мы не доверяем процессам, они могут наделать утечек памяти и без потерянных ссылок.

    Лучше ввести квотирование. Задача в некотором контексте не может выделить больше квоты, а при убиении задачи нужно уметь вернуть системе всё, что это задаче было выделено.


  1. 23derevo
    01.07.2017 21:37

    Дима, а где можно почитать о том, как в Фантоме сделаны атомарные снапшоты?


  1. elw00d
    03.07.2017 15:50
    +2

    А что предполагается делать с приложениями, у которых просто течёт память? Согласно персистентной модели, приложение запускается один раз и на всю жизнь. Пусть это какой-нибудь Office, в нём пользователь создаёт какие-то документы. Этими документами он обменивается с другими приложениями/устройствами. Но вот память течёт и течёт. Можно ли будет остановить это и не потерять созданные документы? Как это будет выглядеть с точки зрения пользователя?


    1. bormotov
      05.07.2017 14:13
      +1

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


      Тут нужно обратить внимание, что никто не запрещает приложению завершать работу.
      «Вся жизнь» — то время, которое необходимо приложению, что бы выполнить полезную для пользователя работу.


      1. qw1
        05.07.2017 16:12

        Тут нет деления на диски и RAM. Если не хватает RAM, страницы вытесняются на диск, но в адресном пространстве за приложением остаются зарезервированные адреса.

        Остановка приложения — это Uninstall.


        1. bormotov
          05.07.2017 20:07

          Остановка приложения — это Uninstall.

          Это как-то очень странно звучит.

          Приведу простой пример, который (надеюсь) будет понятен всем:
          Приложение, написанное на Java/C#/несуть. Допустим, какая-то обработка данных, которые откуда-то поступают, их как-то обрабатывают, результат обработки — куда-то складывают. Для обработки очередной порции данных, запускают 1-2, да хоть 100500 нитей, из которых в каждый момент времени может получаться довольно замысловатый граф обработки. Но по мере завершения работы, нити останавливают.

          До этого места всё понятно?

          Теперь, представьте, вот это гипотетическое приложение — операционка. А одна гипотетическая нить в нем — приложение пользователя. Пользователю прислали документ, он его открыл, что-то в нём исправил сохранил и окно закрыл.

          Значит ли, что пользователь сделал Uninstall того куска кода, который отрисовывает окно, документ в нём, обрабатывает события итд? Очевидно — нет. Как был в бинарнике приложения кусок кода — так он там и остался. Должен ли быть «запущен» тот кусок кода или нужно «останавливать нить»?
          Не знаю, но не вижу никаких проблем поступать хоть так, хоть эдак.


          1. ufm
            06.07.2017 17:55

            Зависит от того, кому (какому приложению) принадлежит ссылка на объект «документ пользователя». Если текстовый редактор умеет передавать владение объектом, то с одной стороны решается проблема освобождения всей памяти занятой текстовым редактором, с другой — возвращаются файлы, от которых, в том числе, хотели избавиться.
            Если развернуть ситуацию — документ становится самодостаточным объектом, и это уже он сам решает кому на себя ссылку давать, то исчезают файлы, но возвращается проблема — что делать с объектом, который «текёт»? Потому что «перезапустить» документ нельзя. Можно только убить.


            1. bormotov
              06.07.2017 20:02

              «файл» — в данном случае, суть «документ», и конечно, есть же сейчас понятие data class?

              Не понимаю, как экземпляр data class может чо-то там сам решать, ну разве что только если у него будут в комплекте какие-то ACL, которые что-то там ограничат. В любом случае — он эе откуда-то взялся, его кто-то создал, и ссылку на него куда-то передал. Или вместе с понятием «владение» или просто, расрашил (полайкал, ретвитнул :)

              И вот этот data class — он сам по себе протекать не может. В него кто-то может че-то напихивать — да. Но ровно так-же, кто-то другой, должен уметь проверить, че там напихали, и лишние удалить.

              А код, который со всем этим работает, по-любому нужно уметь останавливать, хотя бы для того, что бы обновить на новую-свежую версию, которая «уже не протекает»


              1. qw1
                06.07.2017 22:31

                Потребуется вводить понятие «data class» на уровне ОС.
                Иначе кто мешает положить где-то в глубине объекта ссылку на делегат или на класс с виртуальными методами, которые имеют ссылки на код.

                Но тут даже дело не в ссылках на код. Концепция Фантом предполагает отсутствие сериализации. А это значит, если программист где-то запутался в ссылках внутри чистого data object, он это сразу не видит. Если бы документ сохранялся в файл, было бы подозрительно, что страница текста весит 200 МБ. Но если любая программа получает ссылку на «корень» объектной модели документа, никто не задумывается, сколько он физически занимает в памяти (да там может половина объёма — общая с ОС (ссылки на стандартные ассеты) и другими документами, поэтому традиционные способы подсчёта размера ошибочны).


                1. bormotov
                  06.07.2017 23:36

                  Возьмем, например, String. Это data class? Он может протекать?
                  Возьмем, например, какой-нибудь BinaryTree. Это будет data class? Оно сможет протекать? Там будут виртуальные методы? Или, с другой стороны — нужно ли требовать где-то у таких классов отсутствие виртуальных методов?

                  «Нет сериализации».
                  Те же самые String и BinaryTree — их совсем нельзя будет сериализовать? Кто-то запретит мне из них сделать json или xml? Так и представляю, приходит dz и ufm, и грозят пальцем «ай-ай-ай, не сериализуй! Грешно! Будешь всю жизнь в SOAP долбиться!»

                  Что сейчас происходит, если программист забывает какую-то ссылку внутри?
                  Скорее всего в случае Фантома будет ровно то же самое. И просто «добавить персистентность» и «избавить от сериализации» на уровне системы, не сделает автоматически всех программистов умными.

                  Как и «добавь вывод типов» — тоже. Как и «добавь зависимые типы» — тоже не сделает.
                  Инструменты со строгой типизацией помогают человеку меньше ошибаться.
                  Вывод типов компилятором — помогают меньше писать руками.
                  Зависимые типы — позволяют исключить еще целый класс ошибок, и еще меньше писать руками. Чем больше вы добавляете мозгов/математики инструменту на уровне концепции, чем больше инструмент может проверить, по сути сужая область в которой программист может нафигачить — тем меньше ошибок.

                  Но персистентность — это ведь ортогональная штука ко всему этому. Отсутствие сериализации — это просто выкидывание целого класса тупой работы. Это уменьшает количество ошибок, но не концептуально, а рутинно — меньше работы — меньше ошибок.
                  Да и потом, давно уже всю сериализацию генерируют, не?
                  javaxb, protobuf, и вот это всё.
                  Но если у меня есть «документ», как суть, как понятие, он же как-то будет в системе представлен? Будет это data class, или будет это вполне себе smart class — у которого будет внутри какого-то метода while true, но не такой явный, который моймает компилятор, а заумный?

                  Если человек хочет прострелить себе ногу — он её прострелит.
                  Могу только повторить тот пример, с которого начал — представьте сейчас любую программу на managed платформе, у которой внутри запускаются отдельные потоки. Вот это примерно то же самое. Просто у современных программ есть какие-то отдельные концепции типа «файл», «база данных», «сеть», а Фантому эти концепции ненужны. И просто не нужно будет мне из какого-то BinaryTree, делать BinaryTreeJson, но не потому, что dz меня предаст анафеме, а просто с точки зрения здравого смысла — зачем заниматьая какой-то фигнёй, если можно прям сам объект отдать, и там всё правильно поймут?


                  1. qw1
                    07.07.2017 12:42

                    Возьмем, например, String. Это data class? Он может протекать?
                    Возьмем, например, какой-нибудь BinaryTree. Это будет data class? Оно сможет протекать?
                    А что насчёт List«object»? В глубинах какого-нибудь ExcelDocument? Когда программист случайно добавил не объект из документа, а какую-то ненужную временную структуру? Или, типичная ошибка — подписал объект «документ» на получение событий «кнопка нажата», тем самым завязав документ на класс окна, который его редактирует (окно не будет освобождено, пока жив документ).

                    При сериализации в обычный файл это сразу всплывает — нарушение формата (а при ручной сериализации вспомогательные поля документа, которые не должны попадать в файл, тупо не будут записаны).

                    Что сейчас происходит, если программист забывает какую-то ссылку внутри?
                    Скорее всего в случае Фантома будет ровно то же самое.
                    Сейчас утечка памяти живёт до закрытия приложения. Если файловой системы нет, утечки не на виду.
                    Если человек хочет прострелить себе ногу — он её прострелит.
                    Разумеется. Утечки будут всегда (любой новичок в программировании напишет с ошибками). Вопрос был в том, не является ли архитектура Фантом особенно к ним уязвимой.

                    Вот простые сценарии. Программист кладёт undo-операции в документ, но ограничение на длину undo реализовано с ошибкой. Как ловить? Или любой документ, когда-либо открытый редактором, попадает в recent-list, который нигде в интерфейсе не виден. Открываешь файл, редактируешь, нажимаешь «удалить», файл отовсюду из интерфейса пропадает, но память не освободилась.


                    1. bormotov
                      07.07.2017 12:50

                      Сейчас утечка памяти живёт до закрытия приложения. Если файловой системы нет, утечки не на виду.

                      Если утечка не на виду, может это и не утечка вовсе?

                      Вопрос был в том, не является ли архитектура Фантом особенно к ним уязвимой.

                      Не думаю, что есть какая-то заметная разница, в сравнении с обычным приложением, если не рассматривать связи этого приложения и его окружения.

                      Хотя, за счет более стройной архитектуры в целом, может оказаться, что инструментарий для мониторинга/анализа и может быть даже автоматических каких-то реакций в таких ситуациях, будет проще реализовать.


                      1. qw1
                        07.07.2017 17:25

                        Если утечка не на виду, может это и не утечка вовсе?
                        Миллион раз повторил действия — память кончилась, но для юзера занятая сейчас память не приносит пользы — типичная утечка.

                        Не думаю, что есть какая-то заметная разница, в сравнении с обычным приложением
                        Разница в том, что при закрытии приложение освобождает утекшие ресурсы, либо переносит их в файлы, что очень заметно. В фантоме нет файлов, утечки не видны.


                        1. bormotov
                          07.07.2017 18:43

                          Разница в том, что при закрытии приложение освобождает утекшие ресурсы


                          прошу еще раз посмотреть на аналогию. В случае фантома «закрытие приложения» — это суть «завершилась нить» в тех приложениях которые сейчас.

                          Куда переносятся какие ресурсы из нити? Вот будет всё то же самое.

                          Сейчас, например у jre память можно жестко ограничить при запуске, и процесс внутри, будет получать отлупы.
                          Всё ровно то же будет происходить в Фантоме, никакой качественной разницы не будет.
                          То, что условно памяти станет несколько ТБ, я заметной разницей не считаю ;)

                          Попробую совсем тезисов: архитектура фантома не дает каких-то там заметных преимуществ в автоматическом управлении ресурсами. Наличие мозга у программиста всё еще требуется, аккуратность работы — всё еще важна важна. В то же время, каких-то заметных недостатков тоже нет — если программист с мозгом и аккуратен, на него не свалятся какие-то внезапные проблемы.


                          1. qw1
                            07.07.2017 22:41

                            Куда переносятся какие ресурсы из нити? Вот будет всё то же самое.
                            В вашем гипотетическом примере такая же текучая архитектура с нитями, когда все нити живут в одном процессе.

                            Остановка нити возвращает ОС только её стек, а всё остальное — хендлы, выделенная память и т.п. может течь.

                            В современном юзерспейсе принято запускать процессы. Когда завершается процесс, его ресурсы легко вернуть ОС.
                            Сейчас, например у jre память можно жестко ограничить при запуске
                            Ввести квотирование было моим первым предложением, но чуть подумав, я понял, что не всё там просто. Если приложение А выделяет память для документа и передаёт документ приложению Б, в чью квоту оно теперь переходит, непонятно. Квотирование препятствует свободному расшариванию данных.


                            1. bormotov
                              07.07.2017 23:12

                              этот мой «гипотетический пример» — он везде вокруг. По крайней мере, пока Фантом не начал массовый захват мира ;)

                              Но вы опять по какую-то ОС — забудьте. В случае Фантома, если довольно грубо, то ОС — это современная программа. Программы Фантома — нити внутри современной программы. Нить возвращает программе стек? Остальные все ресурсы во владении самой программы? Вот так и будет в Фантоме. Нет никакого ни юзер- ни другого спейса. Прослойка VM и сразу железо.

                              Если вы сейчас в программе запускаете нить, что бы она сделала для вас полезную работу, нить в ходе этого нажрала гигабайты памяти, что происходит?
                              Либо, ссылка на эти гигабайты осталась в стеке, после освобождения стека протухла, и гигабайты подберет сборщик мусора, или нить отдала «полезное нажратое» вам в качестве результата — «на, слушай, сам выкинь, да!» (анекдот про бумеранг)


                              1. qw1
                                08.07.2017 02:32

                                этот мой «гипотетический пример» — он везде вокруг.
                                Именно так. Только программы периодически перезапускают. Тот же Firefox я перезапускаю раз в 2-4 недели, без этого он начинает тормозить.

                                Отсюда и вопрос, который задан выше: если Фантом захватит мир, как бороться с утечками при невозможности рестарта процесса.


                                1. bormotov
                                  08.07.2017 14:29

                                  Только программы периодически перезапускают.


                                  при невозможности рестарта процесса


                                  Любой объект можно создать когда нужно, а когда не нужно — убить.
                                  С чего вдруг какие-то процессы в Фантоме станут неубиваемые или неперезапускаемые? Кто запретит? Таки лично dz будет приходить и говорить, что грешно процессы перезапускать?

                                  Особенно странно звучит эта гипотеза в рамках концепции платформы, которая принципиально 100% managed.


                                  1. qw1
                                    08.07.2017 15:21

                                    Автор на эту тему прямо не писал, но я понял, что процессов в традиционном понимании в этой ОС нет, а вы (возможно) считаете, что они есть.


                                    1. bormotov
                                      08.07.2017 16:51

                                      На эту тему автор пишет уже больше десяти (двадцати?) лет, в разных местах.

                                      процессов в традиционном понимании в этой ОС нет


                                      Вот у вас сейчас, в классической ОС, программа — это такой файл на диске. Она «остановлена»? Что случается, если вы её «запускаете»? Просто подумайте. А теперь представьте, что диска как такового нет — всё некая память, которая не пропадает сама по себе. Как изменится ваш ответ на тему «запуск программы»?

                                      Разница между «запущенной программой» и «незапущенной», только в простом факте: «запущенная» программа получает некие события «извне», и может сама какие-то события генерировать, и куда-то там отправлять. Например запросить кусок памяти, обратиться по сети к какой-то другой программе.

                                      Что означает для программы «завершила работу»? Перестала обрабатывать события извне. По своей внутренней воле. И перестала сама генерировать события. Последнее событие, которое она сгененрировала — сообщила системе — system.exit(0)


                                      1. qw1
                                        08.07.2017 16:59

                                        На эту тему автор пишет уже больше десяти (двадцати?) лет, в разных местах.
                                        Если вы следите за темой, дайте ссылку на наиболее полное описание концепции.
                                        Разница между «запущенной программой» и «незапущенной», только в простом факте: «запущенная» программа получает некие события «извне», и может сама какие-то события генерировать, и куда-то там отправлять
                                        Запущенная программа имеет процесс, который владеет ресурсами ОС.
                                        Что означает для программы «завершила работу»? Перестала обрабатывать события извне. По своей внутренней воле.
                                        А также освободила ресурсы, которыми владел процесс.


                                        1. bormotov
                                          08.07.2017 17:46

                                          я сходу не знаю какой-то одной ссылки, если dz знает — может сам выдаст.

                                          Запущенная программа имеет процесс, который владеет ресурсами ОС.


                                          «Процесс» — это такая запись в таблице процессов? А этот самый PID — это номер этой записи? Есть еще что-то важное? Область памяти в которую загружен код, какие-то стандартные дескрипторы stdin, stdout — это всё не важно. Это по сути тоже какие-то данные в той же таблице, которые можно узнать, зная PID.

                                          Что значит «владеет ресурсами»? Вот, в программе вызывают malloc(), что дальше происходит? Понятно, что основной результат — адрес блока памяти. Но что значит «владеет ресурсами ОС»?

                                          Если ОС память выдает, то в той самой «таблице процессов» находит PID откуда пришел malloc(), и записывает «выдано памяти, по такому-то адресу, такого-то размера». То есть, «процесс владеет ресурсами ОС» — это тоже тупо запись в каких-то структурах данных внутри ОС, связанных с этим PID?

                                          Что значит «освободила ресурсы»? Отправила программа в ОС «сообщение»" free()? Никто не заставляет программу освобождать память, перед тем как сказать system.exit()? Что в этом случае происходит? ОС смотрит в те самые таблицы-итд, находит там PID, находит все ресурсы с ним связанные, выполняет какие-то манипуляции с этими дескрипторами, размерами и так далее. Так?

                                          Что вообще такое «освобождение памяти»? Это отметка, что начиная с такого-то адреса, такой размер блока — свободен. Отметка, опять таки, в какой-то структуре внутри ОС. Как это сейчас принято делать, списки? Деревья?

                                          То есть, всё вот это — это просто изменение данных в системных структурах. Больше никаких физических действий не происходит.

                                          Что изменятся если у нас диск от ОЗУ неотличим? Правильно — ни-че-го.
                                          И это «неотличимо» работает давно, называется «виртуальная память». Если верить википедии, понятие «Вритуальная память» существует с 1956 года. На практике используется с 1969.

                                          То есть, какова бы ни была среда выполнения, ОС (эта самая среда),

                                          — запускает процессы
                                          — выделяет им ресурсы — malloc
                                          — умет освобождать конкретные ресурсы по просьбе процесса — free
                                          — умеет освобождать все ресурсы, по завершению процесса — я думаю, что сама запускает free по всему списку

                                          И мы только что разобрались, что всё это, просто суть данные в каких-то таблицах/списках/деревьяъ/структурах, как там оно реализовано.

                                          Что такое «протекаюзщая программа»? Программа, которая не вызвает free. Но с точки зрения ОС, это означает, что где-то там в её структурах, есть данные про эти куски памяти. И как только программа говорит system.exit() — ОС знает, все эти ресурсы свободны, и сама вызывает free.

                                          Кто или что помешает в ОС Фантом реализовать system.exit()?


                                          1. qw1
                                            08.07.2017 19:28

                                            Разница в том, что обычные ОС делают копию данных, передавая их во владению процессу (да, та самая сериализация/десериализация в файлы).

                                            Вы предлагаете каждому ресурсу добавить метку — PID владельца.
                                            Если от сериализации отказываться, то как будет выглядеть открытие документа?

                                            Вот есть документ в памяти, сейчас его владелец — ОС. Документ передали на редактирование какому-то приложению, что произойдёт?

                                            Процесс приложения при редактировании выделил у менеджера памяти пару новых блоков и добавил их в объект документа. Кто владелец новых блоков? Что с ними произойдёт при убиении процесса?


                                            1. bormotov
                                              09.07.2017 12:34

                                              как это копирование или некопирование данных влияет?

                                              Вы предлагаете каждому ресурсу добавить метку — PID владельца.

                                              это не я предлагаю. Вот, у меня windows вокруг, там есть такая утилита ProcessExplorer, которая залезает во внуренности системы, и показывает очень много чего. Мне, на самом деле, не важно технически, как именно это сделано, потому, что совершенно очевидно, всё это начинается со списка процессов, и далее там как-то к этим процессам подвязано.

                                              Вот есть документ в памяти, сейчас его владелец — ОС. Документ передали на редактирование какому-то приложению, что произойдёт?


                                              Отличный вопрос. Вот у вас есть в программе, в памяти объект. Вы вызываете некую функцию обработки, и передаете этой функции ссылку на этот объект. Что происходит?

                                              Процесс приложения при редактировании выделил у менеджера памяти пару новых блоков и добавил их в объект документа. Кто владелец новых блоков? Что с ними произойдёт при убиении процесса?


                                              Вот та функция внутри программы внутри jvm/.Net/итд (которой дали ссылку на объект) запросил еще памяти, ему выдали, что происходит? Кто владелец новых блоков?

                                              Могу сходу ответить на «что происходит после завершения функции» — если эти ссылки из этой функции никому больше не попали (функция не отдала эту ссылку в результатах своей работы), у них будет ref count = 0, и эти блоки освободит сборщик мусора.


                                              1. qw1
                                                09.07.2017 13:06

                                                как это копирование или некопирование данных влияет?
                                                Это самое важное, водораздел между обычными ОС и Фантомом — нужны ли копирование/сериализация или не нужны.
                                                Вот у вас есть в программе, в памяти объект. Вы вызываете некую функцию обработки, и передаете этой функции ссылку на этот объект. Что происходит?
                                                С точки зрения владения — ничего не происходит.
                                                запросил еще памяти, ему выдали, что происходит? Кто владелец новых блоков?
                                                Ресурсы, занимаемые объектом до и после вызова функции принадлежат процессу и будут освобождены при завершении процесса, с потерей несохранённых данных.
                                                это не я предлагаю. Вот, у меня windows вокруг, там есть такая утилита ProcessExplorer, которая залезает во внуренности системы, и показывает очень много чего.
                                                В системе Фантом вы не видите никаких серьёзных проблем со ссылками, поэтому я предполагаю, что у вас в голове есть какая-то модель её функционирования.

                                                Поэтому и возникают вопросы:

                                                1) Как по-вашему, каждый выделенный блок памяти имеет владельца? (процесс или что-то ещё). Windows с его ProcessExplorer не интересует, интересно мнение, каким вы видите Фантом.
                                                2) Если ответ на 1) — «да», то при завершении процесса блоки памяти, принадлежащие ему, освобождаются?
                                                3) Тот же вопрос, но если на них есть ссылки из блоков, принадлежащих другим процессам? (в традиционных ОС эта ситуация невозможна, т.к. нельзя сделать ссылку из объекта одного процесса на объект, который находится в адресном пространстве другого процесса).

                                                Могу сходу ответить на «что происходит после завершения функции» — если эти ссылки из этой функции никому больше не попали (функция не отдала эту ссылку в результатах своей работы), у них будет ref count = 0, и эти блоки освободит сборщик мусора.
                                                Я попробую это осмыслить, когда вы поясните по предыдущим пунктам. Пока у меня получается противоречивая картинка.


                                                1. bormotov
                                                  09.07.2017 14:22

                                                  В системе Фантом вы не видите никаких серьёзных проблем со ссылками, поэтому я предполагаю, что у вас в голове есть какая-то модель её функционирования.


                                                  повторюсь: ОС Фантом на одном физическом хосте — это (грубо) одна копия jvm/.net или любой другой VM. А разные программы в ОС Фантом — это суть разные нити внутри этой самой jvm. Есть сейчас какие-то вопросы по управлению ресурсами внутри одной копии jvm? Примерно такой-же набор вопросов в ОС Фантом.

                                                  Ключевое отличие от текущей ситуации с jvm:
                                                  — если вы однажды сказали new SomeClass — вам не нужно предпринимать какие-то дополнитлеьные усилия по его сохранению/восстановлению между перезапусками самой системы — это делает ОС сама.
                                                  — память-диск-сеть — для вашего процесса представляется единым API, который суть просто передать ссылку на объект.

                                                  Само по себе persistance никакой не прорыв — есть в каком-то виде наверное вообще для всего.
                                                  Сетевая прозрачность тоже давно известная штука, у jvm есть RMI, например. У того же erlang сетевая прозрачность вообще в базовой концепции. Наверняка можно найти еще примеров успешных промышленных реализаций в этом направлении для других популярных инструментов.

                                                  Разница только в том, что в Фантом дадут всё это сразу, в удобном виде, как базовое API.

                                                  Навскидку, если сейчас на jvm взять комплект akka + akka-cluster + akka-persistance, то можно с помощью напильника и какой-то матери получить примерно такую же среду выполнения как планируется в Фантоме. Список современных инструментов, у которых разное проникновение в промышленность, и для которых в виде библиотек реалихзована персистентность и сетевая прозрачность можно продолжать.

                                                  ответы

                                                  1. конечно, у каждого ресурса есть владелец
                                                  2. при завершении процесса, сборщик мусора освободит все ресурсы, которыми владел процесс
                                                  3. Очевидно, что если владелец передал ссылку на ресурс куда-то наружу, но не передал (разделил?) право владения, то возникает вопрос выбора — что с этим ресурсом делать.

                                                  Сборку мусора по счетчику ссылок, в терминах владения можно сформулировать так: если объект получил у «системы» ресурс, и передал ссылку на него в другой объект — то в этот момент он разделил право владения ресурса с тем другим объектом. У ресурса число ссылок — суть число владельцев.

                                                  Посмотрите на Rust, из того, что сейчас на слуху, это самое интересное место, где люди пытаются вот эти вопросы владения/передачи как-то красиво формализовать, что бы обеспечить автоматическое управление ресурсами.


                                                  1. qw1
                                                    09.07.2017 19:30

                                                    ОС Фантом на одном физическом хосте — это (грубо) одна копия jvm/.net или любой другой VM. А разные программы в ОС Фантом — это суть разные нити внутри этой самой jvm
                                                    Процессы как объекты ОС тут рудиментарны. Если хочется логически сгруппировать нити в одну сущность и дать им возможность обмениваться данными, то достаточно создать объект в памяти класса Process, в котором будут ссылки на данные, и ссылку на этот объект передать в стеки всех нитей.

                                                    Если ОС решает убить процесс, она останавливает нити и обнуляет свою ссылку на Process. Но при этом, если процесс включил ссылку на объект Process в какой-то документ, и отдал этот документ другому процессу, ресурсы первого процесса освободить нельзя.
                                                    Есть сейчас какие-то вопросы по управлению ресурсами внутри одной копии jvm?
                                                    Конечно, есть. Это серверные приложения вылизаны донельзя и не текут (почти). Но и их бывает надо перезапускать. При этом состояние надёжно сохранено в файлах, и перезапуск не приводит к потере данных.

                                                    Если в одну песочницу jvm пускать и системные задачи, и браузеры, и игры, и приложения Васи Пупкина, долго эта vm не проживёт.

                                                    конечно, у каждого ресурса есть владелец
                                                    если объект получил у «системы» ресурс, и передал ссылку на него в другой объект — то в этот момент он разделил право владения ресурса с тем другим объектом
                                                    В такой формулировке явная метка владельца не нужна (если единственный владелец убит, ресурс будет освобождён сборщиком, а если владельцев много, их список не поможет ОС в управлении ресурсом). Концептуально можно считать, что список владельцев есть, фактически вести его не надо.

                                                    То есть, убить процесс (гарантированно освободить его ресурсы) нельзя, т.к. он может [ошибочно] пошарить чуть ли не все свои данные с другим процессом.


          1. qw1
            06.07.2017 22:47

            Значит ли, что пользователь сделал Uninstall того куска кода, который отрисовывает окно, документ в нём, обрабатывает события итд? Очевидно — нет. Как был в бинарнике приложения кусок кода — так он там и остался.
            Тут я имею ввиду, что если нет файлов, где-то в памяти есть список
            List<Application> Apps;
            и способ перехода к приложению Apps[i].Show(). Если внутри приложения что-то потекло, остаётся один выход — Apps.RemoveAt(i);

            Теперь, представьте, вот это гипотетическое приложение — операционка. А одна гипотетическая нить в нем — приложение пользователя. Пользователю прислали документ, он его открыл, что-то в нём исправил сохранил и окно закрыл
            Понятно, что убиение нити освобождает ресурсы, выделенные на её стеке. Но у приложения должен быть и какой-то более глобальный контекст, чтобы нити могли общаться и сохранять общие данные. Вот там-то и можно устроить склад утечек.