Есть ли способ освободить неуправляемые ресурсы при сборке структуры Go?

У меня есть указатель на тип C, завернутый структурой Go, например:

type Wrapper struct {
    unmanaged *C.my_c_type
}

Тип C, в свою очередь, имеет следующие функции:

my_c_type* make_c_type();
void free_c_type(my_c_type *ct);

Есть ли способ гарантировать, что free_c_type вызывается всякий раз, когда завершается экземпляр Wrapper?

Ответ 1

Вы можете использовать runtime.SetFinalizer. Это позволяет запускать функцию очистки, когда объект выходит из области видимости. Это не гарантируется. Однако, освобождая память, это не имеет большого значения. Важно то, что для долгого процесса он, вероятно, будет держать мусор под контролем.

Вот несколько выдержек из документов (были удалены целые абзацы):

SetFinalizer устанавливает финализатор, связанный с x в f. Когда сборщик мусора находит недостижимый блок с ассоциированным финализатором, он очищает ассоциацию и запускает f (x) в отдельном goroutine. Это делает x доступным снова, но теперь без связанного финализатора. Предполагая, что SetFinalizer не вызывается снова, в следующий раз, когда сборщик мусора увидит, что x недоступен, он освободит x.

Финализатор для x планируется запустить в какое-то произвольное время после того, как x становится недоступным. Нет гарантии, что финализаторы будут запускаться до выхода программы, поэтому обычно они полезны только для освобождения ресурсов, не связанных с памятью, связанных с объектом во время длительной программы. Например, объект os.File может использовать финализатор для закрытия связанного файлового дескриптора операционной системы, когда программа отбрасывает os.File без вызова Close, но было бы ошибкой полагаться на финализатор для очистки встроенной памяти I/O, например bufio.Writer, потому что буфер не будет сброшен при выходе программы.

Один горутин последовательно выполняет все финализаторы для программы. Если финализатор должен работать в течение длительного времени, он должен сделать это, запустив новый goroutine.