LINUX.ORG.RU

Вопрос по ручному управлению памятью

 ,


1

2

Я в этом не разбираюсь, но возник такой вопрос. Считается, что в языках с GC нет ручного управления, а, например в сишке есть.

Меня заинтересовал вот какой вопрос. Допустим, мы пишем команду «освободить память такую-то». Интересно, что реально тут происходит в этот момент? Исполнение останавливается до тех пор, пока память не будет освобождена, а затем возобновляется? В таком случае, у нас разница между языком с GC и без него только в том, что нет накладных расходов на сам GC, на алгоритмы подсчета ссылок и/или обход дерева. Непосредственно на расход памяти это не влияет, практически, так получается?

И, кстати, что представляет из себя процесс освобождения памяти, на более низком уровне?

ЗЫ пришлось поставить тег c++, поскольку тегов связанных с «просто си» не нашел вообще. Подскажите что поставить по си.



Последнее исправление: cetjs2 (всего исправлений: 2)

Непосредственно на расход памяти это не влияет, практически, так получается?

Конечно! Ведь всем давно известно, что Java/C#-программы жрут в 10+ раз больше памяти, чем C/C++-программы, просто потому что это заговор производителей железа с целью продать больше планок RAM.

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

EXL ★★★★★
()
Ответ на: комментарий от EXL

Все же, хотелось бы услышать ответ по-существу:)

newquestion
() автор топика
Ответ на: комментарий от EXL

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

newquestion
() автор топика

пока память не будет освобождена

Для начала тебе надо понять, что значит «память будет освобождена». Всё остальное прояснится в процессе познания.

i-rinat ★★★★★
()

И, кстати, что представляет из себя процесс освобождения памяти, на более низком уровне?

Операционка ставит галочку, «эта страница не нужна».

vazgen05 ★★★
()
Ответ на: комментарий от vazgen05

То есть, просто другая программа потом запросит эту страницу, и если на ней стоит эта галка, она ее сможет использовать, так? Никакой обработки не требуется, получается?

newquestion
() автор топика

1. Есть тег ansi_c.

2. https://en.wikipedia.org/wiki/C_dynamic_memory_allocation

Разница ЯП отличных от C в том, что по большей части вместе с реальными данными запихиваются ЯП-специфичные данные, они иногда даже управляются средствами компилятора. А т.к. за этими структурами надо бы тоже кому-то (кому?:) следить, были придуманы GC, грохающие структуры, которые сами же выделили. Для рядового программиста это преподносится как «вау, смотри, GC делает все за тебя!». Конечно делает, сами нагкодили, сами и рулите своими структурами :)

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

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

Кроме размера памяти ты пропускаешь момент о том, что происходит «за кулисами». А там выполняются лишние проверки, инкременты счетчиков и т.д. и т.п. Процессор не резиновый.

ИМХО. Не эксперт. Народ придет, расскажет подробно.

gh0stwizard ★★★★★
()

Чтоб граждане отвечающие знали про контекст и понимали уровень тс:

Где файл останется висеть? А если он несколько террабайтов размером?

В памяти он останется висеть.

Несколько террабайт? Повторюсь.

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

Переместить строку в конец файла (комментарий)

anonymous
()
Ответ на: комментарий от newquestion

Кстати, в C++ почти не осталось ручного управления памятью. То есть оно есть, но лучше напрямую не пользоваться.

i-rinat ★★★★★
()
Ответ на: комментарий от gh0stwizard

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

то есть, если я пишу что-нибудь типа new Array(1,2,3), выделяется памяти больше чем нужно для этого массива? А как же он определяет, сколько памяти надо выделить?

newquestion
() автор топика
Ответ на: комментарий от anonymous

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

newquestion
() автор топика
Ответ на: комментарий от newquestion

то есть, если я пишу что-нибудь типа new Array(1,2,3), выделяется памяти больше чем нужно для этого массива?

В каком ЯП? В языках, где данные представляются в виде объектов (ява скажем), естественно, кроме самих данных будет создана структура я.объект, чтобы ты мог использовать методы вроде asString(), asBytes() и т.п. (примеры из вакуума). Тем не менее это все немного отдаленно сказано, т.к. JVM может содержать оптимизации специально для таких простых случаев. Принцип тем не менее остается в силе: за плюшки ЯП тебе придется платить памятью и/или процесорным временем. GC в плюшки ЯП не входит.

