Недостатки управления памятью на основе областей

38

Мне действительно нравится управление памятью на основе области (SBMM) или RAII , так как на него чаще всего (сбивает с толку?) Ссылается сообщество C ++. Насколько я знаю, за исключением C ++ (и C), сегодня нет другого основного языка, который бы использовал SBMM / RAII в качестве основного механизма управления памятью, и вместо этого они предпочитают использовать сборку мусора (GC).

Я нахожу это довольно запутанным, так как

  1. SBMM делает программы более детерминированными (вы можете точно сказать, когда объект уничтожен);
  2. на языках, которые используют GC, вам часто приходится выполнять ручное управление ресурсами (см., например, закрытие файлов в Java), что частично противоречит цели GC и также подвержено ошибкам;
  3. Кучи памяти также (очень элегантно, imo) могут быть ограничены областью видимости (см. std::shared_ptrв C ++).

Почему СБММ не используется более широко? Каковы его недостатки?

Павел
источник
1
Некоторые недостатки (особенно касающиеся скорости) обсуждаются в Википедии: en.wikipedia.org/wiki/…
Philipp
2
Проблема ручного управления ресурсами Java является побочным эффектом отсутствия гарантии того, что finalize()метод объекта будет вызван перед сборкой мусора. По сути, это создает тот же класс проблем, который должен решать сборщик мусора.
Blrfl
7
@Blrfl Чепуха. «Ручное» управление ресурсами (для ресурсов, кроме памяти, очевидно) было бы предпочтительным, даже если бы этой «проблемы» не существовало, потому что GC может работать очень долго после того, как ресурс станет неиспользованным, или даже вообще не запускаться. Это не проблема для памяти , а управление памятью - это все, что должна решить сборка мусора.
4
Кстати. Мне нравится называть его SBRM, поскольку вы можете использовать один и тот же механизм для управления ресурсами в целом, а не только памятью.
PlasmaHH

Ответы:

27

Давайте начнем с того, что постулируем, что память (в десятки, сотни или даже тысячи раз) более распространена, чем все другие ресурсы вместе взятые. Каждая переменная, объект, член объекта нуждается в некоторой памяти, выделенной для него и освобожденной позже. Для каждого файла, который вы открываете, вы создаете от десятков до миллионов объектов для хранения данных, извлеченных из файла. Каждый поток TCP идет вместе с неограниченным количеством временных строк байтов, созданных для записи в поток. Мы здесь на одной странице? Отлично.

Чтобы RAII работал (даже если у вас есть готовые умные указатели для каждого варианта использования под солнцем), вам необходимо получить право собственности . Вам нужно проанализировать, кому должен принадлежать тот или иной объект, а кому нет, и когда право собственности должно быть передано от А к Б. Конечно, вы можете использовать общее владение всем , но тогда вы будете эмулировать GC с помощью интеллектуальных указателей. В этот момент становится намного проще и быстрее встроить GC в язык.

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

Теперь вы назвали некоторые недостатки создания мусора для всех значений. Тем не менее, интегрировать и RA-безопасные GC, и типы значений с RAII в один язык чрезвычайно сложно, поэтому, возможно, лучше перенести эти компромиссы другими способами?

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

Ваш второй пункт, ручное управление ресурсами, в настоящее время решается с помощью оператора, который выполняет очистку на основе области действия, но не связывает эту очистку с временем жизни объекта (следовательно, не взаимодействует с ГХ и безопасностью памяти). Это usingв C #, withв Python, try-with-resources в последних версиях Java.


источник
1
Это не объясняет, почему недетерминированные модели, такие как GC, должны превосходить детерминированные. usingзаявления возможны только локально. Таким способом невозможно очистить ресурсы, содержащиеся в переменных-членах.
Филипп
8
@ Филипп Общее владение всем - это ГК, просто очень плохое. Если вы подразумеваете «совместное владение», чтобы подразумевать подсчет ссылок, скажите, пожалуйста, но продолжайте обсуждение циклов в комментариях к ответу amon. Я также не уверен, является ли подсчет ссылок детерминированным в том смысле, в котором заинтересован OP (объекты освобождаются как можно раньше, циклы дисконтирования, но вы часто не можете определить, когда это происходит, глядя на программу). Более того, подсчет ссылок происходит медленно, намного медленнее, чем у современной трассировки GC.
16
usingэто шутка по сравнению с RAII, просто чтобы вы знали.
DeadMG
3
@ Филипп, пожалуйста, опишите свою метрику как "улучшенную". Это правда, что ручное управление памятью быстрее во время выполнения для управления памятью. Однако о стоимости программного обеспечения нельзя судить только по времени ЦП, затраченному только на управление памятью.
ArTs
2
@ArTs: я бы даже не согласился с этим. RAII поставляется с требованием, что объект должен быть уничтожен, поскольку он покидает область видимости. Цикл, следовательно, требуется для n разрушений объекта. В современном поколении GC эти разрушения могут быть отложены до конца цикла, или даже позже, и выполнить всего одну операцию, чтобы уничтожить память на сотни итераций. GC может быть очень очень быстрым в своих хороших случаях.
Phoshi
14

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

