В связи с этим вопросом...
Заинтересовало, как же действительно работать с памятью в DLL, и, в частности, что остается с динамической памятью после освобождения DLL.
Создал простую DLL, в ней функция make_array, которая создает массив в динамической памяти. VC++, линковка статическая.
Дальше издеваюсь примерно так:
dll = LoadLibrary("Dll.dll");
memfunc m = (memfunc)GetProcAddress(dll,"make_array");
int * a = m(5);
for(int i = 0; i < 5; ++i) cout << a[i] << " "; cout << endl;
FreeLibrary(dll);
for(int i = 0; i < 5; ++i) cout << (a[i] *= a[i]) << " "; cout << endl;
dll = LoadLibrary("Dll.dll");
m = (memfunc)GetProcAddress(dll,"make_array");
for(int i = 0; i < 5; ++i) cout << (a[i] *= a[i]) << " "; cout << endl;
delete[] a;
a = m(6);
for(int i = 0; i < 5; ++i) cout << a[i] << " "; cout << endl;
Ну, т.е. я проверял, можно ли удалять память в программе, и остается ли память доступна после выгрузки DLL. Получается, если созданы одним компилятором - то все нормально, диспетчер памяти достаточно умен?
То же самое сделал с помощью Open Watcom - результат такой же.
Но если DLL от OW, а программа от VC++ - то работает все, кроме delete[]. Как, само собой, и следовало ожидать - из-за разных диспетчеров памяти.
Нет, я понимаю, что где память выделили - там и освобождайте :)
Но вот никак не могу ответить на два вопроса.
Если память выделена диспетчером памяти в DLL, то какой механизм используется, чтоб два диспетчера не перессорились? Для диспетчера в DLL выделяется какая-то своя область памяти? но при выгрузке DLL, как я понимаю, эта область остается не освобожденной?
Как реально работать с такими вещами, как интеллектуальные указатели? Типа, в DLL вызвать make_shared, результат которого потом использовать в программе - как гарантировать последнее удаление в DLL? Все, что придумывается - слишком уж искусственное. Разве что делетер вызывать из DLL?
P.S. Ну не работал я всерьез с DLL, глубоко не закапывался...
Ответ
Первое, что нужно знать - это рантайм. В случае с студией, есть MD и MT. В первом случае используется общий рантайм. И если в одной длл создать класс, а в другой удалить - все будет работать как ожидается. Если же рантайм у каждого свой - тут сложнее. Если создать в одном месте, а удалить в другом, то может отработать (если менеджер память достаточно умный), может отработать, но упадет потом (если длл и программа используют одинаковый рантайм), а может и сразу упасть (если рантаймы различны).
Как приложение работает с памятью? Обычно диспетчер памяти (который является частью рантайма), запрашивает о операционной системы большой кусок памяти (4Мб или даже 64Мб) и внутри него уже "нарезает" под мелкие объекты, которые создаются с помощью new/malloc. Когда приложение выгружается, эта память отдается назад операционной системе.
но при выгрузке DLL, как я понимаю, эта область остается не освобожденной?
если рантайм динамический, то менеждер памяти общий. И этой памятью заведует само приложение.
Если же используется статический рантайм, то тут все за программистом и рантаймом. Длл знает о том, что её выгружают и "должна" освободить память.
Как реально работать с такими вещами, как интеллектуальные указатели?
умные указатели в этом случае хорошие помощники. Нужно только убедиться, что у них правильно прописан "custom deleter" (даже не знаю, как это на русском - пользовательский удалятор? или пользовательская функция удаления?). В этом случае будет вызвана правильная функция в правильном менеждере памяти (в любом случае, у программиста есть возможность все сломать).
Но если DLL от OW, а программа от VC++ - то работает все, кроме delete[]. Как, само собой, и следовало ожидать - из-за разных диспетчеров памяти.
тут ситуация хитрая. когда вызывается delete[], то менеджер памяти должен знать, сколько удалять памяти. И где то это количество нужно сохранить. И я точно знаю, что студия и gcc используют различные, несовместимые модели. Поэтому, если такой указатель передать между длл с различными менджерами, может быть абсолютно все, что угодно.
Комментариев нет:
Отправить комментарий