LINUX.ORG.RU

Зачем нужны GC, Java, .NET?


0

0

Имеем для каждого объекта счетчик ссылок на него. Исчезает последняя ссылка --> объект удаляется. Запрещаем при этом ручную работу с указателями. Получаем то же самое, что и в Java или .NET, но без огромного оверхеда. Те же Genie или Vala работают по такому принципу, хотя там и есть возможность рулить памятью руками (но чисто опционально). При этом они не таскают за собой большую дуру под названием JVM и работают куда быстрее.

Так в чем прикол «безопасного программирования» по Сану или Микрософту в таком случае?

Ответ на: комментарий от Legioner

Покажите мне пример работы с тремя файлами с with-open-file. Думаю - будет 3 уровня вложенности.

Тоже мне проблема.

(defmacro with-open-files ((&rest filespecs) &body body)
  (if (null filespecs) 
    `(progn ,@body)
    `(with-open-file ,(car filespecs)
       (with-open-files ,(cdr filespecs) ,@body))))

;;пример
(with-open-files ((in1 "a.txt") (in2 "b.txt"))
  (format t "~&~a~%~a~%"
          (read-line in1) (read-line in2)))
файлы и сокеты в лиспах это как бы разные вещи. Общего у них - потоки. Но аналогично можно написать и with-open-streams(без 's' оно есть уже, хотя и так написать не было бы проблем никаких)

С with-cleaners суть такая: Наверху формы биндятся переменные. Опционально можно сразу указать код, который будет выполняться при выходе из области видимости. В ходе формы можно оный код еще добавить(пример - мы список с какими-то ресурсами в ходе формы поставляем в функцию, там нам в этот список кладут еще каких-то ресурсов, но не тех совсем, для которых мы уже написали подчищающий код, мы, соответственно, проверяем список и если там эти новые ресурсы оказались, добавляем к чистящему коду еще и код, который чистит их. Почему сразу не написали наверху - ну, может быть код, чистящий эти новые ресурсы дико ресурсозатратен, и просто так его вызывать - очень нехорошо.). В области видимости, опять же, весь этот код можно вызвать, тем самым аннулировав(и, соответственно, потом добавить еще какой-то).

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

>Скажи лучше что нибудь умное по теме топика.
Не знаю, что собственно сказать, кроме того, что GC нужно(а те, кто говорит, что нет - идиоты), а модели управления ресурсами в большинстве я.п. - что в C++, что в додиезе каком-нибудь - говно говном, и что самое плохое - они привинчены к языкам намертво.

Хотя нет. Лучше помолчи.

Не могу, «в интернете кто-то не прав».

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

> Вот только этот подсчёт осуществляется раз в несколько секунд (или когда там вызывается GC).

Нет, там внутри точно такой же подсчет ссылок. По крайней мере в Java так. Иначе определить неиспользуемые объекты невозможно.

А в случае прямого подсчёта ссылок - постоянно идёт инкремент-декремент, т.к. копирование ссылок - очень частая операция.

Он в любом случае будет. Впрочем, это проблема только в плохо спроектированных многопоточных программах.

Так как вы будете решать вопрос циркулярных ссылок? Таки запускать изредка полный цикл GC? Или, как старый Internet Explorer - кушать память?

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

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

Думаю - будет 3 уровня вложенности.

На самом деле, это - другая проблема лиспа. Уровни вложенности плодятся только так, потому что let, flet, macrolet - это всё разные конструкции. Я придумал такой выход:

(macroexpand-1 
 '(proga
   (flet f (x) (+ 1 x))
   (symbol-macrolet g (f 4))
   (let u g)
   u))
===>
(flet
 ((f (x) (+ 1 x)))
 (symbol-macrolet
  ((g (f 4)))
  (let ((u g)) 
   u)))

Можно видеть, я сэкономил два уровня вложенности и снизил количество скобок с 24 до 14. Я думаю, что именно вот такая гротескная избыточность лиспа в простых вещах была одной из причин его забвения, а вовсе не AI winter. И, кстати, да, это - тоже макрос. Хотя не самый маленький...

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

