- PVSM.RU - https://www.pvsm.ru -
Привет! Представляю вашему вниманию перевод статьи «Demystifying memory management in modern programming languages [1]» за авторством Deepu K Sasidharan.
В данной серии статей мне бы хотелось развеять завесу мистики над управлением памятью в программном обеспечении (далее по тексту — ПО) и подробно рассмотреть возможности, предоставляемые современными языками программирования. Надеюсь, что мои статьи помогут читателю заглянуть под капот этих языков и узнать для себя нечто новое.
Углублённое изучение концептов управления памятью позволяет писать более эффективное ПО, потому как стиль и практики кодирования оказывают большое влияние на принципы выделения памяти для нужд программы.
Управление памятью — это целый набор механизмов, которые позволяют контролировать доступ программы к оперативной памяти компьютера. Данная тема является очень важной при разработке ПО и, при этом, вызывает затруднения или же вовсе остаётся черным ящиком для многих программистов.
Когда программа выполняется в операционный системе компьютера, она нуждается в доступе к оперативной памяти (RAM) для того, чтобы:
Помимо места, используемого для загрузки своего собственного байт-кода, программа использует при работе две области в оперативной памяти — стек (stack) и кучу (heap).
Стек используется для статичного выделения памяти. Он организован по принципу «последним пришёл — первым вышел» (LIFO). Можно представить стек как стопку книг — разрешено взаимодействовать только с самой верхней книгой: прочитать её или положить на неё новую.
Использование стека в JavaScript. Объекты хранятся в куче и доступны по ссылкам, которые хранятся в стеке. Тут [2] можно посмотреть в видеоформате
Куча используется для динамического выделения памяти, однако, в отличие от стека, данные в куче первым делом требуется найти с помощью «оглавления». Можно представить, что куча это такая большая многоуровневая библиотека, в которой, следуя определённым инструкциям, можно найти необходимую книгу.
В отличие от жёстких дисков, оперативная память весьма ограниченна (хотя и жёсткие диски, безусловно, тоже не безграничны). Если программа потребляет память не высвобождая её, то, в конечном итоге, она поглотит все доступные резервы и попытается выйти за пределы памяти. Тогда она просто упадет сама, или, что ещё драматичнее, обрушит операционную систему. Следовательно, весьма нежелательно относиться легкомысленно к манипуляциям с памятью при разработке ПО.
Современные языки программирования стараются максимально упростить работу с памятью и снять с разработчиков часть головной боли. И хотя некоторые почтенные языки всё ещё требуют ручного управления, большинство всё же предоставляет более изящные автоматические подходы. Порой в языке используется сразу несколько подходов к управлению памятью, а иногда разработчику даже доступен выбор какой из вариантов будет эффективнее конкретно для его задач (хороший пример — C++). Перейдём к краткому обзору различных подходов.
Язык не предоставляет механизмов для автоматического управления памятью. Выделение и освобождение памяти для создаваемых объектов остаётся полностью на совести разработчика. Пример такого языка — C. Он предоставляет ряд методов (malloc, realloc, calloc и free) для управления памятью — разработчик должен использовать их для выделения и освобождения памяти в своей программе. Этот подход требует большой аккуратности и внимательности. Так же он является в особенности сложным для новичков.
Сборка мусора — это процесс автоматического управления памятью в куче, который заключается в поиске неиспользующихся участков памяти, которые ранее были заняты под нужды программы. Это один из наиболее популярных вариантов механизма для управления памятью в современных языках программирования. Подпрограмма сборки мусора обычно запускается в заранее определённые интервалы времени и бывает, что её запуск совпадает с ресурсозатратными процессами, в результате чего происходит задержка в работе приложения. JVM (Java/Scala/Groovy/Kotlin), JavaScript, Python, C#, Golang, OCaml и Ruby — вот примеры популярных языков, в которых используется сборщик мусора.
RAII — это программная идиома в ООП, смысл которой заключается в том, что выделяемая для объекта область памяти строго привязывается к его времени существования. Память выделяется в конструкторе и освобождается в деструкторе. Данный подход был впервые реализован в C++, а так же используется в Ada и Rust.
Данный подход весьма похож на сборку мусора с подсчётом ссылок, однако, вместо запуска процесса подсчёта в определённые интервалы времени, инструкции выделения и освобождения памяти вставляются на этапе компиляции прямо в байт-код. Когда же счётчик ссылок достигает нуля, память освобождается как часть нормального потока выполнения программы.
Автоматический подсчёт ссылок всё так же не позволяет обрабатывать циклические ссылки и требует от разработчика использования специальных ключевых слов для дополнительной обработки таких ситуаций. ARC является одной из особенностей транслятора Clang, поэтому присутствует в языках Objective-C и Swift. Так же автоматический подсчет ссылок доступен для использования в Rust и новых стандартах C++ при помощи умных указателей [3].
Это сочетание RAII с концепцией владения, когда каждое значение в памяти должно иметь только одну переменную-владельца. Когда владелец уходит из области выполнения, память сразу же освобождается. Можно сказать, что это примерно как подсчёт ссылок на этапе компиляции. Данный подход используется в Rust и при этом я не смог найти ни одного другого языка, который бы использовал подобный механизм.
Читайте так же другие части серии:
Вы можете подписаться на автора статьи в Twitter [14] и на LinkedIn [15].
Иллюстрации:
Визуализация стека сделана с помощью pythontutor [16].
Иллюстрация концепции владения: Link Clark, The Rust team [17] под Creative Commons Attribution Share-Alike License v3.0 [18].
За вычитку перевода отдельное спасибо Александру Максимовскому [19] и Катерине Шибаковой [20]
Автор: saionaro
Источник [21]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/347309
Ссылки в тексте:
[1] Demystifying memory management in modern programming languages: https://dev.to/deepu105/demystifying-memory-management-in-modern-programming-languages-ddd
[2] Тут: https://www.youtube.com/watch?v=95_CAUC9nvE
[3] умных указателей: https://ru.wikipedia.org/wiki/%D0%A3%D0%BC%D0%BD%D1%8B%D0%B9_%D1%83%D0%BA%D0%B0%D0%B7%D0%B0%D1%82%D0%B5%D0%BB%D1%8C
[4] Part 2: Memory management in JVM(Java, Kotlin, Scala, Groovy): https://dev.to/deepu105/visualizing-memory-management-in-jvm-java-kotlin-scala-groovy-clojure-19le
[5] Part 3: Memory management in V8(JavaScript/WebAssembly): https://dev.to/deepu105/visualizing-memory-management-in-v8-engine-javascript-nodejs-deno-webassembly-105p
[6] http://homepages.inf.ed.ac.uk: http://homepages.inf.ed.ac.uk
[7] https://javarevisited.blogspot.com: https://javarevisited.blogspot.com
[8] http://net-informations.com: http://net-informations.com
[9] https://gribblelab.org: https://gribblelab.org
[10] https://medium.com/computed-comparisons: https://medium.com/computed-comparisons
[11] https://en.wikipedia.org/wiki/Garbage*collection*(computer_science): https://en.wikipedia.org/wiki/Garbage*collection*(computer_science)
[12] https://en.wikipedia.org/wiki/Automatic_Reference_Counting: https://en.wikipedia.org/wiki/Automatic_Reference_Counting
[13] https://blog.sessionstack.com: https://blog.sessionstack.com
[14] Twitter: https://twitter.com/deepu105
[15] LinkedIn: https://www.linkedin.com/in/deepu05/
[16] pythontutor: http://pythontutor.com/javascript.html#mode=display
[17] Link Clark, The Rust team: https://hacks.mozilla.org/2018/12/rust-2018-is-here/
[18] Creative Commons Attribution Share-Alike License v3.0: https://creativecommons.org/licenses/by-sa/3.0/
[19] Александру Максимовскому: https://vk.com/maksimovskiy_alexander
[20] Катерине Шибаковой: https://vk.com/shibakova_e
[21] Источник: https://habr.com/ru/post/489360/?utm_campaign=489360&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.