Стандартная библиотека C и сборщик мусора Boehm

#c #linux #garbage-collection #g

#c #linux #сбор мусора #g

Вопрос:

Я хочу разработать многопоточное приложение на C (где в конечном итоге большая часть кода на C будет генерироваться самим приложением, которое можно рассматривать как язык, специфичный для домена высокого уровня) на Linux / AMD64 / Debian с GCC 4.6 (и, вероятно, последним стандартом C 11).

Я действительно хочу использовать консервативный сборщик мусора Boehm для всех моих распределений кучи, потому что я хочу распределять с new(GC) и никогда не беспокоиться об delete . Я предполагаю, что GC Boehm работает достаточно хорошо.

Основная мотивация для использования C (вместо C) — это все алгоритмы и коллекции std::map std::vector предоставляется стандартной библиотекой C .

GC Boehm предоставляет gc_allocator<T> шаблон (в его файле gc/gc_allocator.h).

Должен ли я переопределить operator ::new как Boehm’s one?

Или я должен использовать все шаблоны коллекции с явным аргументом шаблона распределителя, установленным на some gc_allocator ? Я не совсем понимаю роль второго аргумента шаблона (распределителя) для std::vector? Используется ли он для выделения векторных внутренних данных или для выделения каждого отдельного элемента?

А как насчет std::string -s? Как сделать их данные распределенными для общего доступа? Должен ли я иметь свою собственную строку, используя basic_string шаблон с gc_allocator ? Есть ли какой-нибудь способ получить внутренние массивы символов, выделенные с помощью GC_malloc_atomic not GC_malloc ?

Или вы советуете не использовать Boehm GC с приложением, скомпилированным на g ?

С уважением.

Комментарии:

1. Все зависит от того, насколько вы знакомы с C и хороши в нем. Можете ли вы написать приличную программу на C без использования delete , и понимаете ли вы, что new об этом следует говорить только в очень, очень особых обстоятельствах, и что указатели по большей части не нужны? Если вы понимаете все это и приходите к выводу, что вам требуется сборщик мусора, тогда, во что бы то ни стало, продолжайте. С другой стороны, если вы этого не сделаете, вы можете обнаружить, что идиоматичный современный C довольно хорош в удобном для пользователя детерминированном управлении памятью.

2. Хе-хе-хе — возможно, я здесь перегибаю палку, но мне кажется, что если вы знаете сбор мусора, но не очень хорошо знаете C , то это классическая ситуация «у меня есть молоток»… Почему бы не опубликовать типичный фрагмент кода, и мы сможем увидеть, как это сделать способом C ?

3. Хм. Вам действительно нужно выбрать один. Это разные языки. Когда вы генерируете код на C и делаете это правильно, я бы сказал, что вам не нужен сборщик мусора, и вы должны доказать, зачем он вам понадобился бы . (Ваш компилятор вполне может использовать один, но сейчас я говорю о результирующем коде.)

4. Честно говоря, я думаю, что слишком много вопросов. Лучше всего быть сфокусированным и конкретным. Если быть предельно честным, если вы не очень хорошо знакомы с механикой распределителя C , вам, вероятно, преждевременно разрабатывать инструмент генерации кода C , и вы должны хорошо понимать язык, на который вы переводите, прежде чем приступать к такому предприятию. Это всего лишь мое личное впечатление, и я желаю вам удачи и смелости в этом — я просто думаю, что изучение большего количества C на некоторое время было бы беспроигрышным для всех.

5. Проще говоря, вообще нет причин для new прямого использования и никогда не было причин для delete чего-либо. Судя по вашему сообщению, у меня осталось отчетливое впечатление, что вы совершенно не представляете, как вообще писать код на C , и не используете конструкции, предоставляемые в качестве стандарта, и используете GC просто потому, что это парадигма, к которой вы привыкли.

Ответ №1:

Чтобы частично ответить на мой собственный вопрос, следующий код

 // file myvec.cc
#include <gc/gc.h>
#include <gc/gc_cpp.h>
#include <gc/gc_allocator.h>
#include <vector>

class Myvec {
  std::vector<int,gc_allocator<int> > _vec;
public:
  Myvec(size_t sz=0) : _vec(sz) {};
  Myvec(const Myvecamp; v) : _vec(v._vec) {};
  const Myvecamp; operator=(const Myvec amp;rhs) 
    { if (this != amp;rhs) _vec = rhs._vec; return *this; };
  void resize (size_t sz=0) { _vec.resize(sz); };
  intamp; operator [] (size_t ix) { return _vec[ix];};
  const intamp; operator [] (size_t ix) const { return _vec[ix]; };
  ~Myvec () {};
};

extern "C" Myvec* myvec_make(size_t sz=0) { return new(GC) Myvec(sz); }
extern "C" void myvec_resize(Myvec*vec, size_t sz) { vec->resize(sz); }
extern "C" int myvec_get(Myvec*vec, size_t ix) { return (*vec)[ix]; }
extern "C" void myvec_put(Myvec*vec, size_t ix, int v) { (*vec)[ix] = v; }
  

при компиляции с g -O3 -Wall -c myvec.cc создается объектный файл с

  % nm -C myvec.o
                 U GC_free
                 U GC_malloc
                 U GC_malloc_atomic
                 U _Unwind_Resume
0000000000000000 W std::vector<int, gc_allocator<int> >::_M_fill_insert(__gnu_cxx::__normal_iterator<int*, std::vector<int, gc_allocator<int> > >, unsigned long, int constamp;)
                 U std::__throw_length_error(char const*)
                 U __gxx_personality_v0
                 U memmove
00000000000000b0 T myvec_get
0000000000000000 T myvec_make
00000000000000c0 T myvec_put
00000000000000d0 T myvec_resize
  

Таким образом, в сгенерированном коде нет простого malloc или ::operator new .

Таким образом, используя gc_allocator и new(GC) , я, по-видимому, могу быть уверен, что обычный ::opertor new or malloc не используется без моего ведома, и мне не нужно переопределять ::operator new


дополнения (январь 2017)

Для дальнейшего использования (спасибо Sergey Zubkov за упоминание об этом в Quora в комментарии) см. Также n2670 и <memory> и поддержку сборки мусора (например, std::declare_reachable, std::declare_no_pointers, std::pointer_safety и т. Д.). Однако это не было реализовано (за исключением тривиального, но приемлемого способа сделать это без операции) в текущем GCC или Clang на наименьший.