Сборка мусора в C ++ - почему? - PullRequest
45 голосов
/ 23 октября 2008

Я постоянно слышу, как люди жалуются, что в C ++ нет сборки мусора. Я также слышал, что Комитет по стандартам C ++ планирует добавить его в язык. Боюсь, я просто не вижу в этом смысла ... использование RAII с умными указателями устраняет необходимость в этом, верно?

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

Какие преимущества может предложить сборщик мусора опытному разработчику C ++?

Ответы [ 16 ]

67 голосов
/ 23 октября 2008

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

Мне их очень жаль. Серьезно.

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

Какие преимущества может предложить сборщик мусора опытному разработчику C ++?

Еще один инструмент.

Мэтт J написал это совершенно правильно в своем посте ( Сборка мусора в C ++ - почему? ): нам не нужны функции C ++, так как большинство из них могут быть написаны на C, и мы не не нужны функции C, так как большинство из них могут быть закодированы в Assembly и т. д. C ++ должен развиваться.

Как разработчик: мне наплевать на GC. Я попробовал оба RAII и GC, и я считаю RAII значительно лучше. Как сказал Грег Роджерс в своем посте ( Сборка мусора в C ++ - почему? ), утечки памяти не так страшны (по крайней мере, в C ++, где они редки, если C ++ действительно используется), чтобы оправдать GC вместо RAII. GC имеет недетерминированное освобождение / завершение и является просто способом написать код, который просто не заботится о конкретных вариантах памяти .

Это последнее предложение важно: важно написать код, который "позаботится о том, что все равно". Точно так же в C ++ RAII мы не заботимся об освобождении ресурсов, потому что RAII делает это за нас, или об инициализации объектов, потому что конструктор делает это за нас, иногда важно просто написать код, не заботясь о том, кто является владельцем какой памяти, и какой указатель (общий, слабый и т. д.) нам нужен для того или иного куска кода. Кажется, в C ++ требуется GC. (даже если я лично не вижу его)

Пример хорошего использования GC в C ++

Иногда в приложении появляются «плавающие данные». Представьте себе древовидную структуру данных, но на самом деле никто не является «владельцем» данных (и никто не заботится о том, когда именно они будут уничтожены). Несколько объектов могут использовать его, а затем отказаться от него. Вы хотите, чтобы он был освобожден, когда никто больше его не использует.

Подход C ++ использует умный указатель. На ум приходит повышение :: shared_ptr. Таким образом, каждый фрагмент данных принадлежит своему собственному общему указателю. Здорово. Проблема в том, что когда каждый кусок данных может ссылаться на другой кусок данных. Вы не можете использовать общие указатели, потому что они используют счетчик ссылок, который не будет поддерживать циклические ссылки (A указывает на B, а B указывает на A). Так что вы должны знать, много думать о том, где использовать слабые указатели (boost :: weak_ptr) и когда использовать общие указатели.

С помощью GC вы просто используете древовидные данные.

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

Заключение

В итоге, если все сделано правильно и совместимо с текущими идиомами C ++, GC станет еще одним хорошим инструментом для C ++ .

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

11 голосов
/ 23 октября 2008

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

Этот вопрос выглядит аналогично «что C ++ может предложить опытному разработчику сборок? Инструкции и подпрограммы устраняют необходимость в этом, верно?»

9 голосов
/ 23 октября 2008

С появлением хороших контроллеров памяти, таких как valgrind, я не вижу особого смысла в сборке мусора в качестве защитной сети «на всякий случай», мы забыли освободить что-то, тем более что это не очень помогает в управлении более общим случай ресурсов, кроме памяти (хотя они гораздо реже). Кроме того, явное выделение и освобождение памяти (даже с помощью умных указателей) встречается довольно редко в коде, который я видел, поскольку контейнеры обычно намного проще и лучше.

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

8 голосов
/ 23 октября 2008

Мотивирующим фактором для поддержки GC в C ++, по-видимому, является лямбда-программирование, анонимные функции и т. Д. Оказывается, что лямбда-библиотеки выигрывают от возможности распределять память, не заботясь об очистке. Преимущество для обычных разработчиков было бы проще, надежнее и быстрее компилировать лямбда-библиотеки.

GC также помогает симулировать бесконечную память; единственная причина, по которой вам нужно удалить POD, заключается в том, что вам нужно перезапускать память. Если у вас есть GC или бесконечная память, вам больше не нужно удалять POD.

7 голосов
/ 23 октября 2008

Какие преимущества может предложить сборщик мусора опытному разработчику C ++?

