Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, korvin_, Вы писали: _>>Это возможно только если под этот твой процесс сохранениня стейта уже была выделена вся необходимая для его работы память, что в языках с GC практически невозможно сделать S>Это ещё почему? Сразу выделяем всё на старте.
Это проблематично сделать, учитывая, что даже String (любая конкатенация == аллокация) и любой массив в Java -- Object. При этом нужно быть осторожным с использованием любого библиотечного кода, который может что-то аллоцировать, в том числе и неявно, и даже языковых конструкций, типа for (var x : array) { ... }, т.к. она приводит к аллокации итератора. В Хаскелле контроллировать аллокации ещё сложнее, явного new нет, любое значение может быть (и скорее всего будет) аллоцировано динамически, любой "вызов" функции может породить thunk. Удачи "выделить сразу всё на старте".
_>>соответственно твоё "сделать что-то полезное" точно так же наткнётся на ООМ. S>Не обязательно.
Почти гарантированно.
_>>Это в теории, на практике -- нет. Даже в С/C++ и прочих языках с "ручным" контролем памяти что-то сделать сложно. Поэтому приходит OOM-Killer S>OOM-Killer — это следствие криворукой архитектуры линукса. Программы под винду пишутся на тех же С/С++, но при этом там нет (и не нужно) никакого OOM-Killer.
Может и криворукой, но что делается в Windows в таких случаях?
_>>Поэтому на практике, что можно сделать: упасть. Гипервизор/оркерстратор перезапустит ноду/сервис/под/контейнер, а мы будем изучать логи, метрики, дампы, и по результату, либо добавим больше памяти, либо изменим параметры масштабирования, либо починим утечку, либо оптимизируем код. S>Это очень, очень дорогостоящая процедура. Даже если удалось обойтись без обращения к разработчикам за починкой — всё равно: добавление памяти и перезапуск стоят дней простоя.
Какая процедура дорогостоящая? Перезапуск к Кубернетесе стоит секунды простоя. Добавление memory request/limits -- считанные минуты. При этом, это может коснуться только одного пода, остальные продолжат работать, если у них нет проблем. Тогда для пользователя никакого простоя не будет. Я уж не говорю о том, что ваш graceful shutdown при OOM это никак не решает. Памяти всё равно не хватило. О каких 24/7 вы говорили вообще?
S>Не, я понимаю, что самый простой способ — это просто надеяться. Перезапускаем — может на этот раз карта ляжет так, что упавший процесс успеет отработать до того, как процесс с утечкой сожрёт всю память. _>>Так и что бы это дало? Как его использовать? Напиши пример. S>Повторюсь: пример зависит от области деятельности и от того, где именно вылетел OOM. Если я получил OOM при создании шестибайтового объекта — да, скорее всего продолжать вообще смысла нет, и можно просто перезапускать весь контейнер или даже всю ноду. А если я пытаюсь выделить буфер размером в 100М, то шанс велик, что после вылета ООМ у меня всё ещё ~50М свободно. И можно заняться чем-то другим, не столь прожорливым к памяти. Или попробовать выделить буфер вдвое меньше. Ну, как пример — сортируем файл интов размером в 100гб. Понятно, что нужно делать сортировку слиянием; и понятно, что чем больше размер чанка, тем быстрее мы закончим. Но это не означает, что неудача выделения чанка в 10G — это катастрофа и нужно делать паник. Можно и 100мб кусками его сортировать — просто это займёт больше времени.
И долго вы будете гадать на кофейной OOM-гуще, сколько памяти вам надо? А если другой поток/процесс потребует столько же памяти? Если вы хотите буфер 10G, может, вам изначально стоило озаботиться наличием необходимой памяти?
_>>Так Optional -- это другой тип по отношению к int. S>Ну, так и Expr | OOM — это другой тип по отношению к Expr.
Но вы предлагали засунуть OOM в Expr, т.е. сделать не другой тип.
_>>Теперь, сам Optional же тоже требует памяти, значит Optional<Optional<T>>, и для него тоже нужна память, Optional<Optional<Optional<T>>>, и так далее. S>Нет, зачем? S>1. В хорошей системе типов Optional Optional == Optional.
Нет, в любой нормальной системе типов Optional Optional -- это Optional Optional, также как List of List of T -- это List of List of T, а не List of T. А если они у вас автоматически схлопываются, у вас дерьмовая система типов.
S>2. Независимо от этого, new может всегда резервировать память под sizeof(optional)+sizeof(T), и возвращать none невзирая на то, кому из них не хватило места S>4. Независимо от этого, вычислительная модель может быть построена на ref-типах (см. Java), и тогда Optional<T> вообще не требует никаких накладных расходов. Это ровно тот же "ссылка на T", только в отличие от "обычной" ссылки, эта может принимать значение none.
Она может не требовать, если компилятор особым образом компилирует Optional, а иначе это такой же тип-сумма, как любой другой со всеми вытекающими.
_>>Так вот в Haskell/Ocaml и т.п. у нас нет new T, у нас сразу T, как 1, 2, true, false, "foo", "bar". Сразу значение. S>Ну, это же иллюзия. Значения всё равно как-то конструируются. Иначе бы любая программа на Haskell была бы просто константой.
Конструируются, конечно, но вы никак не можете знать, на стэке или в куче. Любой невинный Int может запросто быть аллоцирован в куче и никак не получится преаллоцироват его для работы в OOM-хэндлере.