跨共享库边界分配和释放内存
Allocating and Deallocating memory across shared lib boundaries
当使用 windows dll 时,确定我们应该将内存 allocation/deallocation 限制在 dll 边界内,因为 dll 可能正在使用它自己的堆。
所以我们从 dll 导出分配器和自由函数。
IsomeInterface* getObject();
void freeObject(IsomeInterface *object);
这样对象的创建和删除将驻留在 dll 中。
linux 上的共享库是否也存在此问题?在处理共享库 (.so) 时,我们是否还需要注意将 allocation/deallocation 保留在共享库中。
我在下面做了一些快速试用,它适用于 linux。相同的示例不适用于 windows dll(如果 exe 和 dll 使用 /MD 编译以使用相同的堆,它将适用于 windows dll)。
std::vector<int> vec;
vec.push_back(42);
PassAVector(vec);
其中 PassAVector
驻留在共享库中
void PassAVector(std::vector<int> &vec)
{
vec.push_back(1); // These would cause reallocation
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
}
这是否意味着 unix 上的共享库与可执行文件共享堆(相当于 windows 上的 /MD 开关)?
是否可以编译(一些编译器标志)linux 上的共享库 (.so) 或可执行文件,使它们开始使用不同的堆(/MT 开关 windows) 这个问题浮出水面?
编辑:发现 这似乎表明只要编译器是 gcc 就可以跨边界传递 STL。
只要您坚持使用 Glibc 或其他“正常”分配器(jemalloc、tcmalloc 等),堆状态将由所有库共享,因此您将能够释放分配在某处的内存 malloc
任何你想要的地方。
理论上是可以绕过这个的。例如,某些库可能与 malloc
/free
的自定义实现链接(通过 -Bsymbolic
的符号脚本欺骗),它有自己的私有堆,因此不会与其他部分很好地交互你的程序。但我在现实生活中从未见过这样的事情。
STL 容器基于 malloc
/free
,因此也可以 pass/modify 它们跨库边界。当然,不同的库可能会使用不同的编译器和不同的不兼容版本的 STL(例如 libstdc++、libcxx 等)进行编译,但它们的 C++ 容器类型会有所不同,编译器根本不允许您将它们传递给不兼容的模块。
在 C++ 中,更改堆的正常方法是重载全局运算符 new/delete。
在 Windows 中,重载仅限于特定的 dll,因此您会遇到问题。
在 Linux 中,重载的全局运算符通常是真正的全局运算符 - 所以第一个获胜,但如果你真的想要它们中的特定堆,你可以实现(然后你需要导出的分配和 free-functions):
当使用 windows dll 时,确定我们应该将内存 allocation/deallocation 限制在 dll 边界内,因为 dll 可能正在使用它自己的堆。 所以我们从 dll 导出分配器和自由函数。
IsomeInterface* getObject();
void freeObject(IsomeInterface *object);
这样对象的创建和删除将驻留在 dll 中。
linux 上的共享库是否也存在此问题?在处理共享库 (.so) 时,我们是否还需要注意将 allocation/deallocation 保留在共享库中。 我在下面做了一些快速试用,它适用于 linux。相同的示例不适用于 windows dll(如果 exe 和 dll 使用 /MD 编译以使用相同的堆,它将适用于 windows dll)。
std::vector<int> vec;
vec.push_back(42);
PassAVector(vec);
其中 PassAVector
驻留在共享库中
void PassAVector(std::vector<int> &vec)
{
vec.push_back(1); // These would cause reallocation
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
}
这是否意味着 unix 上的共享库与可执行文件共享堆(相当于 windows 上的 /MD 开关)?
是否可以编译(一些编译器标志)linux 上的共享库 (.so) 或可执行文件,使它们开始使用不同的堆(/MT 开关 windows) 这个问题浮出水面?
编辑:发现
只要您坚持使用 Glibc 或其他“正常”分配器(jemalloc、tcmalloc 等),堆状态将由所有库共享,因此您将能够释放分配在某处的内存 malloc
任何你想要的地方。
理论上是可以绕过这个的。例如,某些库可能与 malloc
/free
的自定义实现链接(通过 -Bsymbolic
的符号脚本欺骗),它有自己的私有堆,因此不会与其他部分很好地交互你的程序。但我在现实生活中从未见过这样的事情。
STL 容器基于 malloc
/free
,因此也可以 pass/modify 它们跨库边界。当然,不同的库可能会使用不同的编译器和不同的不兼容版本的 STL(例如 libstdc++、libcxx 等)进行编译,但它们的 C++ 容器类型会有所不同,编译器根本不允许您将它们传递给不兼容的模块。
在 C++ 中,更改堆的正常方法是重载全局运算符 new/delete。
在 Windows 中,重载仅限于特定的 dll,因此您会遇到问题。
在 Linux 中,重载的全局运算符通常是真正的全局运算符 - 所以第一个获胜,但如果你真的想要它们中的特定堆,你可以实现(然后你需要导出的分配和 free-functions):