Comment planter une JVM?

144

Je lisais un livre sur les compétences en programmation dans lequel l'auteur demande à la personne interrogée: "Comment faire planter une JVM?" Je pensais que vous pouviez le faire en écrivant une boucle for infinie qui finirait par utiliser toute la mémoire.

Quelqu'un a une idée?

Shivasubramanian A
la source
1
Sur-ensemble possible de: stackoverflow.com/questions/6470651/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
stackoverflow.com/questions/30072883/… "Si j'utilise JDK1.8_40 ou plus récent (Oracle ou OpenJDK font de même), le code suivant avec un redimensionnement de la boîte de dialogue plantera l'application (essayé Windows 7, x64, JDK 64 bits)" - Le code ne fait que 40 lignes et provoque un crash correct de la JVM.
Dreamspace President

Réponses:

6

La chose la plus proche d'une seule "réponse" est de System.exit()mettre fin immédiatement à la machine virtuelle Java sans nettoyage approprié. Mais à part cela, le code natif et l'épuisement des ressources sont les réponses les plus probables. Sinon, vous pouvez aller chercher sur le bug tracker de Sun les bogues dans votre version de la JVM, dont certains permettent des scénarios de crash répétables. Nous avions l'habitude d'avoir des plantages semi-réguliers à l'approche de la limite de mémoire de 4 Go sous les versions 32 bits (nous utilisons généralement 64 bits maintenant).

Leigh Caldwell
la source
71
sans nettoyage adéquat? êtes-vous sûr? La documentation dit "Met fin à la machine virtuelle Java en cours d'exécution en lançant sa séquence d'arrêt ... tous les hooks d'arrêt enregistrés, le cas échéant, sont démarrés ... tous les finaliseurs non invoqués sont exécutés" - n'est-ce pas le bon nettoyage?
user85421
51
Cela ne plante pas la machine virtuelle Java, mais commence délibérément et explicitement un arrêt ordonné de l'exécution.
BenM
9
Runtime.getRuntime (). Halt (status) est plus proche du crash du jvm. Selon la documentation "cette méthode ne provoque pas le démarrage des hooks d'arrêt et n'exécute pas les finaliseurs non sollicités si la finalisation à la sortie a été activée". Toujours pas un crash, mais plus proche que System.exit.
henry
48
C'est vraiment une mauvaise réponse.
Antonio
174

Je n'appellerais pas lancer une OutOfMemoryError ou StackOverflowError un crash. Ce ne sont que des exceptions normales. Pour vraiment planter une VM, il y a 3 façons:

  1. Utilisez JNI et plantez dans le code natif.
  2. Si aucun gestionnaire de sécurité n'est installé, vous pouvez utiliser la réflexion pour planter la machine virtuelle. Ceci est spécifique à la VM, mais normalement une VM stocke un tas de pointeurs vers des ressources natives dans des champs privés (par exemple, un pointeur vers l'objet thread natif est stocké dans un long champ dans java.lang.Thread ). Changez-les simplement par réflexion et la VM plantera tôt ou tard.
  3. Toutes les VM ont des bogues, il vous suffit donc d'en déclencher un.

Pour la dernière méthode, j'ai un court exemple, qui fera planter une VM Sun Hotspot bien silencieusement:

public class Crash {
    public static void main(String[] args) {
        Object[] o = null;

        while (true) {
            o = new Object[] {o};
        }
    }
}

Cela conduit à un débordement de pile dans le GC donc vous n'obtiendrez pas StackOverflowError mais un vrai crash incluant un fichier hs_err *.