Языки со сборщиком мусора не могут использовать RAII напрямую, но часто предлагают синтаксис с эквивалентным эффектом. В Java у нас есть оператор try-with-ressource

try (BufferedReader br = new BufferedReader(new FileReader(path))) { ... }

который автоматически вызывает .close()ресурс при выходе из блока. C # имеет IDisposableинтерфейс, который позволяет .Dispose()вызываться при выходе из using (...) { ... }оператора. Python имеет withутверждение:

with open(filename) as f:
    ...

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

File.open(name, mode) do |f|
    ...
end

Я думаю, что Node.js использует ту же стратегию.

Амон
источник
4
Использование функций более высокого порядка для управления ресурсами датируется задолго до Ruby. В Лиспе довольно часто, скажем, with-open-filehandleфункции, которые открывают файл , передают его функции и по возвращении функции закрывают файл снова.
Jörg W Mittag
4
Циклический ссылочный аргумент довольно распространен, но насколько он важен на самом деле? Циклические ссылки могут быть смягчены, используя слабые указатели, если право собственности ясно.
Филипп
2
@Philipp При использовании исх счета, собственность , как правило , не ясно. Кроме того, этот ответ говорит о языках, которые используют подсчет ссылок исключительно и автоматически, поэтому слабые ссылки либо не существуют, либо их гораздо сложнее использовать, чем строгие ссылки.
3
@ Филиппинские циклические структуры данных очень редки, если вы все равно не работаете со сложными графиками. Слабые указатели не помогают в общем циклическом графе объектов, хотя они помогают в более распространенных случаях, таких как родительские указатели в дереве. Хороший обходной путь - сохранить объект контекста, который представляет ссылку на весь граф и управляет уничтожением. Пересчет не является компромиссом, но он требует, чтобы программист был в курсе его ограничений. Т.е. он имеет немного более высокую когнитивную стоимость, чем GC.
Амон
1
Важной причиной, по которой счетчик ссылок используется редко, является то, что он часто медленнее, чем GC, несмотря на свою простоту.
Rufflewind
14

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

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

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

Патрик
источник
1
Корректность возможна только в том случае, если объекты содержат ссылки только от своего имени, а не от имени целей этих ссылок. Как только такие вещи, как уведомления, входят в микс (Боб держит ссылку на Джо, потому что Джо попросил Боба уведомить его, когда что-то произошло, и Боб пообещал сделать это, но Бобу в противном случае наплевать на Джо), для правильности GC часто требуется управление ресурсами с определенными областями. [реализовано вручную во многих случаях, поскольку в системах GC отсутствует автоматизация C ++].
суперкат
@supercat: «Правильность GC часто требует ограниченного управления ресурсами». А? Область видимости существует только в исходном коде, а GC существует только во время выполнения (и, следовательно, совершенно не замечает существования области видимости).
Джон Харроп
@JonHarrop: я использовал термин «область действия» в том же смысле, что и «указатель области видимости» в C ++ [время жизни объекта должно соответствовать времени существования контейнера, в котором он содержится], поскольку это использование подразумевается в исходном вопросе. Моя точка зрения состоит в том, что объекты создают потенциально долговременные ссылки на себя для таких целей, как получение событий, которые могут быть не компонуемыми в системе с чисто GC. Для правильности определенные ссылки должны быть сильными, а некоторые ссылки должны быть слабыми, и какие ссылки должны быть, которые будут зависеть от того, как используется объект. Например ...
суперкат
... предположим, что объекты Фред и Барни подписываются на уведомление, когда что-либо в определенной директории изменяется. Обработчик Фреда не делает ничего, кроме увеличения счетчика, о значении которого он может сообщить по запросу, но для которого он не имеет другого использования. Обработчик Барни откроет новое окно, если определенный файл был изменен. Для правильности, Фред должен быть подписан со слабым событием, но Барни должен быть сильным, но у объекта таймера не будет никакой возможности узнать это.
суперкат
@supercat: Верно. Я бы не сказал, что это происходит "часто". Я сталкивался с этим только один раз за 30 лет программирования.
Джон Харроп
7

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

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

