Поэтому пост крайне краткий.

Развивал наш централизованный обработчик ошибок, думал, что делать с сабж, ведь при нехватке памяти поведение программы становится не предсказуемым. И родилась ИДЕЯ. Думаю, я не первый, но раньше такого решения не встречал. И так...

Что делать? Продолжать обработку ошибок, формирование протокола и т.д. - не вариант, это все может не отработать, даже так: почти наверняка не отработает. Хочется вернуть программу в рабочее состояние. А что, если высвободить не нужную память? Только где ее взять?

- Чтобы продать что-то не нужное, нужно сначала купить что-то не нужное.

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

Хорошо бы еще, в какой-то момент, заново разместить аварийный блок, но это уже сложно формализуемо.

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


  1. rpc1
    18.01.2022 19:26
    +3

    Лучше наверное выявить утечки памяти и не доводить до OutOfMemoryException. Не знаю про какой язык программирования идет речь, но если про Java можно посмотреть в сторону SoftReference

    These rules vary from one JVM implementation to another, but the documentation states that all soft references to softly-reachable objects are guaranteed to be cleared before a JVM throws an OutOfMemoryError.


    1. ap1973 Автор
      18.01.2022 19:32

      Тут не про Java, разумеется про языки с ручным управлением памятью. Там где сборка мусора, или совсем динамика, или VM конечно другие механизмы. С другой стороны, раз в Java есть OutOfMemoryError, тоже может сработать. Мне понравилась простота решения, иногда хотя бы сохранить протокол ошибки, со Stack Trace и прочим - крайне важно. Потом пусть падает.


    1. horror_x
      18.01.2022 23:43

      Лучше наверное выявить утечки памяти
      Иногда проблема не в утечке, а в том, что памяти действительно не хватает. В таком случае обычно полезно отправить отчёт или сохранить дамп, по которому можно понять, что стоит оптимизировать.

      Хотя сама практика с резервированием памяти для этих целей довольно очевидная и распространённая.


  1. ClearAirTurbulence
    18.01.2022 19:30
    +2

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


    1. ap1973 Автор
      18.01.2022 19:36
      -11

      Маловато... - даже не знаю, я же не обзор нового айфона пишу.

      Реализация тривиальна на любом ЯП. Что тут тестировать?

      Где тут вопрос? Просто поделился идеей, простой и полезной, как мне кажется.


  1. MKMatriX
    18.01.2022 19:35
    +7

    Где-то тут была подобная история. Еще про приставки. Один джун перед релизом нашел баг, исправить смог, но оперативки не хватало. Пошел к лиду, говорит так и так, вот никак не хватает. Лид посмотрел, спросил все ли тот попробовал, и удовлетворившись ответом удалил маллок на пару килобайт в самом начале инициализации. Джун офигевши посмотрел, а лид и говорит, всегда не хватает под конец, поэтому в начале немного мусора закладываю.


    Это актуально если мы говорим про языки с управлением памяти. А у вас, лишь пустая трата ресурсов на выделение и освобождение. Полезно при отладке, однако бесполезно в целом. Лучше писать без утечек, а еще даже операционка берет на себя скидывание оперативки на винт. Сейчас доступен минимум гигабайт на все телодвижения. Т.е. полноценный фильм. Выгрузить текстуры, модели, все что просто копия данных с винта. Если не хватило, то упростить форматы данных. Тот же json или если не повезло xml содержат очень много не нужного мусора.


    В целом для обработки ошибок может и приемлемо. Т.е. загрузили оперативку, оставив например на приложение только 4 гига. И проверяем, что их всегда хватает. Остальное используем только чтобы логи писать. Тогда и восстанавливать особо не надо. Предполагая, что в этих условиях все должно работать.


    1. ap1973 Автор
      18.01.2022 19:39
      +1

      Ответил выше, да, хотя бы сохранить лог.


      1. vkni
        19.01.2022 00:14

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


  1. pfemidi
    18.01.2022 19:58
    +3

    В незабываемом Turbo Vision для DOS именно так и делалось, называлось AFAIR «safety pool». Память резервировалась в этот safety pool при старте, а когда возникало «Out of memory» для какого-нибудь окна (Turbo Vision ведь был в основном текстовый UI, хотя и имел некоторые классы не для отображения на экране, в частности те же streams, короче говоря можно сказать что это был древний текстовый и только DOSовский предшественник Qt) этот safety pool освобождался и памяти вновь хватало.

    Так что всё новое это хорошо забытое старое :-)


    1. ap1973 Автор
      18.01.2022 20:01
      +2

      Я так и знал, что кто-то это уже придумал )


    1. ap1973 Автор
      18.01.2022 20:04
      +3

      Turbo Vision кстати клевая вещь была, но толком заюзать не успел. Перескачил уже на другое.


      1. ap1973 Автор
        18.01.2022 20:14
        -2

        Вот за что тут минус? Фанат FoxPro?


  1. fedorro
    18.01.2022 20:06
    +1

    Если память не изменяет - OOM возникает при попытке выделить блок памяти, т.е. есть 100 Мб, запрашиваем 101 Мб, получаем OOM, но и 100 Мб свободной памяти остается. Чтобы не хватило памяти на запись лога - это надо очень маленькие куски запрашивать - это уже оверхед по памяти и процессору, и требует пересмотра в реализации приложения. К тому же вдруг операция смогла бы выполнится успешно если бы у неё была та память, что в Вашем методе зарезервирована.


    1. ap1973 Автор
      18.01.2022 20:13

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


      1. fedorro
        18.01.2022 20:20

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


        1. ap1973 Автор
          18.01.2022 20:23
          +1

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


        1. ap1973 Автор
          18.01.2022 20:27

          Другой процесс тут конечно не причем. А у потоков память общая.


          1. fedorro
            18.01.2022 20:31

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


            1. ap1973 Автор
              18.01.2022 20:36

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

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


              1. fedorro
                18.01.2022 20:39

                Процесс не может захватить память другого процесса,

                Мы же про свободную память говорим - она не принадлежит процессу ... Или по вашему подержал\освободил - это как облизнул\пометил, моё? ????


                1. ap1973 Автор
                  18.01.2022 20:45

                  Вы серьезно думаете, что когда процесс выполняет Free, память сразу уходит ОС?


    1. vkni
      19.01.2022 00:21

      Там два уровня на Unix: OS (sbrk) и куча стандартной библиотеки - malloc. Просто запуск syscall'а на каждое выделение в куче - слишком накладно.

      Ещё под Linux есть очень неприятная штука "OOM killer".


      1. Alex_Tver
        19.01.2022 13:17

        Да уж.
        На Линуксах в общем случае на такой экзепшн надеяться нельзя. Если запрашиваешь памяти больше, чем доступно в системе, система просто прибивает твой процесс.


        1. edo1h
          19.01.2022 13:41

          ну так оно же настраивается


          1. Alex_Tver
            19.01.2022 20:38

            Ага.
            Только для тюнинга OOM нужны права суперпользователя.
            Которые на правильно настроенной машине у клиента, а не на личной или машине разработчика, обычно недоступны.


            1. ap1973 Автор
              19.01.2022 20:45

              Не про тему, а так, для интереса: В Linux совсем плохо с настройкой политик? Т.е. в условно стандартном Linux, функционирующем в предприятии, есть возможность централизованно раздавать настройки?


              1. gecube
                19.01.2022 21:09

                нет, нельзя, как раз именно поэтому и родился целый класс систем типа SCM - Salt, Chef, Puppet.... отчасти ансибл.


        1. gecube
          19.01.2022 14:12
          +1

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

          нет, ниже ссылку приводил. Это работает так - ты запрашиваешь систему, система радостно тебе ее типа выделяет, но в момент, когда ты реально начинаешь эту память использовать - приходит оомкиллер и убивает какой-то рандомный процесс в системе (не обязательно твой, а может ssh или любой другой). Но @edo1h прав, что это поведение немножко настраивается


          1. edo1h
            19.01.2022 16:41

            почему немножко? overcommit можно же отключить вовсе, тогда может быть обратная ситуация (память ещё в системе есть, а malloc выдаёт ошибку, которой посвящена эта статья)


            1. DistortNeo
              19.01.2022 17:18

              Да, можно отключить. Но многие приложения на него рассчитывают.


  1. niyaho8778
    18.01.2022 20:08
    +3

    вот гениальная картинка к посту .... 30гб физической памяти не зватает :D


    1. ColdPhoenix
      18.01.2022 20:21

      Файл подкачки выключен?

      Хрому он нужен как я выяснил опытным путем.

      В целом вроде работает, но иногда вот так падает, причём памяти еще много. Как вернул файл подкачки, все отлично стало.


    1. VEG
      18.01.2022 20:26
      +2

      Почти наверняка это система без файла подкачки. Эта «свободная память» зарезервирована, и хоть и не используется, система не может её просто занять чем-то другим, так как если потом приложение захочет использовать зарезервированную память, без файла подкачки система не может гарантировать, что это удастся. Даже если у вас 500 гигабайт оперативы, если вы хотите иметь возможность использовать её всю, файл подкачки должен быть.


    1. Ivanzabu
      18.01.2022 20:26

      У вас файл подкачки отключен или очень мал.

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

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

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


      1. VEG
        18.01.2022 20:44
        +1

        Но ведь эта память не занята. Она зарезервирована. Вот представьте, какое-то приложение мапит 40-гигабайтный файл на память, и читает из него 20 рандомных килобайт. Физически памяти этот 40-гигабайтный файл занял только на этих 20 рандомных килобайт, и менеджер памяти может эти страницы автоматически в любой момент дропнуть (всё равно потом можно обратно с диска прочитать), когда память понадобится каким-то другим приложениям. Но ОС зарезервирует память на все эти 40 гигабайт. Просто на случай, если программа вдруг захочет записать что-то по всем замапленным адресам, когда действительно придётся создать копии всех страниц и занять эти 40 гигабайт. А до того момента память свободна, и если есть файл подкачки на экстренный случай, эту неиспользуемую зарезервированную память ОС отдаст другим приложениям.


        1. Ivanzabu
          18.01.2022 21:00
          +5

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

          Сейчас это отображается через зад - "у вас 30гб свободной памяти но приложения крашатся потому что её нет, ой это наши внутринние алгоритмы долбятся там - вот память есть, но её нет, но в диспетчере есть, но для приложений нет, а идите в жопу поднимайте как хотите".

          Ну добавили бы отдельную графу под зарезервированную память, что мешало?

          А так - была у меня система где не могло быть файла подкачки физически (диски readonly, под весь temp микро ram-disk), и какое то приложение резервировало вагон памяти приводя к крашу всего остального, а как узнать какое? - а никак, гадай братец на кофейной гуще, в диспетчере задач этого нет, в мониторе ресурсов тоже значения с потолка, очень удобно.


          1. DistortNeo
            18.01.2022 23:35

            в диспетчере задач этого нет,

            В диспетчере задач можно включить отображение Commit Size. Я всегда это первым делом делаю.

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

            Например, у пользователя создаётся иллюзия, что на системе с меньшим количеством памяти приложения потребляют меньше памяти. Но по факту приложению плевать на количество памяти, оно выделяет столько, сколько ему нужно. А как уже система распределит страницы виртуальной памяти между физической памятью и свопом, ему без разницы. Лишь небольшое количество приложений типа СУБД работают с памятью более тонко.


        1. Siemargl
          18.01.2022 23:01
          -1

          Но ОС зарезервирует память на все эти 40 гигабайт.

          С чего бы? Нет


    1. nev3rfail
      18.01.2022 21:29

      На Windows обязательно нужен своп, резиновый или хотя бы ramsize*2, как раз по той причине, что commited память считается занятой и не отдается другим приложениям.


      1. Evengard
        18.01.2022 22:11

        А всё потому, что в отличии от Линукса, Винда, похоже, не умеет в overcommit...


        1. Siemargl
          18.01.2022 23:00
          +5

          Да, и лучше получить Нулл при выделении памяти, чем потом упасть в рандомном месте при корректному обращению по указателю.


        1. VEG
          18.01.2022 23:04

          При разработке Windows приняли более надёжное решение в данном вопросе.


        1. DistortNeo
          18.01.2022 23:32
          +6

          Зато Linux умеет, и там при нехватке памяти начинается такой цирк с конями, который Windows и не снился.


    1. DistortNeo
      18.01.2022 23:41

      Причём не всё так плохо. 29.9 ГБ из этих 30.3 ГБ используется под дисковый кэш, реально не используется только 0.4 ГБ.


  1. Bobovor
    18.01.2022 20:17

    Хочется вернуть программу в рабочее состояние. 

    Вы ракеты там делаете? Иначе лучшее что можно сделать - грохнутся погромче, с визгом и криками. Т.к. когда в следующий раз не хватит, громко уже не получится, а грохнутся придется. Или есть причины?


    1. ap1973 Автор
      18.01.2022 20:19

      Поясните, не понял.


      1. TheRikipm
        18.01.2022 20:50

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


        1. ap1973 Автор
          18.01.2022 21:24
          +2

          Так я о том же. Если у вас нехватка памяти, поведение процесса становится не предсказуемым (Undefined Behaviour - пользуясь терминологией C). Описанный механизм, как раз и позволяет вернуть память, и обработать ошибку. Т.е. выдать сообщение, сохранить протокол, если все это происходит в try finally, возможно даже освободить память занятую чем то внутри блока try и продолжить работать.


          1. Valle
            18.01.2022 21:44

            Но только один раз.


            1. ap1973 Автор
              18.01.2022 21:45
              +2

              Это уже чудесно, есть информация для разбора полетов.


          1. Evengard
            18.01.2022 22:13

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


          1. Siemargl
            18.01.2022 23:24

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

            С чего бы? Просто надо проверить и обработать ошибку выделения памяти при каждом выделении. Но всем лень.


            1. ap1973 Автор
              18.01.2022 23:42

              Зачет )


      1. YegorP
        18.01.2022 21:45
        +1

        У меня похожий вопрос возникает: какие юзкейсы? И что это за "небольшой, но достаточный эмпирический" размер? Как его выбирать? Чтобы логгеру хватило? Окей, допустим. А что-нибудь посерьёзнее (опуская разговоры про то, что логирование может быть серьёзным требованием само по себе)?

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

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

        Я сталкивался с ответственными задачами из разряда "не успеешь - взорвётся" на производстве (на самом деле лопнет и навоняет, но всё равно неприятно). Это были ПЛК, программируемые на IEC-61131. Так вот в этом языке вообще нет штатных средств динамического выделения памяти. Но это так, кул стори всего лишь. Идея интересная, но как её на практике применить было бы в десять раз интереснее. Вот неудовлетворённый читатель и минусует, наверное.


        1. ap1973 Автор
          18.01.2022 22:20

          Начну с конца, я знал что будут минусовать, да и ладно.

          Теперь по делу.

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

          2. Память могла не утечь, почему все пишут про утечку памяти? Я использую профилировщик, в том числе с анализом Memory Leak. Есть куча других возможностей накосячить: у прикладников есть возможность накосячить, запрос данных, открытие файлов разных... Память может сожрать все, что угодно. Вы же не будете на каждую строчку вешать определение необходимого размера памяти, для ее выполения. Да я и не представлю себе как это возможно в большой системе (ну ладно, в средней).

          3. Про ситуацию. Да нехватка памяти редкая ситуация, но ее все равно нужно обрабатывать, и это моя прямая обязанность.

          4. Всех пишущих без багов и точно знающих как писать программы в которых всегда все хорошо, прошу писать в личку. Возьму на работу.

          Пример к пункту 2, про ситуацию, как ее обработать:

          Есть загрузка XML файла, который всегда небольшой, используется DOM парсер. Вдруг, без обьявленя войны, он оказался в гигабайт. DOM сжирает всю память, мы падаем. Дальше варианты:

          1. Как некоторые предлагают, мы падаем с ошибкой ОС с безумным кодом, который нам не очем не говорит.

          2. Мы падаем с ошибкой, где видно что это в методе XML.Load. Запрашиваем файл, все понимаем, переделывем на SAX.

          3. Какой вариант вы выберете?


          1. AndreyDmitriev
            18.01.2022 22:32

            Вас минусуют за пост на уровне "детский сад". Все учебники пишут о том, что надо разрабатывать программы, которые сами умеют восстанавливаться после сбоя и устойчивы к таким вот проблемам. Но вот вы сами хотели бы, скажем, лететь на самолёте, ПО которого разработано так, как у вас в посте? Где-то я видел большой документ, как разрабатываются программы в НАСА - попробуйте поискать, там есть что почитать.


            1. ap1973 Автор
              19.01.2022 00:22

              Не хочу летать на самолете, где ПО перезапускается после проблемы, хочу пилота с гидроусилителем.


              1. AndreyDmitriev
                19.01.2022 10:10

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


    1. kAIST
      18.01.2022 21:49

      Как вариант, попытаться сохранить несохраненную работы, а потом уже грохнуться. Хотя не с позором грохнуться.


      1. ap1973 Автор
        18.01.2022 22:30

        В специфических ситуациях да. К сожалению, в общем случае, это мало вероятно. Я так думаю.


  1. george_vernin
    18.01.2022 20:21

    Мда. Сделано было уже лет 20-25 назад неоднократно.

    Прочитано и описано в куче книг по отладке и безопасному программированию итд.


    1. ap1973 Автор
      18.01.2022 20:26
      +4

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


      1. george_vernin
        19.01.2022 22:07

        Давно уж все это было . На ум приходит - 'Наука отладки" . Как вспомню еще напишу


  1. Cobalt
    18.01.2022 20:38
    +1

    <sarcasm>Так вот почему программы на движке хромиум жрут память как не в себя!</sarcasm>


  1. Oval
    18.01.2022 20:50
    +1

    Недалеко от Константинополя я встретил маленького, худенького человека. Несмотря на то, что к каждой его ноге было привязано по свинцовой гире в пятьдесят фунтов, он бежал очень быстро.
    Меня это очень удивило, и я спросил его:

    —  Куда ты бежишь, приятель? Почему к твоим ногам привязаны гири?—  Я бегу из Вены. Полчаса тому назад я покинул этот город, —ответил скороход. — В Вене я служил у знатных господ, но сегодня получил отставку. Сейчас направля­юсь в Константинополь поискать себе работу. Спешить мне незачем, и вот, чтобы замедлить бег, я при­вязал эти гири, вспомнив любимое изречение моего школьного учите­ля: «Тише едешь — дальше будешь».


  1. gleb_l
    18.01.2022 21:02

    Это сродни рекомендации для автолюбителей времен СССР возить с собой полную канистру. Действительно, есть ряд случаев, когда канистра спасает. Правда, никто не гарантирует. что а) после использования НЗ представится возможность его пополнить и б) запаса хватит на любой экстренный случай.

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

    Для "больших" же современных систем OOM означает грубые ошибки в организации работы с памятью, и кроме того, из-за виртуальной ее природы чаще всего более высокоприоритетное окружение упавшего процесса обладает достаточными ресурсами для того, чтобы оставить PMD и таким образом, дать ключ к последующему анализу причин ООМ.


  1. gecube
    18.01.2022 21:13

    Почитайте хотя бы https://habr.com/ru/company/oleg-bunin/blog/431536/ и подумайте. Вот именно из-за таких решений, как Вы предлагаете, общая ситуация становится только хуже


    1. ap1973 Автор
      18.01.2022 21:31

      И причем тут это? Ну да, там тоже есть про нехватку памяти, но в совершенно, абсолютно в другом контексте.


  1. WASD1
    18.01.2022 21:13
    +2

    освобождаем буфер, и как минимум продолжаем обработку ошибки, возможно даже и восстанавливаем работоспособность программы.

    Крайне плохой совет.
    Fail fast не получили. Да и скорее всего если память кончилась - то есть системная проблема, на которую вы всё равно нарвётесь.

    Как превратить совет в хороший?
    а) Организовать запись в логи на заранее статически выделенных аренах (пулах) - называйте как вам удобно.
    б) Организовать сохранение пользовательских данных на динамически, но заранее выделяемых аренах (пулах).

    Но вариант б) по очевидным причинам сложнее.


    1. ap1973 Автор
      18.01.2022 21:39
      -1

      Во многом я с вами согласен. Я по этому и написал "Возможно", т.к. ситуации бывают разные.

      а) Все равно будет расход памяти, что не делайте. Как минимум копирвание текста Exception в лог, вот вам операция с памятью.

      б) это не всегда возможно.

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

      P.S. В очередной раз повторюсь (больше не буду - надоело), вопрос именно в предотвращении системного креша, и получении информации, которая позволит дальше разбираться с проблеммой. Все остальное опция.


      1. gecube
        18.01.2022 22:36
        +1

        . Как минимум копирвание текста Exception в лог, вот вам операция с памятью.

        необязательно. Текст exception хранится в static в программе, а работа с файлом запросто может не требовать оверхеда по памяти...

        системного креша

        define что такое системный крэш. Если это падение или тормоза самой ОС - это одно, но вот программа должна упасть или захендлить ситуацию и аварийно завершиться с минимальными потерями для данных


    1. fk0
      19.01.2022 14:37

      Я делал автомобильную сигнализацию на микроконтроллере с 4-мя килобайтами ОЗУ (4096 байт). ПЗУ отдельно, 128кБайт. Fail fast там некуда. Значит у человека машина встанет или не будет открываться, либо наоборот не будет закрываться и система не выполнит охранную функцию. При этом там были SMS-ки которые будучи принимаемыми от модема в HEX-виде (так модем выдаёт) занимают запросто половину памяти, там было голосовое меню, синтезатор речи на ~200 сообщений, работа с модемом и GPS-приёмником. И везде длинные строки которые не вмещаются в память.

      Единственной разумной и реализованной стратегией там было, что многие операции подразумевали временный сбой из-за нехватки памяти и повтор через некоторое время. Без динамической аллокации задача не решена была бы вообще (нужен попросту МК совершенно другого уровня). Память кончилась, это системная проблема, но просто выйти -- некуда и нельзя.

      Подразумевалось, что рано или поздно система приходит в некоторое начальное состояние. Что гарантирует, что не будет фрагментации памяти (ранее выделенные блоки освободятся), что размер свободной памяти в этом состоянии будет таким же как после запуска, например. Я уж не помню деталей, но была на последний факт проверка, вроде периодической попытки выделить через malloc() блоки разных размеров (N, N/2, N/4, N/8...) и оценить объём свободной памяти, и фиксация программного сбоя, если попытка в течении некоторого времени всегда завершается недостаточным объёмом памяти (значит и другие задачи не могли нормально выполняться). Но это -- не нормальная, не штатная ситуация. Она не должна возникать в идеале. Это -- спасательный круг. Чтоб не разбирать пол машины и не отключать питание. Штатно -- нехватка памяти, отказ от операции, откат предыдущих операций и повтор всего через некоторое время.

      И во многих embedded задачах "просто выйти" не выход. Стоп-кран в самолёте невозможен. Критичными, в порядке приоритета являются:

      1. выполнение каких-то критических функций прибора (в станке или роботе -- реакция на кнопку "стоп", например, и корректная остановка без повреждения механизмов, в сигнализации -- открыть/закрыть машину, не заблокировать в движении);

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

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

      К чему написан этот комментарий: fail fast допустим только в программах для Windows (Linux, не важно, для десктопа). Не только в embedded, даже в серверных приложениях может найтись миллион важных нюансов, из-за чего нельзя "просто выйти". Или бывает выйти некуда, так как нет ОС. Или даже есть ОС, но она этот выход вникуда толком обработать не может. Нужно как минимум запротоколировать факт возникновения сбоя, записать какие-то артефакты которые помогут расследовать причины сбоя. И в целом иметь какой-то сценарий обработки ошибки. Просто перезагрузка, рискующая стать циклической, тоже не вариант т.к. начинает препятствовать обновлению "по воздуху" (или "по кабелю"), т.к. на загрузку новой версии нужно какое-то время. Или есть критические функции прибора, которые должны выполняться даже при отказе основых функций ПО. В телевизорах, например, показ emergency alert service канала.


      1. gecube
        19.01.2022 14:56

        И в целом иметь какой-то сценарий обработки ошибки.

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


      1. ap1973 Автор
        19.01.2022 18:12

        Спасибо. Хоть кто-то понял что в посте речь не про микросервисы (и прочее, прочее...), не про автоматическое восстановление инфраструктуры, а про гораздо более частную задачу, в рамках конкретного процесса.


  1. AndreyDmitriev
    18.01.2022 21:26
    -1

    Слушайте, а вы, вероятно, всегда возите с собой в багажнике автомобиля ещё и канистру впридачу (а может и две) и при заправке бака "под горлышко" тут же заполняете ещё и канистру заодно, ну так, на всякий случай?


    1. ap1973 Автор
      18.01.2022 22:34
      -1

      Я продал машину, езжу на такси, в МСК это гораздо дешевле. А канистры, ТО и прочее - проблемы такситстов. Считаю настоящее программисткое решение - главное эффективность.


  1. OlegZH
    18.01.2022 21:41

    А можно ли изначально архитектуру приложений делать под оптимальное управление памятью. Маниловщина?


    1. ap1973 Автор
      18.01.2022 21:48
      +2

      Если кто-то придумает оптимальное управление памятью, он купит Гугл )))


      1. KGeist
        18.01.2022 22:54
        -1

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


  1. KGeist
    18.01.2022 22:03
    +5

    Такой подход обычно уже реализован с самим OutOfMemoryException. Когда происходит OOM, нужно создать объект типа OutOfMemoryException и инициировать завершение программы. Но как это сделать, если памяти-то нет? Поэтому объект и сопутствующая память резервируются заранее при старте рантайма. Идея не нова (в рантаймах можно встретить похожее и для обработки stack overflow), но оно годится только чтобы более-менее адекватно упасть (записать в лог, например), продолжать работу не стоит.


    1. a-tk
      19.01.2022 12:32

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


  1. lunacyrcus
    19.01.2022 04:51

    Очевидный подход, но глупый и неуниверсальный (потому что зависимо от требований к памяти разных задач, может и не покрыть, а резервировать невменяемые количества памяти тоже так себе идея) и к тому же расточительный (круто будет память улетать когда парочка таких "продуманных" процессов будут висеть и отжирать себе по 512 МБ например на всякий случай).

    Лучше уж тогда производить контроль свободной памяти в системе, и когда это возможно, выводить предупреждения о возможной нехватке. Либо изолировать отдельные задачи так, чтобы при нехватке в одном из них их аварийные завершения отменяли эту задачу и освобождали всю память под нее, но гарантированно не ломали при этом всю программу и данные имевшиеся до запуска этой задачи/не кидали ее в неопределенное состояние. Да и еще можно много придумать зависимо от того насколько вообще это нужно, но явно то что в статье это очень уж топорное и глупое.

    // я уж не говорю о том что с нынешним железом и нынешними ОС надо вообще быть наверно особо одаренным, чтобы на низкоуровневых языках писать программы которым в принципе может не хватить памяти наверно при 99% из всех задач какие вообще можно в эти программы запихнуть, задач где реально необходимо выхватить много памяти без невозможности освободить ее очень мало

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

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


  1. edo1h
    19.01.2022 10:08

    моё мнение: это костыль.
    правильнее заранее выделять память под критичные участки кода вроде обработки ошибок (возможно статично).


  1. a-tk
    19.01.2022 12:34
    +2

    Э. Таненбаум - Архитектура компьютера

    Э. Таненбаум - Современные операционные системы

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


  1. fk0
    19.01.2022 14:04

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

    Кроме того, есть определённая опасность для многопоточных приложений: если память в общий пул отдали, то её тут же могут исчерпать другие потоки. Которые в таком случае неплохо бы предварительно остановить. Но тоже не так просто, т.к. потоки могут владеть каким-либо примитивом синхронизации и остановка другого потока может означать, что поток в котором произошла ошибка не сможет её обработать т.к. встанет на вечном ожидании освобождения какого-либо мьютекса. Собственно сам вызов malloc() внутри имеет мьютекс, и нельзя прервать поток зашедший в malloc(). Можно сделать специальную версию malloc() которая будет вызывать отказы во всех потоках, кроме потока обрабатывающего ошибку.

    Пишут в коментариях, мол такое решение, зарезервировать память заранее, неэффективно тратит ресурсы ОС. Это не так. Выделяя достаточно большой объём (от десятков страниц) можно получить аллокацию через mmap, которая при спекулятивной модели выделения (в linux) на самом деле условно-бесплатная. И резервируется здесь адресное пространство, но не страницы физической памяти. Физическая память потребуется при первом использовании каждой выделенной страницы. Не во всех системах так конечно, особенно в embedded. Но в типовом Linux резервирование и неиспользование памяти не является заметной проблемой.

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


    1. gecube
      19.01.2022 14:13

      Но в типовом Linux резервирование и неиспользование памяти не является заметной проблемой.

      не соглашусь. Это является проблемой именно поэтому, потому что разработчики перестают считать байты и думают, что у них условно бесплатное бесконечное количество ОЗУ, вне зависимости от того, сколько в системе реально есть. И это откладывает проблему с недостатком ОЗУ на максимально поздний момент :-) Когда уже поздно что-то исправлять. В этом отношении, такое ощущение, что лучше всякие джавы, которые сами изначально аллоцируют кучу памяти и работают внутри своих пулов...


  1. fk0
    19.01.2022 14:50

    Вдогонку. Тут пишут, мол OOM -- это совершенно другая проблема и вместо подстановки костылкиков мол эту самую проблему и нужно решить. Оно конечно верно, но "в каждой программе есть по меньшей мере одна ошибка" (C) Дейсктра.

    И было бы крайне глупо и самонадеянно полагаться на полное отсутствие ошибок. А если ошибка есть, то лучше иметь какой-то механизм обработки ошибок, чем "просто выйти". Тем более, что последнее и не всегда возможно.

    Да, срабатываение этого механизма -- не норма. Нужно искать и исправлять ошибку. Точно так же как и самолёты не должны падать и пароходы тонуть не должны, это не норма. Но иногда тонут и лучше иметь спасательные плоты, куда погрузить пассажиров, чем всех утопить. Суть в этом.

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

    В целом да, лучше чтоб аварийная система использовала преаллоцированные ресурсы, но коль последнее невозможно, на мой взгляд решение автора всё равно лучше, чем никакого решения. Но, я советовал бы учесть риски (многопоточность) на которые указал в предыдущем комментарии.


    1. ap1973 Автор
      19.01.2022 18:21

      Спасибо за понимание.

      Наверное мне надо учиться лучше излагать мысли, или как минимум, определять в посте контекст, граничные условия.

      Буду исправляться, и разворачивать мысль серьезнее, а не сразу делиться найденой идеей. Хоть и хорошей, никто из критиков меня не переубедил :)