А как же он определяет, сколько памяти надо выделить?

Опять же, зависит от ЯП и может он в типизацию или не может. Если может, то выделит malloc(3 * sizeof(int)). А может оптимизировать, считая себя умнее тебя, и сделать так: malloc(8 * sizeof(int)).

gh0stwizard ★★★★★
()
Последнее исправление: gh0stwizard (всего исправлений: 3)
Ответ на: комментарий от newquestion

то есть, если я пишу что-нибудь типа new Array(1,2,3), выделяется памяти больше чем нужно для этого массива? А как же он определяет, сколько памяти надо выделить?

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

rezedent12 ☆☆☆
()
Последнее исправление: rezedent12 (всего исправлений: 1)
Ответ на: комментарий от gh0stwizard

кроме самих данных будет создана структура я.объект, чтобы ты мог использовать методы вроде asString(), asBytes()

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

newquestion
() автор топика
Ответ на: комментарий от newquestion

Лалка, ты под примером с грепом насал попукивать про висящие в памяти файлы. Ты в любом случае обдристался.

anonymous
()
Ответ на: комментарий от Solace

Ну да. Ручное управление памятью, оно же проявляется не в том, нужно ли писать new/delete или make_unique/make_shared. А, например, в том, сколько усилий нужно приложить к тому, чтобы создать сложную динамическую структуру данных (скажем, граф с циклическими ссылками между узлами). В языке с GC это как два байта об асфальт. А в C++ — тот еще геморрой, даже не смотря на наличие shared_ptr/weak_ptr.

eao197 ★★★★★
()

Меня заинтересовал вот какой вопрос. Допустим, мы пишем команду «освободить память такую-то». Интересно, что реально тут происходит в этот момент?

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

m0rph ★★★★★
()
Ответ на: комментарий от anonymous

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

newquestion
() автор топика
Ответ на: комментарий от Solace

Ты чо, он же написал великий и могучий SObjectiser

anonymous
()
Ответ на: комментарий от eao197

Да, только надо не забыть слинковаться с libstroustrup.so

Deleted
()
Ответ на: комментарий от i-rinat

Кстати, в C++ почти не осталось ручного управления памятью.

Всякие unique_ptr - это всё ещё «ручное» управление, если противопоставляем мы ГЦ.

DarkEld3r ★★★★★
()
Ответ на: комментарий от newquestion

в общем случае они наследуются

От чего наследуются? От класса? И что один класс на все типы данных? А если классы умеют в полиморф, тогда что делать? Даже поставим вопрос проще, есть такая запись myAry = new Array(1,2,3), дальше ты делаешь вызов в такой простой форме myAry.size(). Как по-твоему ЯП узнает, что size() надо вызвать для типа данных массив, но не строка, и даже не мультибайтовая строка? Логично, надо как-то и где-то записать, что myAry это массив из чисел, myAry должен использовать метод size, который используется только для массивов из чисел. Как проще это сделать не выделяя структуры в рантайме, аль в компиляции?

Я не спорю, что конкретно этот случай можно сделать с нулевым потреблением памяти. А что если это делается для выдуманных классов httpClient = new HTTPClient("URL")? Оптимизировать все возможные варианты? Или ограничить возможности ЯП? Вот пример с объектом httpClient, что по-твоему представляет?

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

gh0stwizard ★★★★★
()
Ответ на: комментарий от eao197

В языке с GC это как два байта об асфальт.

GC в большинстве случаев не избавляет от необходимости в weakptr.

Ну да. Ручное управление памятью, оно же проявляется не в том, нужно ли писать new/delete или make_unique/make_shared.

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

Deleted
()
Ответ на: комментарий от Deleted

GC в большинстве случаев не избавляет от необходимости в weakptr.

Вам, конечно же, не составит труда привести пару примерчиков?

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

Вы перестаньте приписывать мне свои бредовые мысли и брехать не придется.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

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

anonymous
()
Ответ на: комментарий от gh0stwizard

И что один класс на все типы данных?

А в си у тебя одна функция на все операции?

Как по-твоему ЯП узнает, что size() надо вызвать для типа данных массив, но не строка, и даже не мультибайтовая строка?

В нормальных языках это делается так. при вызове size ищется данный метод в цепочке родителей, и применяется к данной структуре.

Вот пример с объектом httpClient, что по-твоему представляет?

