#java #c #garbage-collection #smart-pointers
#java #c #сбор мусора #интеллектуальные указатели
Вопрос:
Это просто академический вопрос (я бы никогда не стал этого делать в реальном коде):
Если бы я должен был использовать shared_ptr<> универсально в своем коде, было бы поведение эквивалентно языку, собранному с помощью gc, такому как Java?
Если нет, то как поведение будет отличаться от языка, встроенного в gc? Какая конструкция C даст эквивалентное поведение по сравнению с языком, встроенным в gc?
Примечание: В реальном кодировании я настоятельно предпочитаю использование RAII и строгое владение использованием любых интеллектуальных указателей. Я также знаю, что другие менее универсальные указатели, unique_ptr<> были бы более эффективными. Этот вопрос — всего лишь запрос на эквивалентность смарт-указателя.
Ответ №1:
Нет, было бы несколько важных отличий:
- Вы получите утечку памяти каждый раз, когда у вас есть циклическая ссылка. Сборщик мусора может обрабатывать циклы, подсчет ссылок — нет.
- Вы бы избежали каких-либо остановок или пауз, потому что сборка мусора никогда не происходит. С другой стороны, вы, вероятно, потратите больше общего процессорного времени на очистку ресурсов, потому что амортизированная стоимость случайной сборки мусора довольно низкая, а подсчет ссылок может быть относительно дорогостоящим, если вы делаете это для всего.
Очевидно, что первый пункт — убийца. Если вы это сделаете, многие ваши ресурсы не будут освобождены, и вы потеряете память, и ваше приложение будет вести себя не очень хорошо.
Какая конструкция C даст эквивалентное поведение по сравнению с языком, встроенным в gc?
Нет. В C нет сборщика мусора, потому что нет способа реализовать правильный и надежный. (Да, я знаю о GC Бема, и это хорошее приближение, но оно консервативно и обнаруживает не все ссылки, а только те, в которых он может быть уверен на 100%. В общей программе на C невозможно реализовать сборщик мусора, который просто работает ™)
Комментарии:
1. является ли shared_ptr потокобезопасным? тогда увеличение счетчика стало довольно дорогостоящим
2. Да по обоим пунктам. Приращения / уменьшения реализуются с использованием атомарных операций, поэтому там есть некоторые накладные расходы, потому что каждый из них должен блокировать шину памяти.
3. Java GC является правильным и надежным. Он способен находить и освобождать любой объект, на который нет ссылок. (это все равно может на мгновение остановить вашу программу при выполнении сбора, и это мешает вам использовать RAII и затрудняет контроль времени жизни ваших ресурсов, чем в C , но как GC он выполняет свою работу)
4. У вас есть какие-либо ссылки на
On the other hand, you'd likely spend more total CPU time cleaning up resources
. Кажется довольно смелым утверждением.5. @LokiAstari Программирование должно выполняться с учетом того , откуда берутся затраты. Например, приложения, в которых существует множество динамических распределений и освобождений, будут намного эффективнее в среде GC, поскольку стоимость GC пропорциональна набору живых объектов, то есть выполнение 1 или 1000
new
внутри замкнутого цикла окажет влияние на распределение только в том случае, еслиGC запускается, когда эти ссылки были удалены. Эти 1000 объектов, созданных и забытых, не окажут никакого влияния на производительность GC, в то время как они сильно повлияли бы без GC.
Ответ №2:
@jalf говорит об этом в своем ответе:
Вы бы избежали каких-либо остановок или пауз, потому что сборка мусора никогда не происходит.
Хотя интеллектуальные указатели (или любая схема подсчета ссылок) не имеют паузы во время сборки мусора, вы можете получить паузу, если обнулите последний внешний указатель на большую структуру данных и запустите каскад корректировок и финализаций количества ссылок для каждого узла в структуре данных. Хотя интеллектуальная реализация интеллектуального указателя может улучшить это, вы бы пожертвовали немедленным восстановлением … некоторые люди утверждают, что это преимущество интеллектуальных указателей.
Кроме того, каждый раз, когда вы присваиваете переменной, набираемой с помощью интеллектуального указателя, накладные расходы на выделение объекта больше.
Ответ №3:
Сборка мусора происходит всякий раз, когда GC решает, что это необходимо. shared_ptr
s не собираются. Объект, управляемый a shared_ptr
, будет уничтожен только в деструкторе a shared_ptr
. И, следовательно, вы точно знаете, когда память может и не может быть освобождена.
У вас все еще есть контроль над тем, когда уходит память shared_ptr
. У вас нет этого со сборщиком мусора (за исключением грубых команд, таких как включение / выключение или изменение его поведения). поведение).
Ответ №4:
Основное отличие заключается в том, что подсчет ссылок сам по себе не может освободить циклические структуры данных.
Тем не менее, многие случаи таких структур могут быть обработаны с помощью weak_ptr
соответствующего использования, а некоторые случаи могут быть обработаны путем делегирования ответственности за очистку объекту коллекции.
Однако самые несерьезные структуры спагетти, если они вам нужны (например, для математики), не могут иметь автоматическую очистку, реализованную только путем подсчета ссылок, потому что будут циклические подструктуры.
Приветствия и hth.,
Ответ №5:
Стоит отметить, что общий ptr намного больше, чем ссылка на Java. Как правило, это не имеет значения, но в некоторых ситуациях это может иметь значение.
В Java 6 64-разрядные JVM по-прежнему используют 32-разрядные ссылки для доступа к 32 ГБ кучи (это возможно, потому что объекты находятся на 8 байтовых границах) Однако общий ptr использует два указателя (каждый 8 байт в 64-разрядных приложениях), второй указатель ссылается на объект, который содержит счетчик. В libgcc он выделяет минимум 32 байта для любого объекта malloc / new . В общей сложности общий указатель может использовать 48 байт, что относительно больше, чем 4 байта. 44 байта ничего не изменят, но могут, если у вас их много.