Йорг Миттаг
источник
9
Я не думаю, что замыкания в C ++ являются хорошим примером. Лямбды в C ++ 11 являются просто синтаксическим сахаром для классов функторов (которые значительно предшествуют C ++ 11) и в равной степени небезопасны для памяти: если вы захватываете что-то по ссылке и вызываете замыкание после того, как оно исчезает, вы просто получаете UB, как если бы вы держали ссылку дольше действительной. То, что они появились на 40 лет позже, связано с запоздалым признанием ФП, а не с выяснением того, как сделать их безопасными. И хотя их разработка была, безусловно, огромной задачей, я сомневаюсь, что большая часть усилий ушла на пожизненные соображения.
Я согласен с delnan: C ++ неправильно понял замыкания: вам нужно очень тщательно их программировать, если вы не хотите получать дамп ядра при их вызове.
Джорджио
2
@delnan: лямбда-захват по ссылке очень намеренно имеет такой [&]синтаксис. Любой программист C ++ уже связывает &знак со ссылками и знает о устаревших ссылках.
MSalters
2
@MSalters Что ты думаешь? Я нарисовал эталонное соединение сам. Я не говорил, что лямбды C ++ исключительно небезопасны, я сказал, что они столь же небезопасны, как и ссылки. Я не утверждал, что лямбды C ++ плохие, я выступал против утверждения этого ответа (что C ++ получил замыкания очень поздно, потому что они должны были понять, как это сделать правильно).
5
  1. SBMM делает программы более детерминированными (вы можете точно сказать, когда объект уничтожен);

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

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

Смотрите usingв C # и useв F #.

  1. Куча памяти также (очень элегантно, imo) может быть привязана к области видимости (см. std :: shared_ptr в C ++).

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

Почему СБММ не используется более широко? Каковы его недостатки?

SBMM ограничивает то, что вы можете сделать:

  1. SBMM создает восходящую проблему funarg с первоклассными лексическими замыканиями, поэтому замыкания популярны и просты в использовании в таких языках, как C #, но редки и сложны в C ++. Обратите внимание, что существует общая тенденция использования функциональных конструкций в программировании.

  2. SBMM требует деструкторов, и они препятствуют вызовам хвоста, добавляя больше работы, прежде чем функция сможет вернуться. Хвостовые вызовы полезны для расширяемых конечных автоматов и предоставляются такими вещами, как .NET.

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

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

  5. SBMM поддерживает объекты живыми до конца их области видимости в исходном коде, которая часто длиннее, чем необходимо, и может быть намного дольше, чем необходимо. Это увеличивает количество плавающего мусора (недоступные объекты, ожидающие повторного использования). Напротив, отслеживание сборки мусора приводит к освобождению объектов вскоре после того, как исчезает последняя ссылка на них, что может быть гораздо раньше. См. Мифы управления памятью: оперативность .

SBMM настолько ограничивает, что программистам нужен путь эвакуации в ситуациях, когда нельзя вкладывать время жизни в гнездо. В C ++ shared_ptrпредлагает маршрут эвакуации, но он может быть в ~ 10 раз медленнее, чем отслеживание сборки мусора . Таким образом, использование SBMM вместо GC в большинстве случаев поставило бы большинство людей в тупик. Это не значит, однако, что это бесполезно. SBMM по-прежнему имеет ценность в контексте систем и встроенного программирования, где ресурсы ограничены.

FWIW вы могли бы проверить Forth и Ada, и прочитать о работе Николаса Вирта.

