Ответ 1
Ваши предположения верны. Если есть группа указателей, указывающих друг на друга, но нет ссылки/указателя на любой член этой группы, группа будет обнаружена как недосягаемая сборщиком мусора и будет освобождена должным образом.
Но объяснение утечки памяти простое. Мы можем получить обертки list.Element
из списка, которые содержат указатели Element.next
и Element.prev
, не указанные в нем, для следующего и предыдущего элементов в список.
При удалении элемента из списка, если эти указатели не будут установлены в nil
, они будут содержать ссылки на следующие и предыдущие обертки элементов, включая значения, связанные с этими элементами.
См. этот пример:
var e2 *list.Element
func main() {
listTest()
fmt.Println(e2.Value)
// At this point we expect everything from the list to be
// garbage collected at any time, we only have reference to e2.
// If e2.prev and e2.next would not be set to nil,
// e1 and e3 could not be freed!
}
func listTest() {
l := list.New()
e1 := l.PushBack(1)
e2 = l.PushBack(2)
e3 := l.PushBack(3)
// List is now [1, 2, 3]
fmt.Println(e1.Value, e2.Value, e3.Value)
l.Remove(e2)
// Now list is [1, 3], it does not contain e2
}
В listTest()
мы создаем список с 3 элементами, и мы сохраняем 2-й элемент в глобальной переменной e2
. Затем мы удалим этот элемент. Теперь мы ожидаем, что кроме e2
(и значения, завернутого в него) все остальное получает сбор мусора, когда возвращается listTest()
, потому что список недоступен вне функции listTest()
. Да, у нас есть указатель в e2
на элемент, но e2
имеет (должен иметь) ничего общего с этим списком, поскольку мы его удалили.
Если указатели prev
и next
в e2
не будут установлены в nil
, значения, заключенные в отмеченные ими элементы, никогда не могут быть освобождены, рекурсивно. Но поскольку List.Remove()
правильно устанавливает значения в nil
, в приведенном выше примере e1
и e3
- со значениями, они будут освобождены (при следующем запуске коллекции мусора).