> Нет, там внутри точно такой же подсчет ссылок. По крайней мере в Java так. Иначе определить неиспользуемые объекты невозможно.
Прошу прощения, но ИМХО это бред. Объекты должны иметь какую-то структуру, по которой сборщик мусора может понять, является ли данное конкретное слово в памяти указателем или нет. Можно на секунду вообразить, что в Java это не так, но от этого ситуация не перестаёт быть бредом.

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

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

Пиши на жабе.


btw
«Anyone could learn Lisp in one day, except that if they already knew Fortran, it would take three days.»

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

> Не знаю, что собственно сказать, кроме того, что GC нужно(а те, кто говорит, что нет - идиоты),

Без GC можно обойтись. При этом управление памятью не станет сложнее.

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

ОК, какая «модель управления ресурсами» не говно? CL не в счет, ты к нему явно неравнодушен.

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

>«Anyone could learn Lisp in one day, except that if they already knew Fortran, it would take three days.»

Это не к тому, что надо лисп первым языком программирования учить, а к тому, что не надо, программируя на нем, мыслить по жабьему. Возмущаться отсутствием доступа к элементам структур через точку и т.п.
По крайней мере, надо стараться использовать язык, а не заниматься «исправлением» его(а по сути, просто переделыванием под свой стиль, навеянный всяким говном, вроде упомянутой жабы) и прочим бредом.

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

>Без GC можно обойтись. При этом управление памятью не станет сложнее.
Ты совсем дурак? Тебе уже раз десять тут объяснили, что во-первых, становится сложнее(циклические ссылки, работа с крупными структурами, древовидными и т.п, на элементы которых ссылаются извне), а во-вторых становится менее эффективным.

ОК, какая «модель управления ресурсами» не говно? CL не в счет, ты к нему явно неравнодушен.

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

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

Ну а ты просто дурак, или не совсем адекватен. Тебе об этом вроде даже на c.l.l. говорили.

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

> Прошу прощения, но ИМХО это бред. Объекты должны иметь какую-то структуру, по которой сборщик мусора может понять, является ли данное конкретное слово в памяти указателем или нет. Можно на секунду вообразить, что в Java это не так, но от этого ситуация не перестаёт быть бредом.

Невозможно понять по слову в памяти, указатель оно или нет. Как ты это представляешь - GC проходит по ВСЕЙ памяти, разыменовывая КАЖДОЕ слово как указатель?

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

Как вообще можно реализовать GC иначе?

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

Если GC не может отличить указатель от чего-то другого, то это

Conservative GC, говно т.е.

Невозможно понять по слову в памяти, указатель оно или нет.

http://sbcl-internals.cliki.net/tag bit

Tag bits are applied to most objects and pointers so that we can tell what type they are. There's a two-level tag bit scheme

Main types - lowtag

Almost everything (both pointers and immediate objects) get the first level, which uses the three least-significant bits of the object

000    even-fixnum
001    instance-pointer
010    other-immediate-0
011    list-pointer
100    odd-fixnum
101    fun-pointer
110    other-immediate-1
111    other-pointer

For 64-bit ports the list is:

0000    even-fixnum
0001    instance-pointer
0010    other-immediate-0
0011    pad0
0100    pad1
0101    pad2
0110    other-immediate-1
0111    list-pointer
1000    odd-fixnum
1001    fun-pointer
1010    other-immediate-2
1011    pad3 
1100    pad4 
1101    pad5
1110    other-immediate-3
1111    other-pointer

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

А ты почитай про GC иди.

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

> Тебе уже раз десять тут объяснили, что во-первых, становится сложнее(циклические ссылки, работа с крупными структурами, древовидными и т.п, на элементы которых ссылаются извне), а во-вторых становится менее эффективным.

Кроме тебя, никто этого не говорил. Потому что это явно не так.

В CL какую угодно модель можно привинтить, практически.

И какую из них нельзя привинтить в C/C++?

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