Джон Харроп
источник
1
Если вы скажете, какие биты, я смогу разработать или процитировать статьи.
Джон Харроп
2
Насколько важно быть в 10 раз медленнее в нескольких редких случаях использования, а не вездесущим во всех случаях использования? C ++ имеет unique_ptr и для большинства целей его достаточно. Кроме того, вместо того, чтобы атаковать RAII через C ++ (язык, который многие любят ненавидеть за то, что он является архаичным языком), если вы собираетесь атаковать RAII через нападение на язык, попробуйте младшего брата из семьи RAII, например, Rust. Rust в основном понимает все правильно, что C ++ ошибся, в то время как он понимает большинство вещей так же, как и C ++. Дальнейшее «использование» дает вам очень ограниченный набор вариантов использования и игнорирует композицию.
user1703394
2
«Насколько важно быть в 10 раз медленнее в нескольких редких случаях использования, а не вездесущим во всех случаях использования?». Во-первых, это круговой аргумент: shared_ptrэто редко встречается в C ++, потому что он такой медленный. Во-вторых, это сравнение яблок и апельсинов (как я уже цитировал в статье), потому что shared_ptrоно во много раз медленнее, чем производственный сборщик мусора. В-третьих, ГХ не являются вездесущими и их избегают в таких программах, как LMax и FIX-движок Rapid Addition.
Джон Харроп
1
@ Джон Харроп, пожалуйста, просветите меня. Какой магический рецепт вы использовали в течение этих 30 с лишним лет, чтобы смягчить переходные эффекты использования глубоких ресурсов? Без такого волшебного рецепта спустя 30 с лишним лет я мог бы только заключить, что вы, должно быть, ошибочно приписали его укусам по другим причинам.
user1703394
1
@Jon Harrop, shared_ptr не редкость, потому что он медленный, он редок по той причине, что в прилично спроектированной системе потребность в «совместном владении» встречается редко.
user1703394
4

Глядя на некоторый индекс популярности, такой как TIOBE (который, разумеется, спорен, но я полагаю, что для такого рода вопросов вполне нормально использовать это), вы сначала видите, что ~ 50% из 20 лучших из них - "языки сценариев" или "диалекты SQL" ", где" простота использования "и средства абстракции имеют гораздо большее значение, чем детерминированное поведение. Из оставшихся «скомпилированных» языков есть около 50% языков с SBMM и ~ 50% без. Поэтому, когда вы убираете языки сценариев из своих расчетов, я бы сказал, что ваше предположение неверно, среди скомпилированных языков те, что с SBMM, так же популярны, как и те, у которых нет.

Док Браун
источник
1
Чем отличается «простота использования» от детерминизма? Не следует ли считать детерминированный язык более простым в использовании, чем недетерминированный?
Филипп
2
@ Филипп Только то, что детерминировано или нет, имеет значение. Время жизни объекта само по себе не имеет значения (хотя C ++ и друзья связывают многие вещи, которые имеют значение, со временем жизни объекта, потому что они могут). Когда недоступный объект освобождается, это не имеет значения, потому что по определению вы его больше не используете.
Еще одна вещь, различные «языки сценариев», такие как Perl и Python, также используют подсчет ссылок в качестве основного средства управления памятью.
Филипп
1
@Philipp По крайней мере в мире Python это считается деталью реализации CPython, а не свойством языка (и практически любая другая реализация избегает пересчета). Более того, я бы сказал, что подсчет ссылок без запрета на всеохваты с GC цикла резервного копирования не квалифицируется как SBMM или RAII. На самом деле, вам будет трудно найти сторонников RAII, которые считают этот стиль управления памятью сравнимым с RAII (в основном потому, что это не так, циклы в любом месте могут предотвратить быстрое освобождение в любом месте программы).
3

Одним из главных преимуществ системы GC, о котором еще никто не упомянул, является то, что ссылка в системе GC гарантированно сохраняет свою идентичность до тех пор, пока она существует . Если кто-то вызывает IDisposable.Dispose(.NET) или AutoCloseable.Close(Java) объекта, в то время как копии ссылки существуют, эти копии будут продолжать ссылаться на тот же объект. Объект больше не будет полезен, но попытки использовать его будут иметь предсказуемое поведение, управляемое самим объектом. Напротив, в C ++, если код вызывает deleteобъект, а затем пытается его использовать, все состояние системы становится полностью неопределенным.

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

