对于长时间运行的程序,STL 内存管理是否"reliable"?

Is STL memory management "reliable" for a long running program?

本文关键字:管理 内存 是否 STL reliable 长时间 运行 程序      更新时间:2023-10-16

我在 C++11 STL 中阅读了很多关于内存管理的 SO 帖子,但我真的找不到令人满意的答案。

的情况:我开发了一个长时间运行的服务器[它运行了大约4-6周]。目前,我使用了很多旧的C 代码char [x][y]或位于堆栈上的char [z]变量。

怀疑STL 内存管理是否仍然可靠,在一个运行数周并在超过 1000 万个线程的时间内提供服务的程序中广泛使用它,每个线程都会有很多 STL 操作。

更具体地说:我想将位于堆栈上的所有固定大小的变量重写为std::vector<std::string>std::string类型。

我的问题:

  1. 我可以完全安全地将我的程序重写为新的现代 STL 符号并摆脱旧的 C 代码吗?
  2. 在数百万个线程中运行这么长时间时是否存在任何内存碎片
  3. 性能如何?使用在堆栈上具有变量的旧 C 代码不会对性能产生任何影响。

编译器gcc 4.9.3

我可以完全安全地将我的程序重写为新的现代 STL 符号并摆脱旧的 C 代码吗?

首先,STL并不新鲜;它可以追溯到C++本身标准化之前。其次,我们称之为C++标准库。

第三,只要你的线程遵循C++的要求(即:不要以C++不允许的方式终止),并且你不泄漏内存,那么是的,你会没事的。

在数百万个线程中运行这么长时间时是否存在任何内存碎片?

您正在从存在于堆栈上的对象转向动态分配内存。当然,存在内存碎片的可能性。

这与C++标准库容器完全无关。这是使用动态分配的产物。

同样重要的是,如果您想使用更好的固定大小的堆栈数组,则可以使用std::array<char, ...>。再说一次,std::string具有小字符串优化的实现在很多情况下提供了一个很好的折衷方案,如果字符串低于某个最大大小,则放弃分配内存。

性能如何?使用在堆栈上具有变量的旧 C 代码不会对性能产生任何影响。

它使您的堆栈更长,考虑到 1000 万个线程,可能会导致您提交更多内存页。话又说回来,也许不是。

在任何情况下,当涉及到超线程应用程序时,内存分配始终是一个问题。就其性质而言,内存分配必须是可重入的。这意味着互斥锁等。

您可以设计分配和解分配内存的原子方法,但这往往需要分配固定大小。这样的事情往往有其自身的缺点。您可以有从中分配的线程本地内存池。所有这些都需要使用您自己的内存分配器。

但最重要的是...这些问题同样与专门使用C++标准库类型无关。这就是从静态内存到动态分配时发生的情况。无论您使用的是malloc/free库容器还是标准库容器,问题都出在动态分配上。

首先,我非常感谢所有的评论和Nicol的回答。他关于碎片的最后一条评论一针见血。

1)碎片化取决于这些细节的细节 数以百万计的线程正在做。

在深入分析了该项目之后,我意识到有数百万个内存分配和释放。

因此,我编写了自己的 STL 内存分配器,它:

  1. 具有维护所有指针的内部unordered_map
  2. 是多线程安全的。
  3. 根据第 1 点,它重用标记为自由的释放指针。
  4. 卸载控制器,请求的内存大小 [通过 STL] 与 16 字节大小对齐。

我的 STL 内存分配器记录所有请求,这是摘要 [摘要]:

Statistics:
Total allocated Memory: 813'041'344 bytes
Administrative Memory : 3'464'152 bytes
Available pointers    : 2'500
+-------------------------------------------------------------------------+
| Index | Aligned Memory Size | Max Used Pointers | Total Requested Count |
+-------------------------------------------------------------------------+
|      1|                   48|                296|             49'545'399|
|      2|                   64|                469|             73'226'993|
|      3|                   80|              1'167|             67'108'769|
|      4|                   96|                129|             12'864'168|
|      5|                  112|                281|              4'528'422|
|      6|                  128|                 64|              8'715'454|
|      7|                  144|                 74|              5'148'202|
|     10|                  192|                387|              1'313'920|
|     11|                  208|                 26|              1'311'779|
|     13|                  272|                 56|             11'574'551|
|     15|                  352|                368|              1'178'994|
|     18|                  512|                262|              3'224'044|
|     22|                  656|                  5|              2'586'081|        
+-------------------------------------------------------------------------+

传说:

  • Aligned Memory Size:每个请求的块对齐 16 字节 + 32 字节的维护数据。 E.q. 分配 1 个字节会产生一个实际大小为 48 字节的内存块。
  • Max Used Pointers:这是所有正在运行的线程同时使用此大小的内存块的数量。换句话说,这个内存[Size * Max Used Pointers]是从操作系统物理上分配的。
  • Total Requested Count:每次请求分配 [对齐大小] 时,此计数都会增加。

对我来说,这意味着我可以保存数百万个分配和释放,我不知道使用默认 STL 分配器会是什么样子。


2)我可以摆脱旧的C样式代码,我可以使用更多 方便的 STL 容器。


3)性能还可以。对我来说,这意味着我的分配器不是 最快的一个,但考虑到它是完全多线程安全的,并且 每秒服务数千个请求,完全足以满足我的要求 需要。


所以,答案是,[公平地说]我仍然不知道默认的 STL 内存分配器有多可靠,但由于上述事实,我至少得到了

内部发生的事情的线索。假设我的分配器没有错误[在运行很长时间并为数百万个请求提供服务后我可以期待]我可以为我关闭此案例。