Утечка памяти в срезе Голанга - PullRequest
4 голосов
/ 07 марта 2019

Я только начал учиться идти, проходя трюки с ломтиками, пара моментов очень запутанная. Может ли кто-нибудь помочь мне уточнить.

Для вырезания элементов в срез его дано

Подход 1:

a = append(a[:i], a[j:]...)

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

Подход 2:

copy(a[i:], a[j:])
for k, n := len(a)-j+i, len(a); k < n; k++ {
    a[k] = nil // or the zero value of T
}
a = a[:len(a)-j+i]

Может ли кто-нибудь помочь мне понять, как происходят утечки памяти. Я понял, что sub slice будет поддерживаться основным массивом. Моя мысль не зависит от указателя или нет, мы всегда должны следовать подходу 2.

обновить после ответа @icza и @Volker ..

Допустим, у вас есть структура

type Books struct {
    title   string
    author  string
}

var Book1 Books
var Book2 Books 

    /* book 1 specification */
    Book1.title = "Go Programming"
    Book1.author = "Mahesh Kumar"

    Book2.title = "Go Programming"
    Book2.author = "Mahesh Kumar"

    var bkSlice = []Books{Book1, Book2}
    var bkprtSlice = []*Books{&Book1, &Book2}

сейчас делает

bkSlice = bkSlice[:1]

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

bkSlice[1] = Books{}

так что это будет GCed. Я понял, что указатели нужно обнулять, так как срез будет содержать ненужные ссылки на объекты вне резервного массива.

1 Ответ

4 голосов
/ 07 марта 2019

Самое простое можно продемонстрировать с помощью простого выражения среза.

Давайте начнем с среза *int указателей:

s := []*int{new(int), new(int)}

Этот срез имеет вспомогательный массив длиной 2, и он содержит 2 не nil указателя, указывающих на выделенные целые числа (вне резервного массива).

Теперь, если мы изменим этот фрагмент:

s = s[:1]

Длина станет 1.Массив поддержки (содержащий 2 указателя) не затрагивается, он содержит 2 действительных указателя.Несмотря на то, что мы сейчас не используем 2-й указатель, поскольку он находится в памяти (это резервный массив), указанный сборщик (который является пространством памяти для хранения значения int) не может быть освобожден сборщиком мусора.

То же самое происходит, если вы «вырезаете» несколько элементов из середины.Если исходный фрагмент (и его резервный массив) был заполнен не nil указателями, и если вы не обнуляете их (nil), они будут сохранены в памяти.

Почему это не проблема с не указателями?

На самом деле, это проблема со всеми типами указателей и заголовков (например, срезами и строками), а не только с указателями.

Если у вас будет срез типа []int вместо []*int, то нарезка будет просто «скрывать» элементы типа int, которые должны оставаться в памяти как часть резервного массива независимо от того,есть фрагмент, который содержит это или нет. Элементы не являются ссылками на объекты, хранящиеся за пределами массива, , в то время как указатели ссылаются на объекты, находящиеся вне массива.

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

Обновление:

Если у вас есть фрагмент структур:

var bkSlice = []Books{Book1, Book2}

Если вы нарезаете его как:

bkSlice = bkSlice[:1]

Book2 станет недоступным через bkSlice, но все еще будет в памяти (как часть резервного массива).

Вы не можете nil это, потому что nil не является допустимым значениемдля структур.Однако вы можете присвоить ему нулевое значение следующим образом:

bkSlice[1] = Book{}
bkSlice = bkSlice[:1]

Обратите внимание, что значение структуры Books все еще будет в памяти, являясь вторым элементом массива поддержки,но эта структура будет нулевым значением и, таким образом, не будет содержать строковых ссылок, таким образом, оригинальный автор книги и строки заголовка могут быть собраны сборщиком мусора (если никто не ссылается на них; точнее, фрагмент байта ссылается из заголовка строки).

Общее правило является "рекурсивным": вам нужно только обнулить элементы, которые ссылаются на память, расположенную за пределами резервного массива. Так что если у вас есть фрагмент структур, которые имеют, например, int поля, вам не нужно обнулять его, на самом деле это просто ненужная дополнительная работа.Если в структуре есть поля, которые являются указателями, или срезами, или, например, другой тип структуры, который имеет указатели или срезы и т. Д., То вам следует обнулить его, чтобы удалить ссылку на память вне резервного массива.

...