Supercat
источник
4
Свойство, на которое вы ссылаетесь в первом полугодии, - это безопасность памяти. Несмотря на то, что GC - это очень простой способ обеспечения безопасности памяти, GC не нужен: посмотрите на Rust за хорошо сделанный пример.
@delnan: Когда я просматриваю rust-lang.org, мой браузер не может найти полезную навигацию оттуда; где мне искать дополнительную информацию? У меня сложилось впечатление, что безопасность памяти без GC накладывает определенные ограничения на структуры данных, которые могут не вписываться во все вещи, которые может понадобиться приложению, но я был бы рад оказаться ошибочным.
суперкат
1
Я не знаю ни одного (или даже небольшого набора) хороших ссылок для этого; мои знания Rust накопились за год или два чтения списка рассылки (и всего, что связано с почтой, включая различные учебные пособия, сообщения в блогах о дизайне языка, проблемы с github, ThisWeekInRust и многое другое). Кратко расскажу о вашем впечатлении: Да, каждая безопасная конструкция (обязательно) накладывает ограничения, но практически для любого безопасного фрагмента кода памяти существует или может быть написана соответствующая безопасная конструкция. Наиболее распространенные из них уже существуют в языке и stdlib, все остальные могут быть написаны в коде пользователя.
@delnan: Требует ли Rust блокированных обновлений для счетчиков ссылок, или у него есть какие-то другие средства для работы с неизменяемыми объектами (или неизменяемыми оберточными объектами), которые не имеют определенного владельца? Есть ли у Rust понятие как «владеющий объектами», так и «не владеющий» указателями? Я вспоминаю статью об указателях «Xonor», в которой обсуждалась идея объектов, имеющих единственную ссылку, которая «владеет» ими, и другие ссылки, которые не имеют; когда «владеющая» ссылка выходит за пределы области видимости, все не принадлежащие ей ссылки становятся ссылками на мертвые объекты и могут быть идентифицированы как таковые ...
суперкат
1
Я не думаю, что комментарии Stack Exchange являются подходящим средством для ознакомления с языком. Если вам все еще интересно, вы можете перейти прямо к источнику (#rust IRC, список рассылки rust-dev и т. Д.) И / или поразить меня чатом (вы должны его создать).
-2

Во-первых, очень важно понимать, что приравнивание RAII к SBMM. или даже в СРРМ. Одним из наиболее важных (и наименее известных или наиболее недооцененных) качеств RAII является тот факт, что он делает «ресурс» свойством, которое НЕ транзитивно для композиции.

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

http://minorfs.wordpress.com/2011/04/29/why-garbage-collection-is-anti-productive/

Важно отметить, что хотя RAII в основном используется в C ++, Python (наконец-то версия, не основанная на ВМ) имеет деструкторы и детерминированный GC, который позволяет использовать RAII вместе с GC. Лучший из обоих миров, если бы это было.

user1703394
источник
1
-1 Это одна из худших статей, которые я когда-либо читал.
Джон Харроп
1
Проблема в языках не в том, что они поддерживают GC, а в том, что они отказываются от RAII. Нет причин, по которым язык / структура не должны поддерживать оба.
суперкат
1
@ Джон Харроп, не могли бы вы уточнить. Из утверждений, изложенных в статье, есть ли одно из первых 3 утверждений, которое не имеет места? Я думаю, что вы можете не согласиться с заявлением о производительности, но остальные 3 утверждения абсолютно действительны. Самое главное первое о транзитивности бытия ресурса.
user1703394
2
@ user1703394: Во-первых, вся статья основана на бессмысленном «языке GCed», когда, фактически, он не имеет никакого отношения к сбору мусора. Во-вторых, он обвиняет сборщик мусора, когда на самом деле ошибки связаны с объектно-ориентированным программированием. Наконец, его аргумент опоздал на 10 лет. Подавляющее большинство программистов уже модернизировались для того, чтобы собирать языки как раз потому, что они предлагают гораздо более высокую производительность.
Джон Харроп
1
Его конкретные примеры (ОЗУ, дескрипторы открытых файлов, блокировки, потоки) довольно показательны. Мне трудно вспомнить, когда в последний раз мне приходилось писать код, который имел дело непосредственно с любым из них. С оперативной памятью GC автоматизирует все. С помощью файловых дескрипторов я пишу код, например, File.ReadLines file |> Seq.lengthгде абстракции обрабатывают закрытие для меня. Замки и темы я заменил на .NET Taskи F # MailboxProcessor. Вся эта «Мы взорвали объем ручного управления ресурсами» - просто полная чушь.
Джон Харроп