Analizando el uso de la memoria: ¿Java vs C ++ insignificante?


9

¿Cómo se compara / contrasta el uso de memoria de un objeto entero escrito en Java con el uso de memoria de un objeto entero escrito en C ++? ¿La diferencia es insignificante? ¿Ninguna diferencia? ¿Una gran diferencia? Supongo que es lo mismo porque un int es un int independientemente del idioma (?)

La razón por la que pregunté esto es porque estaba leyendo sobre la importancia de saber cuándo los requisitos de memoria de un programa evitarán que el programador resuelva un problema dado.

Lo que me fascinó es la cantidad de memoria requerida para crear un solo objeto Java. Tomemos, por ejemplo, un objeto entero. Corrígeme si me equivoco pero un objeto entero Java requiere 24 bytes de memoria:

  • 4 bytes para su variable de instancia int
  • 16 bytes de sobrecarga (referencia a la clase del objeto, información de recolección de basura e información de sincronización)
  • 4 bytes de relleno

Como otro ejemplo, una matriz Java (que se implementa como un objeto) requiere 48 + bytes:

  • 24 bytes de información de encabezado
  • 16 bytes de sobrecarga de objetos
  • 4 bytes de longitud
  • 4 bytes para relleno
  • más la memoria necesaria para almacenar los valores

¿Cómo se comparan estos usos de memoria con el mismo código escrito en C ++?

Solía ​​ser ajeno al uso de memoria de los programas C ++ y Java que escribí, pero ahora que estoy empezando a aprender sobre algoritmos, aprecio mucho más los recursos de la computadora.


66
¿Qué es un "objeto entero" en C ++? int? Si es así, debe comparar eso con intJava, no Integer, siempre que sus entradas C ++ sean de 32 bits.
Mat

+1 Si creé una clase de c ++ que solo tenía una variable int, la instanciaron
Anthony

3
un int no es un int - depende de la plataforma

1
Un C ++ con solo un miembro int normalmente no tendrá ninguna sobrecarga. Utilizará exactamente tanto espacio como la plataforma usa para almacenar un int (típicamente 4 bytes en las plataformas de PC actuales).
Dirk Holsopple

+1 para un programador de Java que siente curiosidad por la memoria. Más que cualquier otra cosa, la conciencia de la memoria es el factor más importante que determina el rendimiento en las arquitecturas modernas.
imallett

Respuestas:


15

Depende de la plataforma y la implementación.

C ++ garantiza que el tamaño de chares exactamente un byte y al menos 8 bits de ancho. Entonces, el tamaño de a short intes de al menos 16 bits y no menor que char. El tamaño de un intes al menos tan grande como el tamaño de short int. El tamaño long intes de al menos 32 bits y no menor que int.

sizeof(char) == 1; sizeof(long int) >= sizeof(int) >= sizeof(short int) >= sizeof(bool) >= sizeof(char).

Sin embargo, el modelo de memoria real de C ++ es muy compacto y predecible . Por ejemplo, no hay metadatos en objetos, matrices o punteros. Las estructuras y las clases son contiguas al igual que las matrices, pero el relleno puede colocarse donde sea necesario y necesario.

Francamente, tal comparación es tonta en el mejor de los casos, ya que el uso de la memoria Java depende más de la implementación de Java que del código que ejecuta.


1
Es cierto, pero en aras de la comparación, diría que deberíamos asumir tipos enteros de tamaño equivalente (incluso si solo están disponibles con diferentes nombres). Después de todo, diferentes tamaños significan una semántica diferente, y en muchas (no todas) plataformas comunes los tamaños son idénticos, o los enteros del mismo tamaño están disponibles con diferentes nombres.

Nota para el OP: es mejor que elija el tamaño del entero; si desea un int de 32 bits en C ++, puede usarlo int32_t.
K.Steff

9

La mayoría de las respuestas parecen ignorar un par de puntos bastante importantes.