>Love5an, подозреваю, что этот тред будет недолгим.

А вот и не угадал. :)

Предлагаю, чтоб каждая из сторон привела примеры реально написанных ими «достаточно серьезных» приложений. С одной стороны С++ без GC. С другой Java/C# c GC. Эээээ.... Ну можно ещё Lisp сюда.

И тогда можно более предметно поговорить об достоинствах и недостатках языков с нативным сборщиком мусора на конкретных примерах.

З.Ы. Хотя наверное так никто не сделает, так как в отличии разговоров об абстракциях, говорить о реальных вещах объективно сложнее.

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

>Кроме тебя, никто этого не говорил. Потому что это явно не так.

Ты слепой или натурально дурак?

И какую из них нельзя привинтить в C/C++?


Попробуй привинти хотя бы такую:
http://www.linux.org.ru/view-message.jsp?msgid=4339051&page=2#comment-4342755

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

>Невозможно понять по слову в памяти, указатель оно или нет.

Tag bits are applied to most objects and pointers so that we can tell what type they are. There's a two-level tag bit scheme

Здесь УЖЕ известно, что слово - это часть объекта или указатель. По ПРОИЗВОЛЬНОМУ слову в памяти ничего нельзя сказать.

А ты почитай про GC иди.

Про Java уже прочитал, что там считаются ссылки. В .net используется generational GC, про который Wiki пишет:

Furthermore, the runtime system maintains knowledge of when references cross generations by observing the creation and overwriting of references.

runtime ... observing ... creation and overwriting of references

О чем и была речь. Дай ссылку на описание работы GC, который САМ, без помощи runtime, понимает что объекты не нужны.

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

Из википедии: «Сборка мусора»

Достоинства и недостатки:

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

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

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

>Здесь УЖЕ известно, что слово - это часть объекта или указатель. По ПРОИЗВОЛЬНОМУ слову в памяти ничего нельзя сказать.

А когда объекты создаются(аллоцируются), рантайм что, срёт произвольными битами в память чтоли? Ты дурак?

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

Из википедии: «Сборка мусора»

Проблемы использования:

Освобождение других ресурсов, занятых объектом. -- Помимо динамической памяти, объект может владеть и другими ресурсами — подчас более ценными, чем память. (тут о ресурсах)

Приёмы программирования. — Использование сборки мусора оказывает влияние и на то, какие приёмы программирования, конструкции, шаблоны алгоритмов допустимы для использования программистом. Сколь бы качественным ни был сборщик мусора, он всегда будет ощутимо замедлять работу программ при возникновении некоторых, особо неудачных для быстрой сборки мусора, ситуаций. (тут о граблях, мелочь, но как же без них)

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

Из википедии: «Сборка мусора»

Достижимость объекта:

(тут заметка про «native GC» vs «подсчет ссылок»)

Другой вариант алгоритма определения достижимости — обычный подсчёт ссылок. Его использование замедляет операции присваивания ссылок, но зато определение достижимых объектов тривиально — это все объекты, значение счётчика ссылок которых превышает нуль. Без дополнительных уточнений этот алгоритм, в отличие от предыдущего, не удаляет циклически замкнутые цепочки вышедших из употребления объектов, сохранивших ссылки друг на друга.

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

> Ты слепой или натурально дурак?

Нет, я всего лишь прав, а у тебя кончились аргументы.

Попробуй привинти хотя бы такую: http://www.linux.org.ru/view-message.jsp?msgid=4339051&page=2#comment-4342755

shared_ptr с custom deleter могут сделать то же самое. Правда пользы от этого будет меньше, потому что в C++ нет полноценных лямбд.

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

Из википедии: «Сборка мусора»

(про то, почему на С++ нельзя сделать «полноценный» GC, не помню был спор на эту тему)

