Сборщик мусора и размещение объектов в стеке
От: PC Car  
Дата: 13.12.07 14:00
Оценка:
Давно не программировал, поэтому если встретятся фактические ошибки, делающие неверной цепь рассуждений, просьба просто указать на них. Если это трюизмы (== баян, если в словарь лазить лень), то причина в том же.

Насколько мне известно, память в языках типа Явы или Шарпа — не ресурс. Вернее, ресурс, но управляет им некая внешняя по отношению к программе штуковина — рантайм или виртуальная машина, или еще какой контейнер.

У программистов на плюсах был излюбленный прием — использовать пару конструктор-деструктор для контроля за выходом из блока кода. Например, CWaitCursor() делал запрос к API на отрисовку песочных часов, а ~CWaitCursor() вертал все в зад. Соответственно, можно было разместить на стеке анонимный объект в любом блоке, который управлял курсором автоматически и этот трюк был эксепшено-безопасным, что немаловажно. Конструкции finally не требовалось.

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

Почему не ввести вместо using на такой случай специальный тип — стековый класс? Это ведь не потребует усложнения синтаксиса. Просто у структуры появится деструктор с детерминированным временем вызова.
Re: Сборщик мусора и размещение объектов в стеке
От: GarryIV  
Дата: 13.12.07 15:45
Оценка: -1
Здравствуйте, PC Car, Вы писали:

PC>Почему не ввести вместо using на такой случай специальный тип — стековый класс? Это ведь не потребует усложнения синтаксиса. Просто у структуры появится деструктор с детерминированным временем вызова.


Вообще насчет баяна ты угадал

То есть ты предлагаешь создать типы которые можно располагать только на стеке? И ты думаешь это полетит (в смысле покроет потребности)?

Оставим в покое песочные часы и возьмем SomeConnection к примеру.

Хранить ее только на стеке? Спасибо не надо...
Разрешим хранить в куче? Привет using...
WBR, Igor Evgrafov
Re: Сборщик мусора и размещение объектов в стеке
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.12.07 16:01
Оценка: +1 -1
Здравствуйте, PC Car, Вы писали:

PC>Почему не ввести вместо using на такой случай специальный тип — стековый класс? Это ведь не потребует усложнения синтаксиса. Просто у структуры появится деструктор с детерминированным временем вызова.


В C++/CLI так и сделано. Жить от это особо лучше не стало. По крайней это не компенсировало отсуствия в C++/CLI других полезных свойств Шарпа.

На самом деле после некоторого опыта использования управляемых языков понимашь, что страхи плюсовиков (коим я являлся до перехода на управляемые среды) сильно надуманы. 99% контроля ресурсов — это контроль памяти. А когда память не нужно пасти, то остальное можно хоть руками контролировать. В общем using-а за глаза хватает.

Ну, а стековый объект все равно не всегда поможет. Ведь если время жизни объекта не совпадает с временем проведенным в процедуре, то и контролировать его так не выйдет.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Сборщик мусора и размещение объектов в стеке
От: remark Россия http://www.1024cores.net/
Дата: 14.12.07 06:02
Оценка:
Здравствуйте, GarryIV, Вы писали:

GIV>Оставим в покое песочные часы и возьмем SomeConnection к примеру.


GIV>Хранить ее только на стеке? Спасибо не надо...

GIV>Разрешим хранить в куче? Привет using...


Почему бы не разрешить хранить только на стеке? А если всё же "Спасибо не надо... ", то разрешаешь хранить на стеке объект, который в конструкторе получает соединение из пула, а в деструкторе возвращает в пул.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Сборщик мусора и размещение объектов в стеке
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 14.12.07 06:26
Оценка:
Здравствуйте, PC Car, Вы писали:

В отношении Java 6 нечто подобное (оптимизация под названием Escape-анализ) уже реальность:

Стековая аллокация

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

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