ralfs
la source
9
Hou la la! Cela plante Sun Java 5, Sun Java 6 et OpenJDK 6 (sur Ubuntu 9.04) sans fichier hs_err * mais uniquement avec une "erreur de segmentation!" ...
Joachim Sauer
1
System.exit () est un moyen beaucoup plus simple de planter une JVM (à moins qu'un gestionnaire de sécurité ne soit installé)
instantsetsuna
4
Je ne sais pas quand il a été corrigé, mais juste testé dans 1.7.0_09 et ça va. Je viens de recevoir l'attendu: Exception dans le thread "main" java.lang.OutOfMemoryError: espace de tas Java à CrashJVM.main (CrashJVM.java:7)
Tchad NB
2
J'ai essayé le code ci-dessus sur Intel Core i7 2,4 GHz / 8 Go de RAM / JDK1.7 64 bits mais JVM toujours actif après 20 minutes. (Drôle: le ventilateur de mon ordinateur portable était plus bruyant qu'un avion de combat dans le ciel). Ce problème est-il résolu dans JDK 1.7+?
realPK
3
Dans JDK 1.8_u71, cela provoque unjava.lang.OutOfMemoryError: GC overhead limit exceeded
Clashsoft
124

JNI . En fait, avec JNI, le crash est le mode de fonctionnement par défaut. Vous devez travailler très dur pour qu'il ne plante pas.

Dan Dyer
la source
4
Peut-être que les concepteurs de JNI avaient entendu parler de logiciels de crash seulement , mais n'avaient pas tout à fait saisi l'idée.
Tom Anderson
56

Utilisez ceci:

import sun.misc.Unsafe;

public class Crash {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    public static void crash() {
        unsafe.putAddress(0, 0);
    }
    public static void main(String[] args) {
        crash();
    }
}

Cette classe doit être sur le chemin de classe de démarrage car elle utilise du code de confiance, alors exécutez comme ceci:

java -Xbootclasspath / p :. crash

Dave Griffiths
la source
14
Au lieu d'utiliser -Xbootclasspath, vous pouvez également faire ceci: a Field f = Unsafe.class.getDeclaredField( "theUnsafe" ); f.setAccessible( true ); unsafe = (Unsafe) f.get( null );très bien fonctionné pour moi.
insistant
Unsafeest, par définition, «dangereux». C'est un peu une triche.
Hot Licks
1
getDeclaredFieldConfirmation de l' utilisation de l' astuce dans JDK 8u131 sous Linux x64, y compris la production d' hs_err_pid*.logun fichier SIGSEGV.
Jesse Glick
L'utilisation Unsafen'est pas une triche. OP ne cherche pas une solution «propre» à un problème de programmation. Il a besoin que la jvm plante de la manière la plus laide possible. Faire de mauvaises choses indigènes est ce qui peut y arriver, et c'est exactement ce qui Unsafefait.
jvdneste
34

Je suis venu ici parce que j'ai aussi rencontré cette question dans The Passionate Programmer , de Chad Fowler. Pour ceux qui n'ont pas accès à une copie, la question est formulée comme une sorte de filtre / test pour les candidats en entrevue pour un poste nécessitant de «très bons programmeurs Java».

Plus précisément, il demande:

Comment écririez-vous un programme, en Java pur, qui ferait planter la machine virtuelle Java?

J'ai programmé en Java pendant plus de 15 ans et j'ai trouvé cette question à la fois déroutante et injuste. Comme d'autres l'ont souligné, Java, en tant que langage géré, est spécifiquement conçu pour ne pas planter . Bien sûr, il y a toujours des bogues JVM, mais:

  1. Après plus de 15 ans de JRE de niveau production, c'est rare.
  2. De tels bogues sont susceptibles d'être corrigés dans la prochaine version, alors quelle est la probabilité que vous, en tant que programmeur, rencontriez et rappeliez les détails de l'ensemble actuel de show-stoppers JRE?

Comme d'autres l'ont mentionné, du code natif via JNI est un moyen sûr de planter un JRE. Mais l'auteur a spécifiquement mentionné en Java pur , donc c'est fini.

Une autre option consisterait à alimenter les faux codes d'octets JRE; il est assez facile de vider des données binaires inutiles dans un fichier .class et de demander au JRE de l'exécuter:

$ echo 'crap crap crap' > crap.class
$ java crap
Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1668440432 in class file crap

Cela compte-t-il? Je veux dire que le JRE lui-même ne s'est pas écrasé; il a correctement détecté le faux code, l'a signalé et est sorti.