Не нужно выискивать утечки ресурсов в коде ваших менее опытных коллег.

7 голосов
/ 23 октября 2008

Я не понимаю, как можно утверждать, что RAII заменяет GC или значительно превосходит его. Есть много случаев, которые обрабатываются gc, с которыми RAII просто не может иметь дело вообще. Это разные звери.

Во-первых, RAII не является пуленепробиваемым: он работает против некоторых распространенных сбоев, которые распространены в C ++, но во многих случаях RAII вообще не помогает; он хрупок по отношению к асинхронным событиям (например, сигналы в UNIX). По сути, RAII полагается на область видимости: когда переменная выходит из области видимости, она автоматически освобождается (конечно, при условии правильной реализации деструктора).

Вот простой пример, где ни auto_ptr, ни RAII не могут вам помочь:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <memory>

using namespace std;

volatile sig_atomic_t got_sigint = 0;

class A {
        public:
                A() { printf("ctor\n"); };
                ~A() { printf("dtor\n"); };
};

void catch_sigint (int sig)
{
        got_sigint = 1;
}

/* Emulate expensive computation */
void do_something()
{
        sleep(3);
}

void handle_sigint()
{
        printf("Caught SIGINT\n");
        exit(EXIT_FAILURE);
}

int main (void)
{
        A a;
        auto_ptr<A> aa(new A);

        signal(SIGINT, catch_sigint);

        while (1) {
                if (got_sigint == 0) {
                        do_something();
                } else {
                        handle_sigint();
                        return -1;
                }
        }
}

Деструктор А никогда не будет вызван. Конечно, это искусственный и несколько надуманный пример, но подобная ситуация может действительно случиться; например, когда ваш код вызывается другим кодом, который обрабатывает SIGINT и который вы вообще не можете контролировать (конкретный пример: расширения mex в matlab). Это та же самая причина, почему, наконец, в Python не гарантирует выполнение чего-либо. В этом случае вам может помочь Gc.

Другие идиомы плохо сочетаются с этим: в любой нетривиальной программе вам понадобятся объекты с состоянием (здесь я использую слово «объект» в очень широком смысле, это может быть любая конструкция, разрешенная языком); если вам нужно управлять состоянием вне одной функции, вы не можете легко сделать это с помощью RAII (именно поэтому RAII не так полезен для асинхронного программирования). OTOH, gc просматривает всю память вашего процесса, то есть знает обо всех выделенных объектах и ​​может выполнять асинхронную очистку.

Также может быть намного быстрее использовать gc по тем же причинам: если вам нужно распределить / освободить много объектов (в частности, небольших объектов), gc значительно превзойдет RAII, если вы не напишите пользовательский распределитель, так как gc Можно выделить / убрать много объектов за один проход. Некоторые хорошо известные C ++ проекты используют gc, даже если производительность имеет значение (см., Например, Тим Свини о том, как использовать gc в Unreal Tournament: http://lambda -the-ultimate.org / node / 1277 ) GC в основном увеличивает пропускную способность за счет задержки.

Конечно, есть случаи, когда RAII лучше, чем gc; в частности, концепция gc в основном связана с памятью, и это не единственный ресурс. Такие вещи, как файл и т. Д. ... могут быть хорошо обработаны с RAII. Языки без обработки памяти, такие как python или ruby, имеют что-то вроде RAII для этих случаев, BTW (с оператором в python). RAII очень полезен, когда вам необходимо точно контролировать, когда ресурс освобождается, и это довольно часто, например, для файлов или блокировок.

7 голосов
/ 23 октября 2008

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

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

6 голосов
/ 25 октября 2008

Общепринято считать, что, поскольку в C ++ нет сборки мусора , встроенной в язык , вы не можете использовать сборку мусора в период C ++. Это нонсенс. Я знаю элитных программистов на C ++, которые, как само собой разумеющееся в работе, используют сборщик Boehm.

6 голосов
/ 23 октября 2008

Сборка мусора позволяет отложить решение о том, кому принадлежит объект.

C ++ использует семантику значений, поэтому в RAII объекты действительно повторно выбираются при выходе из области видимости. Это иногда называют "немедленным собранием".

Когда ваша программа начинает использовать ссылочную семантику (с помощью интеллектуальных указателей и т. Д.), Язык больше не поддерживает вас, вы остаетесь в здравом уме своей библиотеки интеллектуальных указателей.

Хитрость в GC заключается в выборе , когда объект больше не нужен.

5 голосов
/ 03 февраля 2012

Сборка мусора делает RCU синхронизацию без блокировки намного проще для правильной и эффективной реализации.

...