Плохо то, что кэш-промах при аллокации объекта в массиве имеет особенно опасное взаимодействие с памятью. При аллокации памяти из массива содержимое данной памяти является мусором — любые биты, оставшиеся после того, как память использовалась в последний раз. Если вы размещаете блок памяти в массиве, который еще не находится в кэше, при выполнении возможны зависания, пока содержимое данной памяти будет переноситься в кэш. Затем вы немедленно перепишете эти значения, которые вы затратили на то, чтобы перенести в кэш с нулями или другими исходными данными, а это приводит к большим потерям работы памяти. (Некоторые процессоры, такие как Azul's Vega, включают аппаратную поддержку для ускорения аллокации массива).

Escape-анализ

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

(прим. ред. — далее приводятся примеры с псевдокодом оптимизации)

...

Escape-анализ в Mustang

Escape-анализ является оптимизацией, о которой уже давно говорилось, и наконец-то вот она — текущие сборки Mustang (Java SE 6) могут осуществлять escape-анализ и конвертировать аллокацию массива в стековую аллокацию (или без аллокации) там, где это уместно. Использование escape-анализа для устранения некоторых результатов аллокаций с более быстрым средним временем аллокации, уменьшенным потреблением ОЗУ и меньшим количеством кэш-промахов. Кроме этого, оптимизирование некоторых аллокаций уменьшает давление на сборщик мусора и позволяет реже запускать сборку.

© Теория и практика Java: Еще раз о городских легендах о производительности. Аллокация гораздо быстрее, чем вы думаете, и все больше ускоряется, Брайан Гетц

Re: Сборщик мусора и размещение объектов в стеке
От: Cyberax Марс  
Дата: 14.12.07 06:36
Оценка:
Здравствуйте, PC Car, Вы писали:

PC>Почему не ввести вместо using на такой случай специальный тип — стековый класс? Это ведь не потребует усложнения синтаксиса. Просто у структуры появится деструктор с детерминированным временем вызова.

А ты дальше подумай — что делать, если эта структура будет полем класса, уничтожение которого недетерминировано?
Sapienti sat!
Re[2]: Смотри шире
От: remark Россия http://www.1024cores.net/
Дата: 14.12.07 06:44
Оценка: :)
Здравствуйте, VladD2, Вы писали:

VD>На самом деле после некоторого опыта использования управляемых языков понимашь, что страхи плюсовиков (коим я являлся до перехода на управляемые среды) сильно надуманы. 99% контроля ресурсов — это контроль памяти. А когда память не нужно пасти, то остальное можно хоть руками контролировать. В общем using-а за глаза хватает.



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

Так же техника применяется для обеспечения транзакционности, когда действие, выполняемое в деструкторе объекта, отменяется методом commit(). Например транзакционная вставка в 3 контейнера (всё или ничего):
vector<int> v1, v2, v3;
push_back_tx tx1 (v1, 1);
push_back_tx tx2 (v2, 2);
v3.push_back(3);
tx1.commit();
tx2.commit();

Если у объекта типа push_back_tx не был вызван метод commit(), то в деструкторе он удаляет из контейнера элемент, который вставил в конструкторе.
Как это будет выглядеть в языке без стековой семантики? Я не видел красивого и *масштабируемого* решения.

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



VD>Ну, а стековый объект все равно не всегда поможет. Ведь если время жизни объекта не совпадает с временем проведенным в процедуре, то и контролировать его так не выйдет.


Хммм... Такие заявления ставят под сомнение твоё умение программировать на языках со стековой семантикой...
Если время жизни объекта не совпадает с временем проведенным в процедуре, то объект создаётся в динамической памяти, а на стеке создаётся объект, контролирующий время жизни первого объекта:
shared_ptr<heavy_class> f()
{
  return shared_ptr<heavy_class>(new heavy_class());
}

shared_ptr<heavy_class> x = f();

Такой приём является абсолютно отработанным и обыденным в современном С++. Это не составляет никакой проблемы.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Сборщик мусора и размещение объектов в стеке
От: remark Россия http://www.1024cores.net/
Дата: 14.12.07 06:56
Оценка:
Здравствуйте, rsn81, Вы писали:

R>Здравствуйте, PC Car, Вы писали:


R>В отношении Java 6 нечто подобное (оптимизация под названием Escape-анализ) уже реальность:[q]Стековая аллокация



LOL
Как сделать хорошо? Вначале сделайте плохо, а потом сделайте как было.
Вначале ввели аллокацию на стеке, потом ручное управление памятью... Осталось только union'ы вернуть



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Сборщик мусора и размещение объектов в стеке
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 14.12.07 07:02
Оценка:
Здравствуйте, remark, Вы писали:

R>LOL

R>Как сделать хорошо? Вначале сделайте плохо, а потом сделайте как было.
R>Вначале ввели аллокацию на стеке, потом ручное управление памятью... Осталось только union'ы вернуть
R>
Иронию не догнал: ручную-детерминированную аллокацию на стеке никто и не обещал, а про ручное управление памятью вообще не понял. Так что чего-то вы не по теме радуетесь.
Re[4]: Сборщик мусора и размещение объектов в стеке
От: remark Россия http://www.1024cores.net/
Дата: 14.12.07 07:54
Оценка: :))
Здравствуйте, rsn81, Вы писали:

R>Здравствуйте, remark, Вы писали:


R>>LOL

R>>Как сделать хорошо? Вначале сделайте плохо, а потом сделайте как было.
R>>Вначале ввели аллокацию на стеке, потом ручное управление памятью... Осталось только union'ы вернуть
R>>
R>Иронию не догнал: ручную-детерминированную аллокацию на стеке никто и не обещал, а про ручное управление памятью вообще не понял. Так что чего-то вы не по теме радуетесь.