Cela nous laisse avec les types de solutions les plus évidents tels que le gonflement de la pile via la récursivité, le manque de mémoire du tas via des allocations d'objets ou simplement le lancement RuntimeException. Mais cela provoque simplement la fermeture du JRE avec une StackOverflowErrorexception ou une exception similaire, ce qui, encore une fois, n'est pas vraiment un crash .

Alors que reste-t-il? J'adorerais vraiment entendre ce que l'auteur avait vraiment en tête comme solution appropriée.

Mise à jour : Chad Fowler a répondu ici .

PS: c'est un livre autrement génial. Je l'ai pris pour un soutien moral tout en apprenant Ruby.

George Armhold
la source
1
Ces questions d'entretien servent de test décisif pour les développeurs Java qui existent depuis assez longtemps pour 1) avoir été témoin de (nombreux) plantages de JVM et 2) avoir tiré des conclusions ou, mieux encore, comme des experts Java (autoproclamés) ont essayé de comprendre le les raisons sous-jacentes d'un accident. Chad Fowler déclare également dans son livre que les programmeurs Java autoproclamés "ne pouvaient même pas trouver la mauvaise réponse", c'est-à-dire qu'ils n'ont pas essayé de penser à quand toute une classe de problèmes potentiels. La ligne du bas: cette question est segway à "Comment éviter les plantages JVM?" ce qui est clairement encore mieux à savoir.
Shonzilla
1
Je chercherais des gens qui répondent simplement (comme la plupart ici l'ont fait) "déborder la pile", system.exit (), ou tout autre arrêt "normal" car ils ne comprennent pas vraiment la JVM ou le mot "Crash". Reconnaître (comme vous l'avez fait) que cela est très anormal est un très bon moyen d'identifier un programmeur plus avancé. Je suis d'accord avec votre première déclaration, je la trouverais excellente et tout à fait juste à poser ou à poser - celles qui n'ont pas de réponses concrètes sont toujours les meilleures.
Bill K le
20

Ce code plantera la JVM de manière désagréable

import sun.dc.pr.PathDasher; 

public class Crash
{
     public static void main(String[] args)
     {    
        PathDasher dasher = new PathDasher(null) ;
     }
}
Rob Mayhew
la source
1
Obtention d'une erreur de compilation lors de l'utilisation de JDK 1.7 avec ce code: Restriction d'accès: Le type PathDasher n'est pas accessible en raison d'une restriction sur la bibliothèque requise C: \ Program Files \ Java \ jdk1.7.0_51 \ jre \ lib \ rt.jar
realPK
7
Cela lance un InternalErrorJDK 1.8. JVM n'échoue plus.
Sotirios Delimanolis
18

La dernière fois que j'ai essayé, cela le ferait:

public class Recur {
    public static void main(String[] argv) {
        try {
            recur();
        }
        catch (Error e) {
            System.out.println(e.toString());
        }
        System.out.println("Ended normally");
    }
    static void recur() {
        Object[] o = null;
        try {
            while(true) {
                Object[] newO = new Object[1];
                newO[0] = o;
                o = newO;
            }
        }
        finally {
            recur();
        }
    }
}

Première partie du fichier journal généré:

#
# An unexpected error has been detected by Java Runtime Environment:
#
#  EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x000000006dad5c3d, pid=6752, tid=1996
#
# Java VM: Java HotSpot(TM) 64-Bit Server VM (11.2-b01 mixed mode windows-amd64)
# Problematic frame:
# V  [jvm.dll+0x2e5c3d]
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
#

---------------  T H R E A D  ---------------

Current thread (0x00000000014c6000):  VMThread [stack: 0x0000000049810000,0x0000000049910000] [id=1996]

siginfo: ExceptionCode=0xc00000fd, ExceptionInformation=0x0000000000000001 0x0000000049813fe8 