опять же, в нормальном языке, то же самое, что экземпляр встроенного класса, никакой принципиальной разницы нет

А после делать сравнение с тем, как ты бы это написал на сишке.

на любом не-ООП языке будет все то же самое, только без лукапов, ты будешь вынужден вручную применять функцию к структуре, вот и вся разница. То есть не myArray.size, а sizeOf(myArray)

newquestion
() автор топика
Ответ на: комментарий от eao197

Раз: https://docs.python.org/3/library/weakref.html

Два: http://ruby-doc.org/stdlib-2.2.3/libdoc/weakref/rdoc/WeakRef.html

Вы перестаньте приписывать мне свои бредовые мысли и брехать не придется.

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

Deleted
()
Ответ на: комментарий от anonymous

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

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

anonymous
()
Ответ на: комментарий от DarkEld3r

это всё ещё «ручное» управление, если противопоставляем мы ГЦ.

Вопрос терминологии. Я бы назвал это «явным» в противовес к «неявному» в случае со сборщиком мусора. Уже не надо вручную прописывать malloc/free, но всё равно программист точно знает, в какой момент память освобождается.

Сборка мусора тоже может быть по явному вызову.

i-rinat ★★★★★
()
Ответ на: комментарий от Deleted

Раз: https://docs.python.org/3/library/weakref.html
Два: http://ruby-doc.org/stdlib-2.2.3/libdoc/weakref/rdoc/WeakRef.html

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

eao197 ★★★★★
()
Ответ на: комментарий от newquestion

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

my $value = 1; # да, тут достаточно int
if ( $value eq "wtf" ) { # а тут не достаточно
  ... 
}
ты платишь внутренней языковой структурой для $value, в которой динамически происходит аллокация памяти для представления числа 1 в виде строки (выделяется намного больше чем 1 байт и даже не 4 байта для одного символа в юникоде), с последующим сохранением этой строки (optimistic memory management). Даже, если все последующие действия с $value окажутся только работой над числами, преобразованная ранее строка "1" никуда не денется, она останется «висеть» в памяти оптимистично ожидая своего звездного часа. После конечно произойдет free(value_struct), но фрагментация памяти в лишние байты для внутренней структуры и новосозданной строки никуда не денется, выделенная ранее память не вернется в ОС [95% случаев].

Почему нужна какая-то внутренняя структура?

Да просто потому чтобы в едином месте связать строковое представление и численное. И, грубо говоря, все только ради этого. Будь ЯП по-проще, на коленке у Васяна придуман, он бы сделал так, что все нужное происходило в момент компиляции, применялись бы шаблоны и т.п. А на случай того же действия в рантайме он писал в ридме, что unimplemented.

Помимо прочего внутренняя структура для $value содержит в себе еще счетчик ссылок. Этот счетчик в рантайме увеличивается и уменьшается. И еще в этой структуре есть несколько разных полей. Например, если $value это объект, то содержиться строка как называется класс, по которому объект создан.

С первого взгляда кажется, что потребление ОЗУ не велико, однако, когда таких структур становится дофига ты замечаешь, что процесс жрет каких-то 30мб, чтобы скачивать и парсить одну единственную html-страницу.

gh0stwizard ★★★★★
()
Ответ на: комментарий от eao197

Упс... А что же там теперь вместо этого?

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

Я понимаю, что если долго заниматься какой-то темой, она становится больной. Так что когда дело доходит до обсасывания терминологии, пожалуйста, приведи ссылку на текст, где описывается, как ты понимаешь то или иное слово. Например «ручное».

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

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Например «ручное».

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

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

Тогда как в языках с нормальным точным GC ситуация совсем другая. У программиста необходимость явно отмечать моменты, когда какая-то группа объектов становится больше не нужна, возникают гораздо реже. А задачи следить за корректностью ссылок, которые могут указывать на удаленные объекты, нет в принципе.

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

eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)

Ничего так вброс. Чтобы вы понимали: ОП - программист на JS и поэтому всё знает лучше всех вас вместе взятых, даже лучше Википедии. Тему, очевидно, создал исключительно чтоб потроллить.

asaw ★★★★★
()
Ответ на: комментарий от eao197

Что значит расставляет? RAII не надо расставлять, это не try-with-resource, finally или using, которые нужно писать каждый раз. Деструктор вызывается автоматически, расставлять ничего не надо.

forCe
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.