Ищи по "The Real-Time Specification for Java". Фактически они добавили синхронное освобождение памяти — схема применяемая в современном С++.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: Сборщик мусора и размещение объектов в стеке
От: Cyberax Марс  
Дата: 14.12.07 08:01
Оценка: +2
Здравствуйте, remark, Вы писали:

R>Ищи по "The Real-Time Specification for Java". Фактически они добавили синхронное освобождение памяти — схема применяемая в современном С++.

Нет. В realtime Java _нет_ синхронного освобождения памяти — это сделать в общем случае чрезвычайно дорого. Для realtime Java гарантируются только верхние границы на время отклика сборщика мусора, ничего более.
Sapienti sat!
Re[2]: Сборщик мусора и размещение объектов в стеке
От: Константин Л. Франция  
Дата: 14.12.07 09:03
Оценка:
Здравствуйте, GarryIV, Вы писали:

[]

GIV>Оставим в покое песочные часы и возьмем SomeConnection к примеру.


GIV>Хранить ее только на стеке? Спасибо не надо...


А что мешает этому объекты хранить ссылки на др объекты в хипе и в деструкторе делать им Dispose?

GIV>Разрешим хранить в куче? Привет using...
Re[2]: Сборщик мусора и размещение объектов в стеке
От: Константин Л. Франция  
Дата: 14.12.07 09:07
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Здравствуйте, PC Car, Вы писали:


PC>>Почему не ввести вместо using на такой случай специальный тип — стековый класс? Это ведь не потребует усложнения синтаксиса. Просто у структуры появится деструктор с детерминированным временем вызова.

C>А ты дальше подумай — что делать, если эта структура будет полем класса, уничтожение которого недетерминировано?

И что? Если ее создали на стеке — ведет себя как стековый объект. Если нет — ну нет так нет. В C++\CLI именно так и работает.
Re[3]: Сборщик мусора и размещение объектов в стеке
От: Cyberax Марс  
Дата: 14.12.07 09:08
Оценка: +2
Здравствуйте, Константин Л., Вы писали:

C>>А ты дальше подумай — что делать, если эта структура будет полем класса, уничтожение которого недетерминировано?

КЛ>И что? Если ее создали на стеке — ведет себя как стековый объект. Если нет — ну нет так нет. В C++\CLI именно так и работает.
Осталось сделать следующий шаг, и понять, что стековые объекты не нужны — достаточно паттерна Disposable и сахара в виде using().
Sapienti sat!
Re[5]: Сборщик мусора и размещение объектов в стеке
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 14.12.07 09:28
Оценка:
Здравствуйте, remark, Вы писали:

R>Ищи по "The Real-Time Specification for Java". Фактически они добавили синхронное освобождение памяти — схема применяемая в современном С++.

Java RTS и Java SE — как говорится найти 10 отличий. Причем тут оно?
И потом, RTS вроде какой-то древнючий JRS-... какой-то там чуть не под первым номером с коммерческой реализацией, а я говорил про Java SE 6.
Re[4]: Сборщик мусора и размещение объектов в стеке
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 14.12.07 09:44
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Осталось сделать следующий шаг, и понять, что стековые объекты не нужны — достаточно паттерна Disposable и сахара в виде using().

Не нужны кому, программисту просто как сахар? Ну тогда да. В принципе, и без конструкции using даже можно жить не чихая.
Re[3]: Смотри шире
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 14.12.07 12:21
Оценка: +1
Здравствуйте, remark, Вы писали:

R>Техника значительно шире. Например, с помощью стекового объекта может контролироваться временный перевод курсора в "часики", блокировка/разблокировка мьютекса, задержка/возобновление обновления графического элемента управления, резервирование/разрезервирование какого-либо места/счётчика и т.д. и т.д.


Это прекрасно реализуется при помощи using. Более того, именно для таких фич using предпочтительнее, потому что явно выделяет регион действия и при чтении сразу видно, что и как происходит.

R>Так же техника применяется для обеспечения транзакционности, когда действие, выполняемое в деструкторе объекта, отменяется методом commit().


Вот как раз под рукой из DSL Tools:
using (var tran = partition.Store.TransactionManager.BeginTransaction())
{
    ...
    tran.Commit();
}


R> Например транзакционная вставка в 3 контейнера (всё или ничего):

R>
R>vector<int> v1, v2, v3;
R>push_back_tx tx1 (v1, 1);
R>push_back_tx tx2 (v2, 2);
R>v3.push_back(3);
R>tx1.commit();
R>tx2.commit();
R>