Registers:
EAX=0x000000006dc83090, EBX=0x000000003680f400, ECX=0x0000000005d40ce8, EDX=0x000000003680f400
ESP=0x0000000049813ff0, EBP=0x00000000013f2df0, ESI=0x00000000013f0e40, EDI=0x000000003680f400
EIP=0x000000006dad5c3d, EFLAGS=0x0000000000010206
Hot Licks
la source
Je suis un peu amusé par le vote défavorable, étant donné que c'est l'une des deux seules réponses montrant comment le faire uniquement avec du code Java, et comprend le code complet.
Hot Licks
Je suis d'accord pour dire qu'un vote défavorable n'est pas justifié - c'est un véritable crash, mais comment répondriez-vous à la question de l'entrevue. À moins que vous n'ayez déjà recherché et mémorisé cela, vous ne pouvez pas le donner comme réponse dans l'interview et si vous le pouviez, je considérerais de toute façon une réponse neutre à mauvaise - cela ne montre pas comment vous aborderiez le problème. Je m'attends à ce que vous ayez fait google pour les exploits de bogues jvm et en implémenter un qui est une excellente réponse.
Bill K
@BillK - Non, ce qui précède est entièrement mon propre travail, même si je l'ai inventé il y a plusieurs années, en expérimentant diverses choses. Dans une interview, je dirais probablement "Je l'ai fait, en utilisant try / catch et récursivité, mais je ne peux pas trouver le code exact pour le moment."
Hot Licks
1
Quels sont les détails (version JDK, tous arguments VM)? Je ne sais pas quelle est la version "11.2-b0". J'exécute ceci, mais cela ne consomme que beaucoup de CPU.
Stefan Reich
@Hot Licks: Quel est le concept dans l'exemple de crash JVM? Pourquoi JVM plante à propos du code. Tous les threads ont des threads de pile séparés ...
VJS
15

Une implémentation JVM parfaite ne plantera jamais.

Pour planter une JVM, à part JNI, vous devez trouver un bogue dans la VM elle-même. Une boucle infinie consomme simplement du processeur. L'allocation de mémoire à l'infini devrait simplement provoquer des OutOfMemoryError dans une JVM bien construite. Cela poserait probablement des problèmes pour d'autres threads, mais une bonne JVM ne devrait toujours pas planter.

Si vous pouvez trouver un bogue dans le code source de la VM, et par exemple provoquer une erreur de segmentation dans l'utilisation de la mémoire de l'implémentation de la VM, vous pouvez en fait la planter.

Dave L.
la source
14

Si vous souhaitez planter JVM, utilisez ce qui suit dans Sun JDK 1.6_23 ou version antérieure:

Double.parseDouble("2.2250738585072012e-308");

Cela est dû à un bogue dans Sun JDK - également trouvé dans OpenJDK. Ce problème est résolu à partir d'Oracle JDK 1.6_24.

Prabath Siriwardena
la source
10

Cela dépend de ce que vous entendez par crash.

Vous pouvez faire une récursion infinie pour le faire manquer d'espace dans la pile, mais cela plantera "gracieusement". Vous obtiendrez une exception, mais la JVM elle-même gérera tout.

Vous pouvez également utiliser JNI pour appeler du code natif. Si vous ne le faites pas correctement, vous pouvez le rendre difficile. Déboguer ces plantages est "amusant" (croyez-moi, j'ai dû écrire une grosse DLL C ++ que nous appelons à partir d'une applet java signée). :)

Herms
la source
6

Le livre Java Virtual Machine de Jon Meyer présente un exemple d'une série d'instructions de bytecode qui ont provoqué le vidage de mémoire de la JVM. Je ne trouve pas mon exemplaire de ce livre. Si quelqu'un en a un, veuillez le rechercher et poster la réponse.

Reevesy
la source
5

sur winxpsp2 avec wmp10 jre6.0_7

Desktop.open (uriToAviOrMpgFile)

Cela provoque un thread généré pour lancer un Throwable non intercepté et bloque le hotspot

YMMV


la source
5

Un matériel cassé peut planter n'importe quel programme. Une fois, j'ai eu un crash d'application reproductible sur une machine spécifique tout en fonctionnant correctement sur d'autres machines avec exactement la même configuration. Il s'avère que cette machine avait une RAM défectueuse.

