2010-11-14 23 views
15

Tengo una aplicación de servidor escrita en C++. Después del inicio, utiliza alrededor de 480 KB de memoria en x86 Linux (Ubuntu 8.04, GCC 4.2.4). Creo que 480 KB es una cantidad excesiva de memoria: el servidor ni siquiera está haciendo nada, no se han conectado clientes al servidor. (Consulte también mi comentario a continuación en el que explico por qué creo que 480 KB es una gran cantidad de memoria.) Lo único que hace el servidor durante la inicialización es engendrar uno o dos hilos, configurar unos pocos sockets y otras cosas simples que no son es muy intensivo en memoria.Cómo reducir el consumo de memoria C++ por defecto?

Tenga en cuenta que estoy hablando de real uso de memoria, no el tamaño de la VM. Lo medí comenzando 100 instancias de mi servidor en una computadora portátil inactiva y midiendo el uso de memoria del sistema con 'libre' antes y después de iniciar las instancias del servidor. Ya tomé el caché del sistema de archivos y cosas así en la cuenta.

Después de algunas pruebas, parece que algo en el tiempo de ejecución de C++ hace que mi servidor use tanta memoria, incluso si el servidor no hace nada. Por ejemplo, si inserto

getchar(); return 0; 

justo después

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

entonces el uso de memoria es todavía 410 KB por ejemplo!

Mi aplicación depende solo de Curl y Boost. Tengo una buena cantidad de experiencia con la programación C y sé que las bibliotecas C no tienden a aumentar el consumo de memoria hasta que las utilizo.

Otras cosas que he encontrado:

  • Una aplicación simple hola mundo C consume alrededor de 50 KB de memoria.
  • Una sencilla aplicación C de hello world vinculada a Curl, pero que de lo contrario no usa Curl, consume aproximadamente 50 KB de memoria también.
  • Una aplicación Hello World C++ (sin Boost) consume aproximadamente 100 KB de memoria.
  • Una sencilla aplicación de C++ de hello world que incluye algunos encabezados de Boost, pero que en realidad no usa Boost, consume aproximadamente 100 KB de memoria. Sin símbolos Boost al inspeccionar el ejecutable con 'nm'.

Por consiguiente, mi conclusión es la siguiente:

  1. gcc tira a la basura símbolos Boost no utilizados.
  2. Si mi aplicación usa Boost, , entonces algo en el tiempo de ejecución C++ (probablemente el enlazador dinámico) hace que use mucha memoria. ¿Pero qué? ¿Cómo averiguo qué son estas cosas y qué puedo hacer con ellas?

Recuerdo algunas discusiones de KDE hace varios años acerca de los problemas del enlazador dinámico de C++. El engarzador dinámico de Linux C++ generaba un tiempo de inicio lento en las aplicaciones de KDE C++ y un gran consumo de memoria. Por lo que sé, esos problemas se han solucionado desde entonces en tiempos de ejecución C++. Pero, ¿podría algo similar ser la causa del consumo excesivo de memoria que estoy viendo?

Las respuestas de gcc/dynamic linking experts son muy apreciadas.

Para aquellos que son curiosos, el servidor en cuestión es agente de registro de pasajeros Phusion: https://github.com/FooBarWidget/passenger/blob/master/ext/common/LoggingAgent/Main.cpp

+2

"boost" no es muy específico, es solo "una biblioteca" en el sentido de que puede obtenerlo en un archivo zip, realmente es un gran paquete de bibliotecas. Si todo lo que usa es 'boost :: shared_ptr', puede salirse con menos de 350K extra, por lo que cualquier intento de reducir su sobrecarga tiene que ser específico para lo que está usando. –

+4

Realmente no veo el problema en 480kb/instancia. Tal vez debería preocuparse por el uso de la memoria en los casos de uso reales, en lugar de cuando no está haciendo nada. – Puppy

+3

No estoy seguro de lo que está viendo es un problema real. Es cierto, 480kB es una gran cantidad de RAM para una aplicación de "hello world", pero los sistemas modernos no están optimizados para ejecutar "hello world" de la manera más eficiente posible. Están optimizados para ejecutar aplicaciones útiles. Entonces, la pregunta más relevante no debería ser "¿cómo hago que 'hello world' sea más pequeño", sino más bien "es mi aplicación actual usando demasiada memoria, y si es así, cómo puedo reducir eso"? –

Respuesta

1

Una forma de reducir el consumo de memoria es reducir el tamaño de rosca de pila.

En cuanto impulso, como Steve Jessop comentado, tiene que ser un poco más específico que "impulso".

+0

Eso afectará la utilización del espacio de direcciones virtuales, no debería afectar la confirmación privada. –

+0

@Ben Voigt Eso depende de la cantidad de apilamiento que le indicó inicialmente al sistema. Reduzca el tamaño de la pila y el tamaño de la confirmación inicial. – JimR

5

El tiempo de ejecución de C asigna más memoria de la que el proceso realmente utiliza como parte del funcionamiento normal. Esto se debe a que la asignación de memoria al nivel kernel es extremadamente lenta y solo se puede realizar en bloques de tamaño de página (el tamaño de página suele ser de 4 kb en cajas x86, pero puede ser más grande y suele ser de 8 kb en máquinas x64).

Además, cuando el tiempo de ejecución de C recibe una solicitud de asignación que no puede satisfacer, a menudo asigna más de lo necesario, una vez más, para eliminar el gasto de ir al kernel la mayor parte del tiempo.

Por último, si está utilizando accesorios de impulso, probablemente dependan de algunos componentes de STL, como std::vector. Estos componentes asignan espacio para los elementos usando std::allocator<T>, que en algunos casos volverá a asignar más espacio del que realmente se usa. (En las estructuras particulares, basada en nodos como std::map, std::set, y por lo general std::list hacer esto para poner los nodos de la lista o árbol juntos en la misma página de memoria)

Larga historia corta: No se preocupe por esto. La mitad de un meg de memoria no es mucho por ningún lado de la imaginación (al menos hoy en día), y la mayor parte probablemente esté amortizando el uso de las funciones de asignación dinámica. Escriba su servidor real, y si está usando demasiada memoria, ENTONCES busque formas de reducir el uso de la memoria.

EDITAR: Si el componente de refuerzo que está utilizando es asio, y está usando sockets, también debe saber que se ha consumido algo de memoria para mantener los búferes para los sockets también.

+0

Esos búferes no deberían asignarse a menos que se construyan realmente zócalos. Su 'int main() {return getchar(); } 'programa no debe incluir ningún bufete de socket (a menos que tenga variables globales). –

+0

Sé que malloc() preasigna memoria. Pero como ya he demostrado, la memoria aumenta incluso antes de que mi aplicación llame malloc(). – Hongli

+0

@Ben: no tengo variables globales que causen la asignación del montón. – Hongli

1

Parece que tiene un problema con un conflicto de direcciones base en algunas de sus bibliotecas de carga dinámica. Si requieren reubicación durante la carga, se mapearán como copias privadas arregladas.

Vuelva a ejecutar prelink en todo el sistema. Si la biblioteca carga en su dirección preferida, se mapeará como memoria compartida y solo costará una copia del código, sin importar cuántos procesos lo estén usando.

BTW, prelink también fue la solución para KDE.

+0

¿Dónde puedo obtener más información sobre los conflictos de direcciones base? ¿Y no te refieres a 'prelink' en lugar de' preload'? – Hongli

+0

El equivalente de Windows es BIND. – MSalters

+0

@Hongli: sí, 'prelink' es el nombre del comando. oops –

Cuestiones relacionadas