Сборщик мусора может нормально функционировать только тогда, когда он может точно отследить все ссылки на все созданные объекты. Очевидно, если язык допускает преобразование ссылок (указателей) в другие типы данных (целые числа, массивы байтов и так далее), отследить использование таких преобразованных ссылок становится невозможно, и сборка мусора становится бессмысленной — она не защищает от «повисания» ссылок и утечек памяти. Поэтому языки, ориентированные на использование сборки мусора, обычно существенно ограничивают свободу использования указателей, адресной арифметики, преобразований типов указателей к другим типам данных.

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

> про то, почему на С++ нельзя сделать «полноценный» GC

Вообще-то это не про то. Это про то, почему в языках с ГК нет адресной арифметики. ))

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

Сам бы почитал ссылки, там интересно.

It is not possible, in general, for a garbage collector to determine exactly which objects are still live. Even if it didn't depend on future input, there can be no general algorithm to prove that an object is live (cf. the Halting Problem).

То есть, сам GC не может отличить живых от неживых.

In tracing garbage collection, the approximation is that an object can't be live unless it is reachable. In reference counting, the approximation is that an object can't be live unless it is referenced. Hybrid algorithms are also possible. Often the term garbage collection is used narrowly to mean only tracing garbage collection.

Для этого используется подсчет ссылок либо аналогичные механизмы.

В жабе, от Sun, по крайней мере, generational copying gc, никакие там ссылки не считаются.

Хм, в сановской яве нет явного счетчика. Но список актуальных ссылок там есть и его можно прочитать:

http://java.sun.com/j2se/1.5.0/docs/guide/jvmti/jvmti.html#Heap

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

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

Нет, я всего лишь прав, а у тебя кончились аргументы.

Т.е. ты что, отрицаешь, что тебе уже несколько раз тут разъясняли, чем подсчет ссылок менее эффективен, чем GC? Таблетки принял?

shared_ptr с custom deleter могут сделать то же самое. Правда пользы от этого будет меньше, потому что в C++ нет полноценных лямбд.

Т.е. этого ты привинтить не можешь? Вопрос исчерпан? Или ты просто не понял, в чем, собственно, суть примера?

(with-cleaners ((f1 (open "f1" :direction :input)
                    (close f1)) ;;добавляется cleanup-код
                (f2 (open "f2" :direction :output :if-exists :supersede)
                    (close f2))) ;;добавляется cleanup-код
  (add-cleaner (write-line (read-line f1) f2))
  (invoke-cleaners) ;;печатает строчку из одного файла в другой и тут же закрывает файлы
  (psetf f1 (open f2 :direction :input)  ;; переоткрывает файлы
         f2 (open f1 :direction :output :if-exists :supersede))
  (add-cleaners (close f1) (close f2) (format t "Files were closed~%"))
  (write-line (read-line f1) f2)
  );;тут файлы закрываются, и тут же на stdout печатается 'Files were closed'
   ;;а если где-то в области видимости with-cleaners возникнет ошибка,
   ;;вызовется тот "стек" cleaners, который последний был назначен
   ;;если же внутри "стека" возникнет ошибка, дальнейшему его исполнению это не
   ;;помешает, т.к. каждая "ступень" обернута в unwind-protect

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

т.е. если конкретнее:
в cleanup добавляем именно «код», а не функции
он не привязан к какому-то отдельному объекту или, тем более, классу
cleanup код вызывается по «стеку»
его можно вызывать не дожидаясь конца области видимости, в этом случае он аннулируется(в конце области видимости, или при invoke-cleaners, не вызовется такой же), но в любой момент можно добавлять новый код
Все это, естественно, обернуто в unwind-protect(т.е. finally), иначе просто не имело бы смысла

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

Нет, там внутри точно такой же подсчет ссылок. По крайней мере в Java так. Иначе определить неиспользуемые объекты невозможно.

У меня подозрение, что вы не понимаете, как работают сборщики мусора в managed средах. Фундаментально.

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

> Т.е. ты что, отрицаешь, что тебе уже несколько раз тут разъясняли, чем подсчет ссылок менее эффективен, чем GC? Таблетки принял?

Сравнивать производительность GC с подсчетом ссылок вообще некорректно, т.к. GC может быть основан на подсчете ссылок.