Michael Borgwardt
la source
5

le plus court possible :)

public class Crash
{
    public static void main(String[] args)
    {
        main(args);
    }
}
RRM
la source
Ne plante pas. Cela donne une erreur de compilation Exception in thread "main" java.lang.StackOverflowError at Test.main. J'utilise jdk1.8.0_65
Quazi Irfan
5

Pas un crash, mais plus proche d'un crash que la réponse acceptée de l'utilisation System.exit

Vous pouvez arrêter la JVM en appelant

Runtime.getRuntime().halt( status )

Selon la documentation: -

"Cette méthode ne provoque pas le démarrage des hooks d'arrêt et n'exécute pas les finaliseurs non invoqués si la finalisation à la sortie a été activée".

Henri
la source
4

Si vous voulez faire comme si vous n'aviez plus de mémoire, vous pouvez le faire

public static void main(String[] args) {
    throw new OutOfmemoryError();
}

Je connais deux façons de provoquer le vidage d'un fichier d'erreur par la JVM en appelant des méthodes natives (celles qui sont intégrées), mais il est probablement préférable que vous ne sachiez pas comment faire cela. ;)

Peter Lawrey
la source
4

Si vous définissez un crash comme un abandon de processus en raison d'une situation non gérée (c'est-à-dire pas d'exception ou d'erreur Java), cela ne peut pas être fait depuis Java (à moins que vous n'ayez l'autorisation d'utiliser la classe sun.misc.Unsafe). C'est tout l'intérêt du code managé.

Les plantages typiques dans le code natif se produisent en dé-référençant des pointeurs vers des zones de mémoire incorrectes (adresse nulle ou mal aligné). Une autre source pourrait être des instructions machine illégales (opcodes) ou des signaux non gérés provenant d'appels de bibliothèque ou de noyau. Les deux peuvent être déclenchés si la JVM ou les bibliothèques système ont des bogues.

Par exemple, le code JITed (généré), les méthodes natives ou les appels système (pilote graphique) peuvent avoir des problèmes menant à de vrais plantages (il était assez courant d'avoir un crash lorsque vous utilisiez des fonctions ZIP et qu'elles manquaient de mémoire). Dans ces cas, le gestionnaire de crash de la JVM démarre et vide l'état. Il pourrait également générer un fichier core du système d'exploitation (Dr. Watson sur Windows et core dump sur * nix).

Sous Linux / Unix, vous pouvez facilement faire planter une JVM en lui envoyant un signal au processus en cours d'exécution. Remarque: vous ne devez pas utiliser SIGSEGVpour cela, car Hotspot capte ce signal et le relance comme une NullPointerException dans la plupart des endroits. Il vaut donc mieux envoyer un SIGBUSpar exemple.

eckes
la source
3

JNI est une source importante de plantages. Vous pouvez également planter en utilisant l'interface JVMTI car cela doit également être écrit en C / C ++.

Jared
la source
2

Si vous créez un processus de thread qui génère infiniment plus de threads (qui génèrent plus de threads, ce qui ...), vous finirez par provoquer une erreur de débordement de pile dans la JVM elle-même.

public class Crash {
    public static void main(String[] args) {

        Runnable[] arr = new Runnable[1];
        arr[0] = () -> {

            while (true) {
                new Thread(arr[0]).start();
            }
        };

        arr[0].run();
    }
}

Cela m'a donné la sortie (après 5 minutes, surveillez votre bélier)

An unrecoverable stack overflow has occurred.
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x0000000070e53ed7, pid=12840, tid=0x0000000000101078
#
# JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# 
Lightfire228
la source
1

Si par "crash" vous entendez un abandon brutal de la JVM, comme cela provoquerait l'écriture de la JVM dans son hs_err_pid% p.log , vous pouvez le faire de cette façon.

Définissez l'argument-Xmx sur une valeur minuscule et indiquez à la machine virtuelle Java de forcer un crash en cas de mémoire insuffisante:

 -Xmx10m -XX:+CrashOnOutOfMemoryError

Pour être clair, sans le deuxième argument ci-dessus, il en résulterait simplement que le jvm se terminerait par une OutOfMemoryError, mais il ne "planterait" pas ou n'abandonnerait pas brusquement le jvm.

Cette technique s'est avérée utile lorsque j'essayais de tester l'arg JVM -XX: ErrorFile, qui contrôle où un tel journal hs_err_pid doit être écrit. J'avais trouvé ce post ici, en essayant de trouver des moyens de forcer un tel crash. Quand j'ai trouvé plus tard que ce qui était le plus simple pour mon besoin, j'ai voulu l'ajouter à la liste ici.

Enfin, FWIW, si quelqu'un peut tester cela alors qu'il a déjà une valeur -Xms définie dans vos arguments (à une valeur plus grande que ci-dessus), vous voudrez supprimer ou modifier cela également, ou vous n'obtiendrez pas un plantage mais simplement un échec du démarrage du jvm, signalant «La taille initiale du tas est définie sur une valeur supérieure à la taille maximale du tas». (Cela ne serait pas évident si vous exécutiez la JVM en tant que service, comme avec certains serveurs d'applications. Encore une fois, cela m'a mordu, alors je voulais le partager.)

Charlie Arehart
la source
0

Si vous changez cette boucle for infinie en un appel récursif à la même fonction, vous obtiendrez une exception de débordement de pile:

public static void main(String[] args) {
    causeStackOverflow();
}

public void causeStackOverflow() {
    causeStackOverflow();
}
Mike Stone
la source
0

Je le fais maintenant, mais je ne sais pas vraiment comment ... :-) JVM (et mon application) disparaissent parfois complètement. Aucune erreur générée, rien enregistré. Passe de travailler à ne pas fonctionner du tout instantanément sans avertissement.

