33 votes

Utilisation de la mémoire en Java int

Alors que je pensais sur l'utilisation de la mémoire de différents types, j'ai commencé à devenir un peu confus de Java utilise de la mémoire pour les entiers lorsqu'ils sont passés à une méthode.

Dire, j'ai eu le code suivant:

public static void main (String[] args){
     int i = 4;
     addUp(i);
}

public static int addUp(int i){
     if(i == 0) return 0;
     else return addUp(i - 1);         
}

Dans l'exemple qui suit, je me demande si ma logique suivante est correcte:

  • J'ai fait un mémoire initialement pour l'entier i = 4. Puis-je passer à une méthode. Cependant, depuis les primitives ne sont pas fait en Java, dans le addUp(i == 4), j'en crée un autre entier i = 4. Ensuite, il y a un autre addUp(i == 3), addUp(i == 2), addUp(i == 1), addUp(i == 0) dans laquelle à chaque fois, puisque la valeur n'est pas fait, une nouvelle que j'valeur est alloué dans la mémoire.
  • Alors, pour un seul "int i" de la valeur, j'ai utilisé 6 valeur entière de souvenirs.

Cependant, si je devais toujours passer par un tableau:

public static void main (String[] args){
     int[] i = {4};
     // int tempI = i[0];
     addUp(i);
}

public static int addUp(int[] i){
     if(i[0] == 0) return 0;
     else return addUp(i[0] = i[0] - 1);         
}

- Depuis que j'ai créer un tableau d'entiers de taille 1 et de passer ensuite que addUp qui sera à nouveau passé pour addUp(i[0] == 3), addUp(i[0] == 2), addUp(i[0] == 1), addUp(i[0] == 0), je n'ai eu qu'à utiliser 1 tableau d'entiers de la mémoire de l'espace, et donc beaucoup plus rentable. En outre, si je devais faire un int valeur à l'avance pour stocker la valeur initiale de i[0], j'ai toujours mon "original" de la valeur.

Ensuite, ce qui m'amène à la question, pourquoi les gens passent primitives comme les int dans les méthodes de Java? N'est-il pas bien plus efficace en terme de mémoire pour juste passer le tableau des valeurs de ces primitives? Ou en est le premier exemple d'une certaine façon toujours juste O(1) de la mémoire?

Et sur le dessus de cette question, je me demande simplement la mémoire des différences de l'aide int[] et int en particulier pour une taille de 1. Je vous remercie à l'avance. Je me demandais simplement d'être plus efficace en terme de mémoire à Java, et il en est venu à ma tête.

Merci pour toutes ces réponses! Je suis juste en train de rapidement demandais si je devais "analyser" big-oh la mémoire de chaque code, seraient-ils tous deux être considérés comme O(1) ou serait-ce une erreur de croire?

56voto

GhostCat Points 83269

Ce qui vous manque ici: les valeurs int dans votre exemple, aller sur la pile, pas sur le tas.

Et il est beaucoup moins de frais généraux à traiter avec la taille fixe des valeurs primitives existantes sur la pile par rapport à des objets sur le tas!

En d'autres termes: à l'aide d'un "pointeur" signifie que vous devez créer un nouvel objet sur le tas. Tous les objets en direct sur le tas; il n'y a pas de pile pour les tableaux! Et des objets devient l'objet de la collecte des ordures immédiatement après avoir cessé de les utiliser. Des piles d'autre part d'aller et venir comme vous appelez des méthodes!

Au-delà: gardez à l'esprit que les abstractions que les langages de programmation offrent à nous sont créés pour nous aider à écrire du code qui est facile à lire, à comprendre et à maintenir. Votre démarche est essentiellement de faire une sorte de réglage qui a conduit à plus de code compliqué. Et ce n'est pas la façon dont Java résout ces problèmes.

C'est à dire: Java, la vraie performance "magique" qui se passe au moment de l'exécution, lorsque le just-in-time compiler des coups de pied dans! Vous voyez, le JIT peut inline appels aux petites méthodes lorsque la méthode est appelée "assez souvent". Et puis, il devient encore plus important de garder les données de "fermer" ensemble. Comme dans: lorsque les données des vies sur le tas, vous pourriez avoir à accéder à la mémoire d'obtenir une valeur. Alors que les éléments de vie de la pile - peut-être encore "fermer" (comme dans: dans le cache du processeur). Si votre petite idée pour optimiser l'utilisation de la mémoire pourrait ralentir l'exécution du programme par ordre de grandeur. Parce que même aujourd'hui, il y a des ordres de grandeur entre les accès à la mémoire cache du processeur et de la lecture de la mémoire principale.

Longue histoire courte: éviter de tomber dans ces "micro-tuning", soit des performances ou de l'utilisation de la mémoire: la JVM est optimisé pour le "normal" typique des cas d'utilisation. Vos tentatives pour introduire intelligent de contournement peut donc facilement provoquer des "moins bons" résultats.

