Je suis conscient que chaque objet nécessite une mémoire de tas et que chaque primitive / référence sur la pile nécessite une mémoire de pile.
Lorsque j'essaie de créer un objet sur le tas et que la mémoire est insuffisante pour cela, la JVM crée une java.lang.OutOfMemoryError sur le tas et me la lance.
Donc implicitement, cela signifie qu'il y a de la mémoire réservée par la JVM au démarrage.
Que se passe-t-il lorsque cette mémoire réservée est utilisée (elle serait certainement utilisée, lisez la discussion ci-dessous) et que la JVM n'a pas assez de mémoire sur le tas pour créer une instance de java.lang.OutOfMemoryError ?
Est-ce que ça se bloque? Ou me lancerait-il null
car il n'y a pas de mémoire dans new
une instance de MOO?
try {
Object o = new Object();
// and operations which require memory (well.. that's like everything)
} catch (java.lang.OutOfMemoryError e) {
// JVM had insufficient memory to create an instance of java.lang.OutOfMemoryError to throw to us
// what next? hangs here, stuck forever?
// or would the machine decide to throw us a "null" ? (since it doesn't have memory to throw us anything more useful than a null)
e.printStackTrace(); // e.printStackTrace() requires memory too.. =X
}
==
Pourquoi la JVM ne pouvait-elle pas réserver suffisamment de mémoire?
Quelle que soit la quantité de mémoire réservée, il est toujours possible d'utiliser cette mémoire si la JVM n'a pas de moyen de "récupérer" cette mémoire:
try {
Object o = new Object();
} catch (java.lang.OutOfMemoryError e) {
// JVM had 100 units of "spare memory". 1 is used to create this OOM.
try {
e.printStackTrace();
} catch (java.lang.OutOfMemoryError e2) {
// JVM had 99 units of "spare memory". 1 is used to create this OOM.
try {
e.printStackTrace();
} catch (java.lang.OutOfMemoryError e3) {
// JVM had 98 units of "spare memory". 1 is used to create this OOM.
try {
e.printStackTrace();
} catch (java.lang.OutOfMemoryError e4) {
// JVM had 97 units of "spare memory". 1 is used to create this OOM.
try {
e.printStackTrace();
} catch (java.lang.OutOfMemoryError e5) {
// JVM had 96 units of "spare memory". 1 is used to create this OOM.
try {
e.printStackTrace();
} catch (java.lang.OutOfMemoryError e6) {
// JVM had 95 units of "spare memory". 1 is used to create this OOM.
e.printStackTrace();
//........the JVM can't have infinite reserved memory, he's going to run out in the end
}
}
}
}
}
}
Ou plus précisément:
private void OnOOM(java.lang.OutOfMemoryError e) {
try {
e.printStackTrace();
} catch (java.lang.OutOfMemoryError e2) {
OnOOM(e2);
}
}
la source
OutOfMemoryException
puis faire quelque chose qui impliquait la création d'un grand tampon ...OutOfMemoryError
et y conserve une référence. Il s'avère que la capture d'unOutOfMemoryError
n'est pas aussi utile qu'on pourrait le penser, car vous ne pouvez garantir presque rien sur l'état de votre programme lors de sa capture. Voir stackoverflow.com/questions/8728866/…Réponses:
La JVM ne manque jamais vraiment de mémoire. Il effectue à l'avance le calcul de la mémoire de la pile de segments de mémoire.
La structure de la JVM, chapitre 3 , section 3.5.2 indique:
Pour Heap , Section 3.5.3.
Donc, il fait un calcul à l'avance avant de faire l'allocation de l'objet.
Ce qui se passe, c'est que la JVM essaie d'allouer de la mémoire à un objet de la mémoire appelé région de génération permanente (ou PermSpace). Si l'allocation échoue (même après que la JVM a invoqué le garbage collector pour essayer d'allouer de l'espace libre), elle lance un
OutOfMemoryError
. Même les exceptions nécessitent un espace mémoire afin que l'erreur soit renvoyée indéfiniment.Lectures complémentaires. ? De plus, cela
OutOfMemoryError
peut se produire dans différentes structures JVM.la source
Graham Borland semble avoir raison : au moins ma JVM réutilise apparemment OutOfMemoryErrors. Pour tester cela, j'ai écrit un programme de test simple:
L'exécuter produit cette sortie:
BTW, la JVM que j'exécute (sur Ubuntu 10.04) est la suivante:
Edit: J'ai essayé de voir ce qui se passerait si je forçais la JVM à manquer complètement de mémoire en utilisant le programme suivant:
En fin de compte, il semble boucler pour toujours. Cependant, curieusement, essayer de terminer le programme avec Ctrl+ Cne fonctionne pas, mais ne donne que le message suivant:
Java HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGINT to handler- the VM may need to be forcibly terminated
la source
n
MOO et de réutiliser l'un d'eux par la suite, afin qu'un MOO puisse toujours être lancé. La JVM de Sun / Oracle ne prend pas du tout en charge la récursivité de queue IIRC?n
cadres sur la pile et finit par créer et détruire des cadresn+1
pour l'éternité, donnant l'impression de fonctionner sans fin.La plupart des environnements d'exécution pré-alloueront au démarrage, ou réserveront autrement, suffisamment de mémoire pour faire face aux situations de famine de mémoire. J'imagine que la plupart des implémentations JVM sensées feraient cela.
la source
catch
clause tente d'utiliser plus de mémoire, la machine virtuelle Java peut simplement continuer à lancer la même instance de MOO encore et encore.La dernière fois que je travaillais en Java et que j'utilisais un débogueur, l'inspecteur de tas a montré que la JVM avait alloué une instance de OutOfMemoryError au démarrage. En d'autres termes, il alloue l'objet avant que votre programme ne commence à consommer, et encore moins à manquer de mémoire.
la source
Dans la spécification JVM, chapitre 3.5.2:
Chaque machine virtuelle Java doit garantir qu'elle lancera un fichier
OutOfMemoryError
. Cela implique qu'il doit être capable de créer une instance deOutOfMemoryError
(ou de l'avoir créé à l'avance) même s'il n'y a plus d'espace de stockage.Bien qu'il ne doive pas garantir, qu'il reste suffisamment de mémoire pour l'attraper et imprimer une belle trace de pile ...
Une addition
Vous avez ajouté du code pour montrer que la JVM risque de manquer d'espace de tas si elle devait en lancer plusieurs
OutOfMemoryError
. Mais une telle mise en œuvre violerait l'exigence d'en haut.Il n'est pas nécessaire que les instances levées
OutOfMemoryError
soient uniques ou créées à la demande. Une machine virtuelle Java peut préparer exactement une instance deOutOfMemoryError
lors du démarrage et la lancer chaque fois qu'elle manque d'espace de stockage - ce qui est une fois, dans un environnement normal. En d'autres termes: l'instanceOutOfMemoryError
que nous voyons pourrait être un singleton.la source
Question interessante :-). Alors que les autres ont donné de bonnes explications sur les aspects théoriques, j'ai décidé de l'essayer. C'est sur Oracle JDK 1.6.0_26, Windows 7 64 bits.
Configuration de test
J'ai écrit un programme simple pour épuiser la mémoire (voir ci-dessous).
Le programme ne fait que créer un statique
java.util.List
et continue à y bourrer de nouvelles chaînes jusqu'à ce que le MOO soit lancé. Il le rattrape ensuite et continue le bourrage dans une boucle sans fin (pauvre JVM ...).Résultat du test
Comme on peut le voir sur la sortie, les quatre premières fois que OOME est lancé, il est livré avec une trace de pile. Après cela, les OOME suivants ne s'impriment que
java.lang.OutOfMemoryError: Java heap space
siprintStackTrace()
est invoqué.Donc, apparemment, la JVM fait un effort pour imprimer une trace de pile si elle le peut, mais si la mémoire est vraiment serrée, elle omet simplement la trace, comme le suggèrent les autres réponses.
Le code de hachage de OOME est également intéressant. Notez que les premiers OOME ont tous des hachages différents. Une fois que la JVM commence à omettre les traces de pile, le hachage est toujours le même. Cela suggère que la JVM utilisera des instances OOME fraîches (préallouées?) Aussi longtemps que possible, mais si la poussée vient à bout, elle réutilisera simplement la même instance plutôt que de n'avoir rien à jeter.
Production
Remarque: J'ai tronqué certaines traces de pile pour rendre la sortie plus facile à lire ("[...]").
Le programme
la source
System.out
mais desprintStackTrace()
utilisationsSystem.err
par défaut. Vous obtiendrez probablement de meilleurs résultats en utilisant l'un ou l'autre flux de manière cohérente.Je suis à peu près sûr, la JVM s'assurera absolument qu'elle a au moins assez de mémoire pour lever une exception avant de manquer de mémoire.
la source
Les exceptions indiquant une tentative de violation des limites d'un environnement de mémoire gérée sont gérées par le runtime dudit environnement, dans ce cas la JVM. La JVM est son propre processus, qui exécute l'IL de votre application. Si un programme tente d'effectuer un appel qui étend la pile d'appels au-delà des limites ou d'allouer plus de mémoire que la JVM ne peut en réserver, le runtime lui-même injectera une exception, ce qui entraînera le déroulement de la pile d'appels. Quelle que soit la quantité de mémoire dont votre programme a besoin actuellement, ou la profondeur de sa pile d'appels, la JVM aura alloué suffisamment de mémoire dans ses propres limites de processus pour créer ladite exception et l'injecter dans votre code.
la source
Vous semblez confondre la mémoire virtuelle réservée par la JVM dans laquelle la JVM exécute des programmes Java avec la mémoire native du système d'exploitation hôte dans laquelle la JVM est exécutée en tant que processus natif. La JVM de votre machine s'exécute dans la mémoire gérée par le système d'exploitation, et non dans la mémoire que la JVM a réservée pour exécuter des programmes Java.
Lectures complémentaires:
Et pour finir , essayer d' attraper une java.lang.Error (et ses classes descendantes) afin d'imprimer une trace de pile peut ne pas vous donner d'informations utiles. Vous voulez un vidage de tas à la place.
la source
Pour clarifier davantage la réponse de @Graham Borland, fonctionnellement, la JVM le fait au démarrage:
Plus tard, la JVM exécute l'un des bytecodes Java suivants: «nouveau», «anewarray» ou «multianewarray». Cette instruction oblige la JVM à exécuter un certain nombre d'étapes dans un état de mémoire insuffisante:
allocate()
.allocate()
tente d'allouer de la mémoire pour certains une nouvelle instance d'une classe ou d'un tableau particulier.doGC()
, qui tente de faire le garbage collection.allocate()
essaie à nouveau d'allouer de la mémoire à l'instance.throw OOME;
virtuelle Java, dans allocate (), fait simplement un , se référant à l'OOME qu'elle a instanciée au démarrage. Notez qu'il n'a pas eu à allouer cet OOME, il y fait simplement référence.Évidemment, ce ne sont pas des étapes littérales; ils varieront de JVM à JVM dans l'implémentation, mais c'est l'idée de haut niveau.
(*) Une quantité importante de travail se produit ici avant d'échouer. La JVM tentera d'effacer les objets SoftReference, tentera l'allocation directement dans la génération sécurisée lors de l'utilisation d'un collecteur générationnel, et éventuellement d'autres choses, comme la finalisation.
la source
Les réponses qui disent que la JVM pré-allouera
OutOfMemoryErrors
sont en effet correctes.En plus de tester cela en provoquant une situation de mémoire insuffisante, nous pouvons simplement vérifier le tas de n'importe quelle machine virtuelle Java (j'ai utilisé un petit programme qui fait juste une mise en veille, en l'exécutant à l'aide de la machine virtuelle Java Hotspot de Java 8 mise à jour 31).
En utilisant
jmap
nous voyons qu'il semble y avoir 9 instances de OutOfMemoryError (même si nous avons beaucoup de mémoire):On peut alors générer un vidage de tas:
et ouvrez-le à l'aide d' Eclipse Memory Analyzer , où une requête OQL montre que la JVM semble réellement pré-allouer
OutOfMemoryErrors
pour tous les messages possibles:Le code de la machine virtuelle Java 8 Hotspot qui les préalloue en fait peut être trouvé ici , et ressemble à ceci (avec certaines parties omises):
et ce code montre que la JVM essaiera d'abord d'utiliser l'une des erreurs pré-allouées avec de l'espace pour une trace de pile, puis retombera sur une sans trace de pile:
la source
Metaspace
. Ce serait bien si vous pouviez montrer un morceau de code qui s'adresse également à PermGen et le comparer avec Metaspace également.