如何减少默认C++内存消耗?

发布于 2024-10-02 08:16:23 字数 1549 浏览 4 评论 0原文

我有一个用 C++ 编写的服务器应用程序。启动后,它在 x86 Linux(Ubuntu 8.04、GCC 4.2.4)上使用约 480 KB 内存。我认为 480 KB 的内存过多:服务器甚至还没有做任何事情,没有客户端连接到服务器。 (另请参阅下面的评论,其中解释了为什么我认为 480 KB 是很大的内存。) 服务器在初始化期间所做的唯一事情是生成一两个线程、设置一些套接字以及其他不是非常占用内存的简单事情。

请注意,我谈论的是实际内存使用情况,而不是虚拟机大小。我通过在一台闲置笔记本电脑上启动 100 个服务器实例并在启动服务器实例之前和之后测量“free”系统内存使用情况来进行测量。我已经考虑了文件系统缓存和类似的事情。

经过一些测试后,即使服务器本身没有执行任何操作,C++ 运行时中的某些内容也会导致我的服务器使用这么多内存。例如,如果我

getchar(); return 0;

立即

int main(int argc, char *argv[]) {

插入,那么每个实例的内存使用量仍然是 410 KB!

我的应用程序仅依赖于 Curl 和 Boost。我在 C 编程方面有相当多的经验,并且我知道 C 库在我使用它们之前不会增加内存消耗。

我发现的其他事情:

  • 一个简单的 hello world C 应用程序消耗大约 50 KB 的内存。
  • 链接到 Curl 但不使用 Curl 的简单 hello world C 应用程序也会消耗大约 50 KB 的内存。
  • 一个简单的 hello world C++ 应用程序(无 Boost)消耗大约 100 KB 内存。
  • 一个简单的 hello world C++ 应用程序包含一些 Boost 标头,但实际上并不使用 Boost,消耗大约 100 KB 的内存。使用“nm”检查可执行文件时没有升压符号。

因此我的结论如下:

  1. Gcc 丢弃未使用的 Boost 符号。
  2. 如果我的应用程序使用 Boost,那么 C++ 运行时中的某些内容(可能是动态链接器)会导致它使用大量内存。但是什么?我如何找出这些东西是什么,以及我能对它们做什么?

我记得几年前一些关于 C++ 动态链接器问题的 KDE 讨论。当时的 Linux C++ 动态链接器导致 KDE C++ 应用程序启动速度慢且内存消耗大。据我所知,这些问题已在 C++ 运行时中得到解决。但类似的原因可能是我看到的内存消耗过多的原因吗?

非常感谢 gcc/动态链接专家的回答。

对于那些好奇的人,有问题的服务器是 Phusion Passenger 的日志代理: https://github.com/FooBarWidget/passenger/blob/master/ext/common/LoggingAgent/Main.cpp

I have a server application written in C++. After startup, it uses about 480 KB of memory on x86 Linux (Ubuntu 8.04, GCC 4.2.4). I think 480 KB is an excessive amount of memory: the server isn't even doing anything yet, no clients have been connected to the server. (See also my comment below in which I explain why I think 480 KB is a lot of memory.)
The only things the server does during initialization is spawning one or two threads, setting up a few sockets, and other simple things that aren't very memory-intensive.

Note that I'm talking about real memory usage, not VM size. I measured it by starting 100 instances of my server on an idle laptop and measuring the system memory usage with 'free' before and after starting the server instances. I've already taken filesystem cache and things like that into account.

After some testing it would appear that something in the C++ runtime is causing my server to use this much memory even if the server itself doesn't do anything. For example, if I insert

getchar(); return 0;

right after

int main(int argc, char *argv[]) {

then the memory usage is still 410 KB per instance!

My application depends only on Curl and Boost. I have a fair amount of experience with C programming and I know C libraries don't tend to increase memory consumption until I use them.

Other things that I've found:

  • A simple hello world C app consumes about 50 KB of memory.
  • A simple hello world C app linked to Curl, but otherwise not using Curl, consumes about 50 KB of memory as well.
  • A simple hello world C++ app (no Boost) consumes about 100 KB of memory.
  • A simple hello world C++ app that includes some Boost headers, but does not actually use Boost, consumes about 100 KB of memory. No Boost symbols when inspecting the executable with 'nm'.

My conclusion is therefore as follows:

  1. Gcc throws away unused Boost symbols.
  2. If my app uses Boost, then something in the C++ runtime (probably the dynamic linker) causes it to use a lot of memory. But what? How do I find out what these things are, and what can I do about them?

I remember some KDE discussions several years ago about C++ dynamic linker issues. The Linux C++ dynamic linker back then caused slow startup time in KDE C++ apps and large memory consumption. As far as I know those issues have since been fixed in C++ runtimes. But could something similar be the cause of the excessive memory consumption I'm seeing?

Answers from gcc/dynamic linking experts are greatly appreciated.

For those who are curious, the server in question is Phusion Passenger's logging agent: https://github.com/FooBarWidget/passenger/blob/master/ext/common/LoggingAgent/Main.cpp

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

柠檬色的秋千 2024-10-09 08:16:23

C 运行时分配的内存多于进程正常操作时实际使用的内存。这是因为在内核级别分配内存非常慢,并且只能在页面大小的块中完成(页面大小在 x86 机器上通常为 4kb,但它可以更大,在 x64 机器上通常为 8kb 或更多)。

此外,当 C 运行时收到它无法满足的分配请求时,它通常会分配比所需的更多的资源,以消除大部分时间访问内核的费用。

最后,如果您使用 boost Goodies,它们可能依赖于一些 STL 组件,例如 std::vector。这些组件使用 std::allocator为元素分配空间,在某些情况下,这将再次分配比实际使用更多的空间。 (特别是基于节点的结构,例如 std::mapstd::setstd::list 通常这样做是为了将列表或树的节点一起位于同一内存页上)

长话短说:不用担心这一点。无论怎么想,半兆内存都不算多(至少现在如此),而且其中大部分可能只是摊销动态分配函数的使用。写下您的实际服务器,如果它使用了太多内存,那么看看减少内存使用的方法。

编辑:如果您使用的 boost 组件恰好是 asio,并且您正在使用套接字,您还应该知道为了维护套接字的缓冲区也会消耗一些内存。

The C runtime allocates more memory than your process actually uses as part of normal operation. This is because allocating memory at the kernel level is extremely slow, and can only be done in page sized blocks (Page size is typically 4kb on x86 boxes, but it can be larger, and is usually 8kb or more on x64 machines).

Furthermore, when the C runtime receives an allocation request it cannot satisfy, it will often allocate more than is necessary, again, to remove the expense of going to the kernel most of the time.

Finally, if you're using boost goodies, they probably depend on some STL components, such as std::vector. These components allocate space for elements using std::allocator<T>, which in some instances will again allocate more space than is actually used. (In particular, node-based structures like std::map, std::set, and std::list usually do this to put the nodes of the list or tree together on the same memory page)

Long story short: Don't worry about this. Half a meg of memory isn't a lot by any stretch of the imagination (At least nowadays), and most of that is probably just amortizing the use of dynamic allocation functions. Write up your actual server, and if it's using too much memory, THEN look at ways of reducing memory usage.

EDIT: If the component of boost you're using happens to be asio, and you're using sockets, you should also know there's some memory consumed in order to maintain buffers for sockets too.

思念绕指尖 2024-10-09 08:16:23

减少内存消耗的一种方法是减少线程堆栈大小。

关于boost,正如Steve Jessop评论的那样,你必须比“boost”更具体一点。

One way to reduce the memory consumption is to reduce thread stack size.

Regarding boost, as Steve Jessop commented, you have to be a bit more specific than "boost".

不及他 2024-10-09 08:16:23

听起来您的某些动态加载库存在基址冲突问题。如果它们在加载期间需要重新定位,它们将被映射为私有固定副本。

在整个系统中重新运行 prelink。如果库在其首选地址加载,它将被映射为共享内存,并且无论有多少进程正在使用它,都只需要一份代码副本。

顺便说一句,prelink 也是 KDE 的修复程序。

Sounds like you have a problem with a base address conflict on some of your dynamic load libraries. If they require relocation during load, they will be mapped in as private fixed-up copies.

Re-run prelink across your entire system. If the library loads at its preferred address, it will be mapped as shared memory and only cost one copy of the code no matter how many processes are using it.

BTW, prelink was also the fix for KDE.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文