Alors - quand vous vous inquiétez au sujet de la performance: faire ce que tout le monde est en train de faire. Et si vous avez un vraiment de soins - puis apprendre comment la machine fonctionne. Comme il s'avère que même ma connaissance est légèrement dépassés, que les commentaires qui implique qu'une équipe commune d'enquête peut inline objets sur la pile. Dans ce sens: se concentrer sur l'écriture claire, élégante, code qui résout le problème de simple!

Enfin: il est sujet à des changements, à un certain point. Il y a des idées pour introduire la vraie valeur des objets de valeur à java. Qui vivez sur la pile, pas le tas. Mais ne vous attendez pas que cela arrive avant Java10. Ou 11. Ou ... (je pense que ce serait pertinent ici).

18voto

pfranza Points 1504

Plusieurs choses:

Première chose sera de fractionnement de poils, mais lorsque vous passez un int en java vous allouez de 4 octets sur la pile, et lorsque vous passez un tableau (parce que c'est une référence) vous sont effectivement l'allocation de 8 octets (en supposant une architecture x64) sur la pile, plus les 4 octets qui stockent l'int dans le tas.

Plus important encore, les données qui vit dans le tableau est alloué dans le tas, tandis que la référence au tableau lui-même est alloué sur la pile, lors du passage d'un entier il n'y a pas d'allocation de tas nécessaire que la primitive est allouée sur la pile. Au fil du temps en réduisant le tas allocations signifie que le garbage collector aura moins de choses à nettoyer. Alors que le nettoyage de la pile d'images est trivial et ne nécessite pas de traitement supplémentaire.

Cependant, c'est tout à fait discutable (à mon humble avis) car dans la pratique quand vous avez compliqué collections de variables et d'objets, vous êtes probablement va les grouper dans une classe. En général, vous devriez être écrit afin de favoriser la lisibilité et la maintenabilité plutôt que d'essayer de serrer la dernière goutte de performances de la JVM. La JVM est assez rapide comme il est, et il est toujours la Loi de Moore comme un filet de sécurité.

Il serait difficile d'analyser le Big-O pour chaque parce que, pour obtenir une image fidèle que vous auriez à un facteur déterminant dans le comportement du garbage collector et que le comportement est fortement tributaire à la fois de la JVM elle-même et à tout moment de l'exécution (JIT), les optimisations que la JVM a fait à votre code.

Rappelez-vous , Donald Knuth's sages que "l'optimisation prématurée est la racine de tous les maux"

Écrire du code qui évite les micro-réglage, le code qui favorise la lisibilité et la maintenabilité va s'en sortent mieux sur le long terme.

10voto

harold Points 14256

Si votre hypothèse est que les arguments passés à des fonctions consomment nécessairement de la mémoire (ce qui est faux en passant), dans votre deuxième exemple qui passe un tableau, une copie de la référence au tableau est créée. Cette référence peut en fait être plus grande qu'un int, il est peu probable qu'elle soit plus petite.

7voto

Theodore Norvell Points 2470

Si ces méthodes prennent en O(1) O(N) dépend du compilateur. (Ici, N est la valeur de i ou i[0], selon.) Si le compilateur utilise tail-recursion optimisation ensuite la pile de l'espace pour les paramètres, les variables locales, et l'adresse de retour peuvent être réutilisés et la mise en œuvre sera alors O(1) pour l'espace. Absent de la queue-de récursivité à l'optimisation de l'espace de la complexité est la même que la complexité du temps, O(N).

Fondamentalement tail-recursion optimisation des quantités (dans ce cas) pour le compilateur de réécriture de votre code

public static int addUp(int i){
     while(i != 0) i = i-1 ;
     return 0;        
}

ou

public static int addUp(int[] i){
     while(i[0] != 0) i[0] = i[0] - 1 ;
     return 0 ;
}

Un bon optimiseur peut optimiser encore plus loin les boucles.

Autant que je sache, pas de compilateurs Java en œuvre de la queue-de récursivité de l'optimisation à l'heure actuelle, mais il n'y a pas de raison technique pour que ça ne peut pas être fait dans de nombreux cas.

5voto

Oleksandr Points 7545

En fait, lorsque vous passer un tableau en paramètre à une méthode d'une référence à ce tableau est passé sous le capot. Le tableau lui-même est stocké sur le tas. Et la référence peut être 4 ou 8 octets taille (en fonction de l'architecture du PROCESSEUR, de la JVM de mise en œuvre, etc.; même plus, JLS ne dit rien sur l'importance de la référence à la mémoire).

D'autre part, primitif int de la valeur toujours consomme seulement 4 octets et se trouve sur la pile.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X