Primero, en una gran cantidad de Java, prácticamente nunca se ve un raw int; casi todo el uso es útil Integer, por lo que el hecho de que un intpodría ser (aproximadamente) del mismo tamaño que un inten C o C ++ es casi irrelevante, excepto por eso ( en mi experiencia, pequeño) porcentaje de código que solo usa en intlugar de Integer.

En segundo lugar, el tamaño de los objetos individuales no tiene casi nada que ver con la huella de memoria de un programa en su conjunto. En Java, la huella de memoria del programa se relaciona principalmente con cómo se ha ajustado el recolector de basura. En la mayoría de los casos, el GC se ajusta para maximizar la velocidad, lo que (en gran medida) significa ejecutar el GC con la menor frecuencia posible.

No tengo un enlace a mano en este momento, pero se han realizado algunas pruebas que muestran que Java puede correr a la misma velocidad que C, pero para hacerlo hay que ejecutar el GC raramente lo suficiente como para usar alrededor de 7 veces más memoria. Esto no se debe a que los objetos individuales sean 7 veces más grandes, sino a que GC puede ser bastante costoso si lo hace con demasiada frecuencia. Peor aún, GC solo puede liberar memoria cuando puede "probar" que ya no hay ninguna forma de acceder a un objeto, en lugar de simplemente cuando sabes que ya no lo usas. Esto significa que incluso si ejecuta el GC con mucha más frecuencia para minimizar el uso de memoria, probablemente aún pueda planificar que un programa típico tenga una huella de memoria que sea mayor. En un caso como este, puede reducir el factor a 2 o 3 en lugar de 7. Sin embargo, aunque vaya drásticamente por la borda, no lo haga.1 .

Dependiendo de la situación, hay otro factor que puede o no ser significativo: la memoria ocupada por la propia JVM. Esto es más o menos fijo, por lo que, como porcentaje, puede ser enorme si la aplicación no necesita mucho almacenamiento propio, o puede ser minúsculo si la aplicación necesita almacenar mucho. Al menos en mi máquina, incluso la aplicación Java más trivial parece ocupar algo así como 20-25 megabytes (podría ser más de 1000x para programas trivlal, o casi inconmensurablemente pequeña para los grandes).


1 Eso no quiere decir que nadie podría lograr escribir Java con una huella tan cercana a lo que obtendría con C ++. Es solo para decir que solo tener el mismo número / tamaño de objetos y ejecutar el GC con mucha frecuencia no lo llevará a eso por regla general.


77
Con respecto a su primer punto: no soy un tipo Java, pero las API de Java que he visto nunca se usaron Integer(¿por qué lo harían?) En lugar de int. Solo las colecciones genéricas no tienen más opción que usar Integerdebido a la eliminación de tipos, pero si le importa, puede reemplazarlas con una implementación especializada into cualquier tipo primitivo que necesite. Y luego está el boxeo temporal para pasar por el código genérico de envoltura (por ejemplo, todo lo que requiere un Object[]). Además de eso, ¿tiene fuentes para la sobrecarga del espacio GC? Realmente no lo dudo, simplemente tengo curiosidad.


9

Espero que se den cuenta de que todo esto está profundamente definido en la implementación, tanto para Java como para C ++. Dicho esto, el modelo de objetos de Java requiere bastante espacio.

Los objetos C ++ no necesitan (generalmente) ningún almacenamiento, excepto lo que necesitan los miembros. Tenga en cuenta que (a diferencia de Java, donde todo lo definido por el usuario es un tipo de referencia), el código del cliente puede usar objetos como tipo de valor o como tipos de referencia, es decir, un objeto podría almacenar un puntero / referencia a otro objeto, o almacenar el objeto directamente sin indirección Es necesario un puntero adicional por objeto si hay algún virtualmétodo, pero algunas clases útiles están diseñadas para llevarse bien sin polimorfismo y no lo necesitan. No hay metadatos GC ni bloqueo por objeto. Por lo tanto, los class IntWrapper { int x; public: IntWrapper(int); ... };objetos no necesitan más espacio que los simples int, y se pueden colocar directamente (es decir, sin indirección) en colecciones y otros objetos.

