Как уменьшить потребление памяти по умолчанию на С++?

У меня есть серверное приложение, написанное на С++. После запуска он использует около 480 КБ памяти на x86 Linux (Ubuntu 8.04, GCC 4.2.4). Я думаю, что 480 КБ - это чрезмерный объем памяти: сервер еще ничего не делает, клиенты не подключены к серверу. (См. Также мой комментарий ниже, в котором я объясняю, почему я думаю, что 480 КБ - большая часть памяти.) Единственное, что делает сервер во время инициализации, - это генерация одного или двух потоков, настройка нескольких сокетов и другие простые вещи, которые не очень интенсивные для памяти.

Обратите внимание, что я говорю об использовании реальной памяти, а не о VM. Я измерил его, запустив 100 экземпляров моего сервера на незанятом ноутбуке и измерив использование системной памяти с "свободным" до и после запуска экземпляров сервера. Я уже принял кэш файловой системы и все такое.

После некоторого тестирования оказалось, что что-то в среде выполнения С++ заставляет мой сервер использовать эту большую память, даже если сам сервер ничего не делает. Например, если я вставляю

getchar(); return 0;

сразу после

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

тогда использование памяти по-прежнему составляет 410 КБ на экземпляр!

Мое приложение зависит только от Curl и Boost. У меня есть большой опыт работы с программированием на С, и я знаю, что библиотеки C не имеют тенденций увеличивать потребление памяти, пока я их не использую.

Другие вещи, которые я нашел:

  • Простое приложение приветствия Clo потребляет около 50 КБ памяти.
  • Простое приложение hello world C, связанное с Curl, но в противном случае не используя Curl, также потребляет около 50 КБ памяти.
  • Простое приложение С++ для hello world (без Boost) потребляет около 100 КБ памяти.
  • Простое приветственное приложение С++, включающее некоторые заголовки Boost, но на самом деле не использует Boost, потребляет около 100 КБ памяти. Нет символов Boost при проверке исполняемого файла с помощью "nm".

Поэтому я заключу следующее:

  • Gcc выбрасывает неиспользуемые символы Boost.
  • Если мое приложение использует Boost, , то что-то в среде выполнения С++ (возможно, динамический компоновщик) заставляет его использовать много памяти. Но что? Как узнать, что это такое, и что я могу сделать с ними?

Я помню несколько дискуссий KDE несколько лет назад о проблемах динамического компоновщика С++. Тогда динамический компоновщик Linux С++ вызвал медленное время запуска приложений KDE С++ и большое потребление памяти. Насколько я знаю, эти проблемы были исправлены во время выполнения С++. Но может ли что-то подобное быть причиной чрезмерного потребления памяти, которое я вижу?

Ответы экспертов gcc/dynamic linking приветствуются.

Для тех, кому интересно, сервер, о котором идет речь, является агентом регистрации Phusion Passenger: https://github.com/FooBarWidget/passenger/blob/master/ext/common/LoggingAgent/Main.cpp

Ответы

Ответ 1

Среда выполнения C выделяет больше памяти, чем ваш процесс фактически использует как часть нормальной работы. Это связано с тем, что выделение памяти на уровне ядра чрезвычайно медленное и может выполняться только в блоках размера страницы (размер страницы обычно составляет 4 кбайт в ячейках x86, но он может быть больше и обычно составляет 8 кбайт или более на машинах x64).

Кроме того, когда среда выполнения C получает запрос на распределение, который он не может удовлетворить, он часто выделяет больше, чем это необходимо, опять же, чтобы удалить расходы на переезд в ядро ​​большую часть времени.

Наконец, если вы используете стимулы повышения, они, вероятно, зависят от некоторых компонентов STL, таких как std::vector. Эти компоненты выделяют пространство для элементов с помощью std::allocator<T>, который в некоторых случаях снова выделяет больше места, чем фактически используется. (В частности, структуры, основанные на node, такие как std::map, std::set и std::list обычно делают это, чтобы объединить узлы списка или дерева на одной странице памяти)

Короче говоря: не беспокойтесь об этом. Половина мега-памяти - это не то, что связано с воображением (по крайней мере в наши дни), и большинство из них, вероятно, просто амортизируют использование динамических функций распределения. Напишите ваш фактический сервер, и если он использует слишком много памяти, THEN рассмотрит способы сокращения использования памяти.

EDIT: если компонент повышения, который вы используете, является асионом, и вы используете сокеты, вы также должны знать, что там используется некоторая память, чтобы поддерживать буферы для сокетов тоже.

Ответ 2

Один из способов уменьшить потребление памяти - уменьшить размер стека потоков.

Что касается повышения, как прокомментировал Стив Джессоп, вы должны быть немного более конкретным, чем "повышение".

Ответ 3

Похоже, у вас возникла проблема с конфликтом базового адреса в некоторых динамических библиотеках загрузки. Если они требуют перемещения во время загрузки, они будут отображаться как частные скопированные копии.

Повторно запустите prelink по всей вашей системе. Если библиотека загружается с предпочтительным адресом, она будет отображаться как разделяемая память и будет стоить только одну копию кода независимо от того, сколько процессов использует ее.

BTW, prelink также было исправлением для KDE.