А ты что, отрицаешь, что в любой схеме с GC при копировании ссылок runtime отслеживает «умершие» объекты? Например, путем подсчета ссылок?

А таблетки у нас с утра доктор раздает, не пропущу, не бойся ^__"

Т.е. этого ты привинтить не можешь? Вопрос исчерпан? Или ты просто не понял, в чем, собственно, суть примера?

Могу. Более того, могу в любом другом языке. И не только я - любой программист может, кому это будет нужно.

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

Окей, рассказываю алгоритм, вкратце. Имеется список глобальных переменных (статических членов всех классов в джаве). Этот список среде исполнения известен (т.е. адреса их в памяти). Более того, язык строго типизирован, поэтому известны их типы, по крайней мере ссылочные или примитивные. Далее, имеется множество потоков (managed потоков), с которыми связаны стеки. В стеках известен типа каждого элемента. Объединяя всё это получаем множество начальных объектов. У каждого объекта известны начало, размер и точная структура (т.е. смещения, по которым хранятся указатели на другие объекты). Далее рекурсивно проходимся по этим объектам, помечая все деревья объектов как доступные. Вся оставшаяся память - свободная. Если вводить финализаторы, более реальные GC, всё становится сложнее, но счётчики ссылок вести на каждое присваивание/удаление точно не нужно.

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

> У меня подозрение, что вы не понимаете, как работают сборщики мусора в managed средах. Фундаментально.

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

Если это не так - ссылку пожалуйста.

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

>Могу. Более того, могу в любом другом языке. И не только я - любой программист может, кому это будет нужно.
Давай, вперед.

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

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

Вот так намного понятней.

То есть ссылка состоит из «указателя» в памяти и идентификатора типа того, на что он указывает.

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

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

Хорошо что я не использую такое чудовище. Надеюсь, и не придется.

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

Love5an, скажите, пожалуйста, Вы занимаетесь программированием на LISP профессионально? Является ли это основным родом Вашей деятельности? Спасибо.

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

Взаимодействие с другими языками сильно усложняется, указатели непредсказуемы (а с moving GC вообще бесполезны)...

Оказывается, в lua такой GC. А я, дурак и идиот, хотел ее использовать ;__;

Придется все-таки встраивать forth как скриптовый движок. Ну и ладно, мне он все равно больше нравился. Одна проблема - как заставить других на нем писать...

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

Love5an, скажите, пожалуйста, Вы занимаетесь программированием на каком-либо языке семейства LISP (Common Lisp, Scheme) профессионально? Является ли это основным родом Вашей деятельности? Спасибо.

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

Щас он тебе напишет, что не знает семейства LISP. И вообще будет упираться рогом, пока ты не напишешь lisp. ))))

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

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

Плюсофилы такие плюсофилы. Вместо того чтобы реально исследовать причины плохого перформанса занимаются какой-то магией вуду. Много ли вообще памяти надо выделять в обычном приложении? Может всеже ограничиться static и thread-local для локального хозяйства которым не надо обмениваться между подсистемами?

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

>Помимо прочего там есть любопытное с точки зрения методологии исследование GC vs malloc.

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

И эти их «5 times as much memory» — это как средняя температура по больнице: зависит от конкретного приложения: если приложение создает очень мало временных объектов, работать будет быстро и на «2 times as much memory», а если приложение будет интенсивно работать со строками в стиле Java, то тут и «10 times as much» может маловато оказаться.

Вообще исследования не читал, но приведенная цитата почему-то заставила меня задаться вопросом: автор для phoronix'а бенчмарки не гоняет на досуге?

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

>И ещё есть такая подстава в GC (сам я с ней не сталкивался) - это то, что финализация объекта может вызываться в произвольное время.

Именно поэтому Господь Бог создал unwind-protect и прочие finally для таких языков. В C++, как нетрудно убедиться, никаких finally нет, и, более того, в подобных конструкциях отсутствует необходимость.

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

> Вообще исследования не читал, но...

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

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