Brian Knoblauch
la source
Commencez à vérifier votre matériel, en particulier votre mémoire!
Thorbjørn Ravn Andersen
Malheureusement, c'est sur plusieurs machines qui fonctionnent autrement très bien. Ce n'est que cette application en particulier qui le fait (et ce n'est pas gourmand en mémoire ou en processeur).
Brian Knoblauch
0

Le plus court? Utilisez la classe Robot pour déclencher CTRL + BREAK. J'ai repéré cela lorsque j'essayais de fermer mon programme sans fermer la console (il n'avait pas de fonctionnalité de «sortie»).


la source
Ancienne question - j'espère que quelqu'un bénéficiera de votre réponse à l'avenir.
dbmitch
0

Cela compte-t-il?

long pid = ProcessHandle.current().pid();
try { Runtime.getRuntime().exec("kill -9 "+pid); } catch (Exception e) {}

Cela ne fonctionne que pour Linux et à partir de Java 9.

Pour une raison quelconque, je n'obtiens pas, ProcessHandle.current().destroyForcibly();ne tue pas la JVM et jette java.lang.IllegalStateExceptionavec le message la destruction du processus actuel non autorisée .

mszmurlo
la source
0

Vous rencontrez ce problème lors de la tentative de réplication du plantage de la JVM.

Jni fonctionne, mais il doit être peaufiné pour différentes plates-formes. Finalement, j'utilise cette combinaison pour faire planter JVM

  1. Démarrez l'application avec ces options JVM -XX:+CrashOnOutOfMemoryError
  2. Utilisez a long[] l = new long[Integer.MAX_VALUE];pour déclencher le MOO

Ensuite, JVM plantera et générera le journal des plantages.

Frank Teng
la source
-2

Si un «Crash» est quelque chose qui interrompt le jvm / programme de l'arrêt normal, alors une exception non gérée pourrait le faire.

public static void main(String args[]){
   int i = 1/0;
   System.out.print(i); // This part will not be executed due to above  unhandled exception
  }

Alors, cela dépend de quel type de CRASH?!

Sankarganesh Eswaran
la source
3
Lancer une exception n'est pas un crash.
Quazi Irfan
Les exceptions d'exécution non gérées sont des plantages, cependant, qui ArithmeticExceptionest
mi