Возможна ли утечка памяти в Python?

Приведите пожалуйста пример плохого кода на Python, содержащий утечку памяти. Я не очень хорошо понимаю что это такое, как такого можно "добиться". Разве сборщик мусора нужен нам не для того, чтобы утечек памяти не было?


Ответы (2 шт):

Автор решения: vadim vaduxa
import matplotlib.pyplot as plt
import random, psutil

def png(x, y) -> None:
    (fig, axs) = plt.subplots(1, 1, figsize=(17, 5))
    axs.plot(x, y)

if __name__ == '__main__':
    while True:  # RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).
        png(random.randint(1, 10), random.randint(1, 10))
        print('memory % used:', psutil.virtual_memory()[2])

out:

memory % used: 57.9
...
memory % used: 58.0
...
memory % used: 58.4
...
memory % used: 58.9
...
memory % used: 60.7
...
→ Ссылка
Автор решения: Bol4onok

В Python может быть утечка памяти если не использовать слабые ссылки при использовании циклических структур по типу связных списков. Хотя циклический сборщик мусора и должен следить за такими объектами, на практике, циклические объекты при неправильном удалении могут долго оставаться живыми. Например, если удалить голову у вышеупомянутого связного списка, то всё тело останется в памяти неосвобождённым, и доступа к нему не будет.

Простой пример:

import weakref


class Node:
    def __init__(self, data):
        self.prev = self.next = None
        self.data = data
    def __del__(self):
        print(f"Сработал Деструктор {self.data}")


class DoubleLinkedList:
    __slots__ = ("head", "tail",)

    def __init__(self):
        self.head = self.tail = None

    def append(self, data):
        newNode = Node(data)
        if self.head is None:
            self.head = self.tail = newNode
            self.head.prev = None
            self.tail.next = None
        else:
            self.tail.next = newNode
            newNode.prev = weakref.ref(self.tail)
            self.tail = newNode
            self.tail.next = None
        return self

obj = DoubleLinkedList()
obj.append(4).append(3).append(2).append(1)
del obj
input()

Вывод

Сработал Деструктор 4
Сработал Деструктор 3
Сработал Деструктор 2
Сработал Деструктор 1

Вывод если убрать weakref.ref

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

→ Ссылка