Физические Утечки
Ошибки, которые устраняет GC, кажутся (по крайней мере, внешнему наблюдателю) вещами, которые программист, который хорошо знает свой язык, библиотеки, концепции, идиомы и т. Д., Не сделал бы. Но я могу ошибаться: действительно ли ручная обработка памяти сложна?
Исходя из конца C, который делает управление памятью как можно более ручным и четким, чтобы мы сравнивали крайности (C ++ в основном автоматизирует управление памятью без GC), я бы сказал «не совсем» в смысле сравнения с GC, когда оно приходит к утечкам . Начинающий, а иногда и профессионал может забыть написать free
для данного malloc
. Это определенно происходит.
Тем не менее, существуют такие инструменты, как valgrind
обнаружение утечек, которые сразу же обнаруживают при выполнении кода, когда / где такие ошибки происходят вплоть до точной строки кода. Когда это интегрировано в CI, становится практически невозможно объединить такие ошибки и легко исправить их. Так что это никогда не имеет большого значения в любой команде / процессе с разумными стандартами.
Конечно, могут быть некоторые экзотические случаи выполнения, которые находятся под радаром тестирования, когда их free
не удалось вызвать, возможно, при обнаружении скрытой внешней ошибки ввода, такой как поврежденный файл, в этом случае система может пропустить 32 байта или что-то в этом роде. Я думаю, что это, безусловно, может произойти даже при довольно хороших стандартах тестирования и средствах обнаружения утечек, но было бы не так критично утекать немного памяти в то, что почти никогда не происходит. Мы увидим гораздо более серьезную проблему, когда мы можем утечь огромные ресурсы даже в общих путях выполнения ниже, так как GC не сможет это предотвратить.
Также трудно без чего-то, напоминающего псевдо-форму GC (например, подсчет ссылок), когда время жизни объекта необходимо продлить для какой-либо формы отложенной / асинхронной обработки, возможно, другим потоком.
Висячие указатели
Реальная проблема с более ручными формами управления памятью не утечка для меня. Сколько собственных приложений, написанных на C или C ++, мы знаем о том, что они действительно негерметичны? Является ли ядро Linux негерметичным? MySQL? CryEngine 3? Цифровые аудио рабочие станции и синтезаторы? Утечка Java VM (это реализовано в нативном коде)? Photoshop?
Во всяком случае, я думаю, что когда мы оглядываемся вокруг, самыми распространенными являются приложения, написанные с использованием схем GC. Но прежде, чем это будет принято за сбор мусора, нативный код имеет существенную проблему, которая вообще не связана с утечками памяти.
Вопрос для меня всегда был в безопасности. Даже когда мы free
запоминаем через указатель, если есть другие указатели на ресурс, они станут висящими (недействительными) указателями.
Когда мы пытаемся получить доступ к пуанте этих висячих указателей, мы в конечном итоге сталкиваемся с неопределенным поведением, хотя почти всегда нарушение segfault / access приводит к серьезному, немедленному падению.
Все те нативные приложения, которые я перечислил выше, потенциально имеют неясный крайний случай или два, которые могут привести к сбою, главным образом, из-за этой проблемы, и определенно есть значительная доля некачественных приложений, написанных на нативном коде, которые очень интенсивны при сбое, и часто во многом из-за этой проблемы.
... и это потому, что управление ресурсами сложно, независимо от того, используете ли вы GC или нет. Практическая разница часто бывает либо утечка (GC), либо сбой (без GC) перед лицом ошибки, приводящей к неэффективному управлению ресурсами.
Управление ресурсами: Сборка мусора
Сложное управление ресурсами - сложный, ручной процесс, несмотря ни на что. GC не может ничего автоматизировать здесь.
Давайте рассмотрим пример, где у нас есть этот объект «Джо». Джо упоминается рядом организаций, членом которых он является. Каждый месяц или около того они получают членский взнос со своей кредитной карты.
У нас также есть одна ссылка на Джо, чтобы контролировать его жизнь. Допустим, как программисты, нам больше не нужен Джо. Он начинает приставать к нам, и нам больше не нужны эти организации, которым он принадлежит, чтобы тратить их время на общение с ним. Поэтому мы пытаемся стереть его с лица земли, удалив его ссылку на спасательный круг.
... но подождите, мы используем сборщик мусора. Каждое сильное упоминание о Джо будет держать его рядом. Поэтому мы также удаляем ссылки на него из организаций, к которым он принадлежит (отписываясь от него).
... кроме упс, мы забыли отменить подписку на журнал! Теперь Джо остается в памяти, приставая к нам и используя ресурсы, и журнальная компания также заканчивает тем, что продолжает обрабатывать членство Джо каждый месяц.
Это основная ошибка, которая может вызвать утечку многих сложных программ, написанных с использованием схем сбора мусора, и начать использовать все больше и больше памяти, чем дольше они работают, и, возможно, все больше и больше обработки (повторяющаяся подписка на журнал). Они забыли удалить одну или несколько из этих ссылок, что сделало невозможным сборщик мусора, пока вся программа не будет закрыта.
Программа не падает, однако. Это совершенно безопасно. Это просто продолжит ломать память, а Джо все еще будет задерживаться. Для многих приложений такой вид утечек, когда мы просто добавляем все больше памяти / обработки для решения этой проблемы, может быть гораздо предпочтительнее жесткого сбоя, особенно с учетом того, сколько памяти и вычислительной мощности имеют наши машины сегодня.
Управление ресурсами: руководство
Теперь давайте рассмотрим альтернативу, где мы используем указатели на Джо и ручное управление памятью, например так:
Эти синие ссылки не управляют жизнью Джо. Если мы хотим удалить его с лица земли, мы вручную просим его уничтожить, вот так:
Теперь это обычно оставляет повсюду повисшие указатели, поэтому давайте удалим указатели на Джо.
... упс, мы снова совершили ту же ошибку и забыли отписаться от подписки на журнал Джо!
За исключением теперь у нас есть висячий указатель. Когда подписка на журнал пытается обработать ежемесячную плату Джо, весь мир взорвется - как правило, мы сразу же переживаем тяжелую аварию.
Эта та же самая ошибка неэффективного управления ресурсами, когда разработчик забыл вручную удалить все указатели / ссылки на ресурс, может привести к множеству сбоев в собственных приложениях. Как правило, они не занимают память больше, чем дольше работают, потому что в этом случае они часто будут зависать.
Реальный мир
Теперь приведенный выше пример использует смехотворно простую диаграмму. В реальном приложении могут потребоваться тысячи изображений, сшитых вместе, чтобы покрыть весь граф, с сотнями различных типов ресурсов, хранящихся в графе сцены, ресурсами GPU, связанными с некоторыми из них, ускорителями, привязанными к другим, наблюдателями, распределенными по сотням плагинов. наблюдение за множеством типов объектов в сцене на предмет изменений, наблюдатели, наблюдающие за наблюдателями, аудио, синхронизированные с анимацией и т. д. Таким образом, может показаться, что легко избежать ошибки, которую я описал выше, но, как правило, это далеко не так просто в реальном мире. производственная кодовая база для сложного приложения, охватывающего миллионы строк кода.
Вероятность того, что кто-то когда-нибудь будет неэффективно управлять ресурсами где-то в этой кодовой базе, имеет тенденцию быть довольно высокой, и эта вероятность одинакова с GC или без него. Основное различие заключается в том, что произойдет в результате этой ошибки, что также влияет на то, насколько быстро эта ошибка будет обнаружена и исправлена.
Крушение против утечки
Теперь какой из них хуже? Немедленное падение или тихая утечка памяти, когда Джо просто таинственно задерживается?
Большинство может ответить на последний вопрос, но допустим, что это программное обеспечение рассчитано на работу в течение нескольких часов подряд, возможно, дней, и каждый из этих добавлений Джо и Джейн, которые мы добавляем, увеличивает использование программного обеспечения памятью на гигабайт. Это не программное обеспечение для решения критически важных задач (сбои на самом деле не убивают пользователей), а критическое для производительности.
В этом случае серьезный сбой, который сразу же обнаруживается при отладке, указывая на допущенную вами ошибку, на самом деле может быть предпочтительнее, чем просто утечка программного обеспечения, которое может даже оказаться под радаром вашей процедуры тестирования.
С другой стороны, если это программное обеспечение для решения критически важных задач, где производительность не является целью, а не происходит сбой любыми возможными способами, то утечка может быть предпочтительнее.
Слабые ссылки
Существует своего рода гибрид этих идей, доступных в схемах GC, известных как слабые ссылки. При слабых ссылках у нас могут быть все эти организации со слабыми ссылками на Джо, но мы не можем помешать его удалению, когда сильная ссылка (владелец / спасательный круг Джо) исчезнет. Тем не менее, благодаря этим слабым ссылкам мы можем обнаружить, что Джо больше не существует, что позволяет нам легко получать воспроизводимые ошибки.
К сожалению, слабые ссылки используются не так часто, как их, вероятно, следует использовать, поэтому часто многие сложные приложения GC могут быть подвержены утечкам, даже если они потенциально гораздо менее аварийны, чем сложные приложения C, например
В любом случае, будет ли GC облегчать или усложнять вашу жизнь, зависит от того, насколько важно для вашего программного обеспечения избегать утечек, и имеет ли оно дело со сложным управлением ресурсами такого рода.
В моем случае я работаю в области, критически важной для производительности, где ресурсы занимают сотни мегабайт в гигабайты, и не освобождают эту память, когда пользователи запрашивают выгрузку из-за ошибки, подобной приведенной выше, на самом деле может быть менее предпочтительным для сбоя. Сбои легко обнаружить и воспроизвести, что делает их часто любимой ошибкой программиста, даже если это наименее любимый пользователь, и многие из этих сбоев будут обнаружены с помощью вменяемой процедуры тестирования еще до того, как они достигнут пользователя.
Во всяком случае, это различия между ГХ и ручным управлением памятью. Чтобы ответить на ваш непосредственный вопрос, я бы сказал, что ручное управление памятью сложно, но оно имеет мало общего с утечками, и как ГХ, так и ручные формы управления памятью все еще очень трудны, когда управление ресурсами нетривиально. GC, возможно, имеет более хитрое поведение, когда программа работает нормально, но потребляет все больше и больше ресурсов. Ручная форма менее хитрая, но она может потерпеть крах и погореть с ошибками, подобными показанным выше.