R>Если у объекта типа push_back_tx не был вызван метод commit(), то в деструкторе он удаляет из контейнера элемент, который вставил в конструкторе.
R>Как это будет выглядеть в языке без стековой семантики? Я не видел красивого и *масштабируемого* решения.

Аналогично
using (var tx1 = v1.TxPushBack(1))
using (var tx2 = v2.TxPushBack(2))
{
    v3.PushBack(3);
    tx1.Commit();
    tx2.Commit();
}


R>Хммм... Такие заявления ставят под сомнение твоё умение программировать на языках со стековой семантикой...

R>Если время жизни объекта не совпадает с временем проведенным в процедуре, то объект создаётся в динамической памяти, а на стеке создаётся объект, контролирующий время жизни первого объекта:

И счетчик с количеством использований? Это, мягко говоря, не очень стыкуется с GC.

R>Такой приём является абсолютно отработанным и обыденным в современном С++. Это не составляет никакой проблемы.


Для этого, помимо стековой семантики, нужен еще и автоматический вызов деструкторов всех членов класса.
... << RSDN@Home 1.2.0 alpha rev. 725 on Windows Vista 6.0.6000.0>>
AVK Blog
Re[4]: Сборщик мусора и размещение объектов в стеке
От: Константин Л. Франция  
Дата: 14.12.07 12:40
Оценка: +1 -1
Здравствуйте, Cyberax, Вы писали:

C>Здравствуйте, Константин Л., Вы писали:


C>>>А ты дальше подумай — что делать, если эта структура будет полем класса, уничтожение которого недетерминировано?

КЛ>>И что? Если ее создали на стеке — ведет себя как стековый объект. Если нет — ну нет так нет. В C++\CLI именно так и работает.
C>Осталось сделать следующий шаг, и понять, что стековые объекты не нужны — достаточно паттерна Disposable и сахара в виде using().

Шаг к чему? К дао? Достаточно понять, что using менее сладок нежели стековые объекты.
Re[3]: Смотри шире
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.12.07 13:25
Оценка:
Здравствуйте, remark, Вы писали:

VD>>На самом деле после некоторого опыта использования управляемых языков понимашь, что страхи плюсовиков (коим я являлся до перехода на управляемые среды) сильно надуманы. 99% контроля ресурсов — это контроль памяти. А когда память не нужно пасти, то остальное можно хоть руками контролировать. В общем using-а за глаза хватает.


R>Ресурсами, которые контролируются стековым объектом, могут быть не только такие объекты как соединение, файл и т.д.

R>Техника значительно шире. Например, с помощью стекового объекта может контролироваться временный перевод курсора в "часики", блокировка/разблокировка мьютекса, задержка/возобновление обновления графического элемента управления, резервирование/разрезервирование какого-либо места/счётчика и т.д. и т.д.

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

Вот тебе реальные факты. В проекте компилятора Немерле есть 5 файлов (из 124 файлов) в которых используется using:
http://nemerle.org/svn/nemerle/trunk/macros/Data.n — 4
http://nemerle.org/svn/nemerle/trunk/macros/Util.n — 3
http://nemerle.org/svn/nemerle/trunk/ncc/codedom/NemerleCodeCompiler.n — 2
http://nemerle.org/svn/nemerle/trunk/ncc/CompilationOptions.n — 1
http://nemerle.org/svn/nemerle/trunk/ncc/hierarchy/TypesManager.n — 1
общее число применений равно 11. Это на 2.2 мегабайта исходников.

Теперь, ВНИМАНИЕ вопрос... О чем речь то?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Сборщик мусора и размещение объектов в стеке
От: remark Россия http://www.1024cores.net/
Дата: 14.12.07 13:26
Оценка: -1
Здравствуйте, Константин Л., Вы писали:

КЛ>Здравствуйте, Cyberax, Вы писали:


C>>Здравствуйте, Константин Л., Вы писали:


C>>>>А ты дальше подумай — что делать, если эта структура будет полем класса, уничтожение которого недетерминировано?

КЛ>>>И что? Если ее создали на стеке — ведет себя как стековый объект. Если нет — ну нет так нет. В C++\CLI именно так и работает.
C>>Осталось сделать следующий шаг, и понять, что стековые объекты не нужны — достаточно паттерна Disposable и сахара в виде using().

КЛ>Шаг к чему? К дао? Достаточно понять, что using менее сладок нежели стековые объекты.


Стековые объекты слаще

void f()
{
    lock l1 (m1);
    //...
    lock l2 (m2);
    //...
    file f;
    //...
}

void f()
{
    using (lock l1 = m1.lock())
    {
        //...
        using (lock l2 = m2.lock())
        {
            //...
            using (file f = open_file())
            {
                //...
            }
        }
    }
}



Не говоря о том, что using это всё ещё просто сахар, который можно забыть, а стековый объект — способ форсирования корректного использования.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.