Python утечка памяти в ctypes 'c_char_p' - PullRequest
2 голосов
/ 18 февраля 2020

Я занимаюсь разработкой библиотеки Python для криптографии. Я хотел оптимизировать свою библиотеку, написав основные классы на C ++ с GMP. Я написал свои классы C ++ и написал extern методы для использования основных арифметических c операций: сложение, вычитание и т. Д. c ... Эти методы возвращают результаты в виде char *, чтобы избежать проблем приведения. Я собрал DLL своей библиотеки и объявил методы в оболочке Python с ctypes. Я заметил, что после каждой арифметической операции c с огромными числами память росла в геометрической прогрессии. Я искал проблемы в моей реализации C ++, но не было никаких проблем благодаря сборщику мусора C ++. Я искал возможное решение, поэтому обнаружил, что мне нужно реализовать метод C ++, чтобы освободить память для строки, созданной DLL. Поэтому я написал этот простой метод:

extern "C" {

    __declspec(dllexport) void free_memory(char * n)
    {
        free(n);
    }
    ...
}

Я реализовал этот код в оболочке Python, чтобы освободить память, выделенную DLL:

import os
import ctypes

DIR_PATH = os.path.dirname(os.path.realpath(__file__))
NUMERIC = ctypes.CDLL(DIR_PATH + "/numeric.dll")
...
NUMERIC.free_memory.argtypes = [ctypes.c_void_p]
NUMERIC.free_memory.restype = None

def void_cast(n):
    a = ctypes.cast(n, ctypes.c_char_p)
    res = ctypes.c_char_p(a.value)
    NUMERIC.free_memory(a)
    return res

То же самое с res = ctypes.c_char_p (a.value) Я создаю новую переменную, которая больше не указывает на a. Таким образом, я правильно удаляю a, используя метод DLL, но у меня все еще есть проблемы утечки памяти. Как будто сборщик мусора Python неправильно освобождает память для строк типа c_char_p. В предыдущей реализации я использовал только Python и библиотеку gmpy2, поэтому все числа были преобразованы в mpz или mpq. Я проверил потребление памяти с помощью пакета memory_profiler. Я создал 40 объектов типа проективной точки, определенных на эллиптической кривой, и вычислил произведения i*P, с i от 1 до 40. С gmpy2 было использовано около 70 МБ. Вместо этого при использовании ctypes с классами в C ++ потребление памяти возросло до 1,5 ГБ. Очевидно, что что-то не так, особенно когда меняются только базовые классы, которые имеют дело с арифметическими c операциями. Как правильно освободить память без проблем с утечкой памяти? Я поместил пример метода extern для вычисления арифметической операции c, но я уже проверил, что проблема заключается только в правильном освобождении памяти с помощью функции free_memory и переназначении строки так, чтобы сборщик мусора Python освободит строку при необходимости.

extern "C" {
    __declspec(dllexport) const char* rat_add(const char * n, const char * m)
    {
        return (RationalNum(n) + RationalNum(m)).getValue();
    }
}

Заранее спасибо и хорошего дня.

PS: Ясно, что в C ++ я правильно реализовал метод деструктора, чтобы освободить пространство mpz_t и mpq_t созданные объекты.

1 Ответ

1 голос
/ 18 февраля 2020

Проблема в этой строке:

res = ctypes.c_char_p(a.value)

Это создает копию a.value и устанавливает res в c_char_p, который указывает на копию. Однако Python не выполняет управление памятью для ctypes указателей, поэтому копия будет утечка!

Утечка должна быть устранена, если заменить вышеуказанную строку на:

res = bytes(memoryview(a.value))

Это также создает копию, но res будет настоящим Python объектом.

...