Tout d'abord, ce qui suit n'a pas fait l'objet de recherches. Je n'ai pas "plongé" dans le code source d'OpenJDK pour valider ce qui suit, et je n'ai pas accès à des connaissances internes.
J'ai essayé de valider vos résultats en exécutant votre test sur ma machine :
$ java -version
openjdk version "1.8.0_71"
OpenJDK Runtime Environment (build 1.8.0_71-b15)
OpenJDK 64-Bit Server VM (build 25.71-b15, mixed mode)
Le "nombre" varie dans une fourchette de ~250. (Pas autant que ce que vous voyez)
Tout d'abord, un peu de contexte. Dans une implémentation Java typique, la pile d'un thread est une région contiguë de la mémoire qui est allouée avant le démarrage du thread, et qui n'est jamais agrandie ou déplacée. Un débordement de pile se produit lorsque la JVM tente de créer un cadre de pile pour effectuer un appel de méthode, et que le cadre dépasse les limites de la région de mémoire. Le test pourrait Il est possible de le faire en testant explicitement la PS, mais je crois comprendre que cela est normalement mis en œuvre en utilisant une astuce astucieuse avec les paramètres de la page mémoire.
Lorsqu'une région de pile est allouée, la JVM effectue un appel système pour demander au système d'exploitation de marquer une page de "zone rouge" à la fin de la région de pile en lecture seule ou non accessible. Lorsqu'un thread effectue un appel qui déborde de la pile, il accède à la mémoire dans la "zone rouge", ce qui déclenche un défaut de mémoire. Le système d'exploitation le signale à la JVM par le biais d'un "signal", et le gestionnaire de signaux de la JVM le fait correspondre à un fichier StackOverflowError
qui est "jeté" sur la pile du thread.
Voici donc quelques possible des explications de la variabilité :
-
La granularité de la protection matérielle de la mémoire est la limite de la page. Ainsi, si la pile de threads a été allouée en utilisant malloc
le début de la région ne sera pas aligné sur la page. Par conséquent, la distance entre le début de la trame de la pile et le premier mot de la "zone rouge" (qui est >< alignée sur la page) va être variable.
-
La pile "principale" est potentiellement spéciale, car cette région peut être utilisé pendant l'amorçage de la JVM. Cela peut conduire à ce que des "trucs" soient laissés sur la pile avant le démarrage de la JVM. main
a été appelé. (Ce n'est pas convaincant... et je ne suis pas convaincu.)
Cela dit, la "grande" variabilité que vous observez est déconcertante. La taille des pages est trop petite pour expliquer une différence de ~7000 dans les comptages.
UPDATE
Lorsque le JIT est désactivé (-Djava.compiler=NONE), j'obtiens toujours le même chiffre (11907).
Intéressant. Entre autres choses, cela pourrait faire en sorte que la vérification des limites de la pile soit faite différemment.
C'est logique, car les optimisations du JIT affectent probablement la taille des cadres de pile et le travail effectué par le JIT doit certainement varier entre les exécutions.
Plausible. La taille du cadre d'empilement pourrait bien être différente après le f()
a été compilé en JIT. En supposant que f()
a été compilé en JIT à un moment donné, votre pile aura un mélange d'"anciennes" et de "nouvelles" images. Si la compilation JIT a eu lieu à des moments différents, alors le rapport sera différent ... et donc l'indicateur count
sera différent lorsque vous atteindrez la limite.
Néanmoins, je pense qu'il serait bénéfique que cette théorie soit confirmée par des références à de la documentation sur le sujet et/ou des exemples concrets du travail effectué par le JIT dans cet exemple spécifique qui conduit à des changements de taille de trame.
Il y a peu de chances que cela arrive, j'en ai peur... à moins que vous ne soyez prêt à PAYER quelqu'un pour faire quelques jours de recherche pour vous.
1) Aucune documentation de référence (publique) de ce type n'existe, AFAIK. Du moins, je n'ai jamais été capable de trouver une source définitive pour ce genre de chose ... à part une plongée profonde dans le code source.
2) L'examen du code compilé en JIT ne vous dit rien sur la façon dont l'interpréteur de bytecode traitait les choses avant que le code ne soit compilé en JIT. Ainsi, vous ne serez pas en mesure de voir si la taille de l'image a été modifiée. modifié .