Las matrices son complicadas simplemente porque no hay un equivalente común prefabricado a una matriz de Java en C ++. Simplemente podría asignar un grupo de objetos con new[](sin sobrecarga / metadatos) pero no hay un campo de longitud; la implementación probablemente almacena uno pero no puede acceder a él. std::vectores una matriz dinámica y por lo tanto tiene una sobrecarga adicional y una interfaz más grande. std::arrayy matrices de estilo C (int arr[N];), necesita una constante de tiempo de compilación. En teoría, debería ser solo el almacenamiento del objeto más un número entero para la longitud, pero dado que puede obtener un cambio de tamaño dinámico y una interfaz con todas las funciones con muy poco espacio extra, simplemente vaya por eso en la práctica. Tenga en cuenta que todas estas, así como todas las demás colecciones, predeterminan el almacenamiento de los objetos por valor, lo que le ahorra indirección y el espacio para referencias, y mejora el comportamiento de la memoria caché. Debe almacenar explícitamente los punteros (los inteligentes, por favor) para obtener la indirección.

Las comparaciones anteriores no son del todo justas, porque algunos de estos ahorros se obtienen al no incluir las características que incluye Java, y su equivalente de C ++ a menudo está menos optimizado que el equivalente de Java (*). La forma común de implementar virtualen C ++ impone exactamente la misma sobrecarga que la forma común de implementar virtualen Java. Para obtener un bloqueo, necesita un objeto mutex con todas las funciones, que probablemente sea más grande que unos pocos bits. Para obtener el recuento de referencia ( noequivalente a GC, y no debe usarse como tal, pero a veces útil), necesita un puntero inteligente que agregue un campo de recuento de referencia. A menos que el objeto se construya con cuidado, el recuento de referencias, el objeto de puntero inteligente y el objeto de referencia están en ubicaciones completamente separadas, e incluso cuando lo construye correctamente, el puntero compartido puede (¿debe?) Seguir siendo dos punteros en lugar de uno. Por otra parte, un buen estilo de C ++ no utiliza estas características lo suficiente como para que importe: en la práctica, los objetos de una biblioteca de C ++ bien escritos usan menos. Eso no necesariamente significa menos uso de memoria en general, pero sí significa que C ++ tiene una buena ventaja en este sentido.

(*) Por ejemplo, puede obtener llamadas virtuales, códigos hash de identidad y bloqueo con solo una palabra para algunos objetos (y dos palabras para muchos otros objetos) fusionando la información de tipo con varios indicadores y eliminando bits de bloqueo para objetos que son Es poco probable que necesite cerraduras. Para obtener una explicación detallada de esta y otras optimizaciones, vea Implementación eficiente en tiempo y espacio del modelo de objetos Java (PDF) de David F. Bacon, Stephen J. Fink y David Grove.


3

Un plano int, en java, ocupa exactamente tanto espacio como inten C ++, siempre que ambas implementaciones usen el mismo tamaño de entero y alineación de memoria.

Un int 'objeto' (un entero en caja , es decir, una instancia de clase Integer), lleva toda la sobrecarga de una instancia de clase en Java, por lo que es significativamente más grande que un inten C ++. Sin embargo, si tuviera que equipar un objeto en C ++ con las mismas características que los objetos Java vienen de fábrica (polimorfismo, boxeo, recolección de basura, RTTI), entonces probablemente terminaría con un objeto de igual Talla.

Y luego hay consideraciones de optimización; Dado que los modelos de ejecución y los paradigmas de programación difieren, es poco probable que cualquier problema no trivial se resuelva de la misma manera en ambos idiomas, por lo que comparar el tamaño de almacenamiento en este nivel no tiene mucho sentido.

Sí, los objetos Java llevan más sobrecarga por defecto que las clases de C ++, pero vienen con más características, y esto lleva a un estilo de programación diferente: un buen programador puede aprovechar las ventajas y desventajas de cualquier lenguaje.


+1 Más gastos generales pero más funciones en Java, lo entiendo ahora, gracias
Anthony
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.