Comment l'utilisation de la mémoire d'un objet entier écrit en Java se compare \ contraste avec l'utilisation de la mémoire d'un objet entier écrit en C ++? La différence est-elle négligeable? Aucune différence? Une grosse différence? Je suppose que c'est la même chose car un int est un int quelle que soit la langue (?)
La raison pour laquelle j'ai posé cette question est parce que je lisais sur l'importance de savoir quand les besoins en mémoire d'un programme empêcheront le programmeur de résoudre un problème donné.
Ce qui m'a fasciné, c'est la quantité de mémoire requise pour créer un seul objet Java. Prenons par exemple un objet entier. Corrigez-moi si je me trompe mais qu'un objet entier Java nécessite 24 octets de mémoire:
- 4 octets pour sa variable d'instance int
- 16 octets de surcharge (référence à la classe de l'objet, aux informations de récupération de place et aux informations de synchronisation)
- 4 octets de remplissage
Comme autre exemple, un tableau Java (qui est implémenté en tant qu'objet) nécessite 48 octets et plus:
- 24 octets d'informations d'en-tête
- 16 octets de surcharge d'objet
- 4 octets de longueur
- 4 octets pour le remplissage
- plus la mémoire nécessaire pour stocker les valeurs
Comment ces utilisations de la mémoire se comparent-elles au même code écrit en C ++?
J'étais inconscient de l'utilisation de la mémoire des programmes C ++ et Java que j'ai écrits, mais maintenant que je commence à apprendre les algorithmes, j'apprécie davantage les ressources de l'ordinateur.
la source
int
? Si c'est le cas, vous devriez comparer cela àint
Java, pasInteger
- tant que vos entrées C ++ sont 32 bits.Réponses:
Cela dépend de la plate-forme et de la mise en œuvre.
C ++ garantit que la taille de
char
est exactement d'un octet et d'au moins 8 bits de large. Ensuite, la taille de ashort int
est d'au moins 16 bits et pas inférieure àchar
. La taille d'unint
est au moins aussi grande que la taille deshort int
. La taille delong int
est d'au moins 32 bits et pas inférieure à int.sizeof(char) == 1; sizeof(long int) >= sizeof(int) >= sizeof(short int) >= sizeof(bool) >= sizeof(char).
Le modèle de mémoire réel de C ++ est cependant très compact et prévisible . Par exemple, il n'y a pas de métadonnées dans les objets, les tableaux ou les pointeurs. Les structures et les classes sont contiguës, tout comme les tableaux, mais le remplissage peut être placé là où c'est nécessaire et nécessaire.
Franchement, une telle comparaison est au mieux idiote car l'utilisation de la mémoire Java dépend plus de l'implémentation Java que du code qu'elle exécute.
la source
int32_t
.La plupart des réponses semblent ignorer quelques points assez importants.
Tout d'abord, dans un très grand nombre de Java, vous ne voyez pratiquement jamais un brut
int
- presque toute utilisation est deInteger
, donc le fait qu'unint
pourrait être (environ) de la même taille qu'unint
en C ou C ++ est presque hors de propos, sauf pour cela ( d'après mon expérience, petit) pourcentage de code qui utilise uniquementint
au lieu deInteger
.Deuxièmement, la taille des objets individuels n'a presque rien à voir avec l'empreinte mémoire d'un programme dans son ensemble. En Java, l'empreinte mémoire du programme se rapporte principalement à la façon dont le garbage collector a été réglé. Dans la plupart des cas, le GC est réglé pour maximiser la vitesse, ce qui signifie (en grande partie) d'exécuter le GC aussi rarement que possible.
Je n'ai pas de lien à portée de main pour le moment, mais il y a eu des tests montrant que Java peut fonctionner à la même vitesse que C, mais pour ce faire, vous devez exécuter le GC assez rarement qu'il utilise environ 7 fois plus Mémoire. Ce n'est pas parce que les objets individuels sont 7 fois plus gros, mais parce que le GC peut devenir assez cher si vous le faites trop souvent. Pire, GC ne peut libérer de la mémoire que lorsqu'il peut "prouver" qu'il n'y a plus aucun moyen d'accéder à un objet, plutôt que simplement lorsque vous savez que vous avez fini de l'utiliser. Cela signifie que même si vous exécutez le GC beaucoup plus souvent pour minimiser l'utilisation de la mémoire, vous pouvez probablement prévoir qu'un programme typique ait une empreinte mémoire plus grande. Dans un cas comme celui-ci, vous pouvez réduire le facteur à 2 ou 3 au lieu de 7. Cependant, même si vous allez trop loin,1 .
Selon la situation, un autre facteur peut ou non être significatif: la mémoire occupée par la JVM elle-même. Ceci est plus ou moins fixe, donc en pourcentage, il peut être énorme si l'application n'a pas besoin de beaucoup de stockage, ou minuscule si l'application a besoin de stocker beaucoup. Au moins sur ma machine, même l'application Java la plus triviale semble occuper quelque chose comme 20-25 mégaoctets (pourrait être plus de 1000 fois pour les programmes triviaux, ou presque incommensurablement minuscule pour les gros).
1 Cela ne veut pas dire que personne ne pourrait réussir à écrire Java avec une empreinte proche de ce que vous obtiendriez avec C ++. Il est seulement de dire que tout ayant le même nombre / taille des objets, et le fonctionnement du GC va vraiment pas souvent vous y en règle.
la source
Integer
(pourquoi le feraient-ils?) À la placeint
. Seules les collections génériques n'ont pas d'autre choix que d'utiliser enInteger
raison de l'effacement des types, mais si vous vous en souciez, vous pouvez les remplacer par une implémentation spécialiséeint
ou quel que soit le type primitif dont vous avez besoin. Et puis il y a la boxe temporaire pour passer par du code générique d'encapsulation (par exemple tout ce qui nécessite unObject[]
). En dehors de cela, avez-vous des sources pour la surcharge de l'espace GC? Je n'en doute pas vraiment, je suis simplement curieux.J'espère que vous vous rendez compte que tout cela est profondément défini par l'implémentation, à la fois pour Java et C ++. Cela étant dit, le modèle objet de Java nécessite un peu d'espace.
Les objets C ++ n'ont (généralement) besoin d' aucun stockage sauf ceux dont les membres ont besoin. Notez que (contrairement à Java, où tout ce qui est défini par l'utilisateur est un type de référence), le code client peut utiliser un objet à la fois comme type de valeur ou comme type de référence, c'est-à-dire qu'un objet peut stocker un pointeur / référence vers un autre objet, ou stocker directement l'objet sans indirection. Un pointeur supplémentaire par objet est nécessaire s'il existe des
virtual
méthodes, mais plusieurs classes utiles sont conçues pour s'entendre sans polymorphisme et n'en ont pas besoin. Il n'y a pas de métadonnées GC ni de verrouillage par objet. Ainsi, lesclass IntWrapper { int x; public: IntWrapper(int); ... };
objets n'ont pas besoin de plus d'espace que lesint
s simples et peuvent être placés directement (c'est-à-dire sans indirection) dans les collections et autres objets.Les tableaux sont délicats simplement parce qu'il n'y a pas d'équivalent prédéfini et commun à un tableau Java en C ++. Vous pouvez simplement allouer un tas d'objets avec
new[]
(sans aucune surcharge / métadonnée) mais il n'y a pas de champ de longueur - l'implémentation en stocke probablement un mais vous ne pouvez pas y accéder.std::vector
est un tableau dynamique et a donc une surcharge supplémentaire et une interface plus grande.std::array
et les tableaux de style C (int arr[N];
), ont besoin d'une constante de temps de compilation. En théorie, cela devrait être juste le stockage de l'objet plus un seul entier pour la longueur - mais comme vous pouvez obtenir un redimensionnement dynamique et une interface complète avec très peu d'espace supplémentaire, vous optez simplement pour cela dans la pratique. Notez que tous ceux-ci, ainsi que toutes les autres collections, stockent par défaut les objets par valeur, vous économisant ainsi l'indirection et l'espace pour les références, et améliorant le comportement du cache. Vous devez explicitement stocker des pointeurs (intelligents, s'il vous plaît) pour obtenir l'indirection.Les comparaisons ci-dessus ne sont pas entièrement justes, car certaines de ces économies sont réalisées en n'incluant pas les fonctionnalités incluses par Java, et leur équivalent C ++ est souvent moins optimisé que l'équivalent Java (*). La manière courante d'implémenter
virtual
en C ++ impose exactement autant de frais généraux que la manière courante d'implémentervirtual
en Java. Pour obtenir un verrou, vous avez besoin d'un objet mutex complet, qui est probablement plus grand que quelques bits. Pour obtenir le comptage des références ( paséquivalent à GC, et ne doit pas être utilisé comme tel, mais parfois utile), vous avez besoin d'un pointeur intelligent qui ajoute un champ de comptage de référence. À moins que l'objet ne soit construit avec soin, le nombre de références, l'objet pointeur intelligent et l'objet référencé sont dans des emplacements complètement séparés, et même lorsque vous le construisez correctement, le pointeur partagé peut (doit?) Toujours être deux pointeurs au lieu d'un. Là encore, un bon style C ++ n'utilise pas suffisamment ces fonctionnalités pour que cela ait de l'importance - en pratique, les objets d'une bibliothèque C ++ bien écrits en utilisent moins. Cela ne signifie pas nécessairement moins d'utilisation de la mémoire dans l'ensemble, mais cela signifie que C ++ a une bonne longueur d'avance à cet égard.(*) Par exemple, vous pouvez obtenir des appels virtuels, des codes de hachage d'identité et un verrouillage avec un seul mot pour certains objets (et deux mots pour de nombreux autres objets) en fusionnant les informations de type avec divers indicateurs et en supprimant les bits de verrouillage pour les objets qui sont peu susceptible d'avoir besoin de verrous. Voir Implémentation efficace en temps et en espace du modèle objet Java (PDF) par David F. Bacon, Stephen J. Fink et David Grove pour une explication détaillée de cette optimisation et d'autres.
la source
Un plain
int
, en java, prend exactement autant d'espace qu'unint
en C ++, à condition que les deux implémentations utilisent la même taille entière et le même alignement de mémoire.Un «objet» int (un entier encadré , c'est-à-dire une instance de classe
Integer
), transporte tous les frais généraux d'une instance de classe en Java, il est donc beaucoup plus volumineux qu'unint
en C ++. Cependant, si vous deviez équiper un objet en C ++ avec les mêmes fonctionnalités que les objets Java fournis avec le prêt à l'emploi (polymorphisme, boxe, garbage collection, RTTI), vous vous retrouveriez probablement avec un objet d'égale Taille.Et puis il y a des considérations d'optimisation; étant donné que les modèles d'exécution et les paradigmes de programmation diffèrent, il est peu probable qu'un problème non trivial soit résolu de la même manière dans les deux langages, donc comparer la taille du stockage à ce niveau n'a pas beaucoup de sens.
Oui, les objets Java entraînent plus de surcharge par défaut que les classes C ++, mais ils viennent avec plus de fonctionnalités, et cela conduit à un style de programmation différent - un bon programmeur peut tirer parti des avantages et des inconvénients de l'un ou l'autre langage.
la source