web-dev-qa-db-fra.com

Comment écrasez-vous une machine virtuelle?

Je lisais un livre sur les compétences en programmation dans lequel l'auteur demandait à l'interviewé: "Comment écraser une JVM?" Je pensais que vous pourriez le faire en écrivant une boucle infinie qui finirait par utiliser toute la mémoire.

Quelqu'un a une idée?

141
Shivasubramanian A

La chose la plus proche d'une "réponse" unique est System.exit() qui met immédiatement fin à la machine virtuelle Java sans nettoyage approprié. Mais à part cela, l'épuisement du code natif et des ressources est la réponse la plus probable. Sinon, vous pouvez consulter le gestionnaire de bogues de Sun pour rechercher des bogues dans votre version de la machine virtuelle Java, dont certains permettent des scénarios de crash répétables. Nous avions l'habitude d'avoir des plantages quasi réguliers lorsque nous approchions de la limite de mémoire de 4 Gb sous les versions 32 bits (nous utilisons généralement le 64 bits maintenant).

6
Leigh Caldwell

Je n'appellerais pas lancer un OutOfMemoryError ou StackOverflowError un crash. Ce ne sont que des exceptions normales. Pour planter vraiment un 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 faire planter la machine virtuelle. Ceci est VM spécifique, mais normalement un VM stocke un tas de pointeurs sur des ressources natives dans des champs privés (par exemple, un pointeur sur l'objet thread natif est stocké) un long champ dans Java.lang.Thread). Il suffit de les changer par réflexion et le VM va planter tôt ou tard.
  3. Tous les ordinateurs virtuels ont des bogues, vous devez donc en déclencher un.

Pour la dernière méthode, j’ai un bref exemple, qui plantera un Sun Hotspot VM

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

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

Cela entraîne un débordement de pile dans le CPG et vous n'obtiendrez donc pas de StackOverflowError mais un véritable crash, y compris un fichier hs_err *.

172
ralfs

JNI . En fait, avec JNI, le crash est le mode de fonctionnement par défaut. Vous devez travailler très fort pour ne pas tomber en panne.

123
Dan Dyer

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 un code de confiance. Exécutez-la comme suit:

Java -Xbootclasspath/p :. crash

56
Dave Griffiths

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 qui passent une entrevue pour un poste exigeant de "très bons Java") ".

Plus précisément, il demande:

Comment écririez-vous un programme, en Java pur, qui provoquerait le blocage de la Java machine virtuelle)?

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

  1. Après 15 ans de JRE au niveau de la production, c'est rare.
  2. De tels bogues seront probablement corrigés dans la prochaine version. Dans ce cas, en tant que programmeur, avez-vous des chances de rencontrer et de rappeler les détails du jeu actuel de JRE show-stoppers?

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 , c'est donc fini.

Une autre option serait d’alimenter les faux octets de JRE; Il est assez facile de vider certaines données binaires 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 la JRE elle-même n'a pas planté; il a correctement détecté le code factice, l'a signalé et est sorti.

Cela nous laisse avec les types de solutions les plus évidents, tels que vider la pile via la récursion, manquer de mémoire vive via les allocations d'objets ou simplement lancer RuntimeException. Mais cela provoque simplement la fermeture de JRE avec une exception StackOverflowError ou similaire, ce qui, encore une fois n'est pas vraiment un crash .

Alors qu'est-ce qui reste? J'aimerais vraiment entendre ce que l'auteur pensait vraiment comme solution appropriée.

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

PS: c'est un excellent livre. Je l'ai ramassé pour un soutien moral tout en apprenant Ruby.

32
George Armhold

Ce code plantera la JVM de manière méchante

import Sun.dc.pr.PathDasher; 

public class Crash
{
     public static void main(String[] args)
     {    
        PathDasher dasher = new PathDasher(null) ;
     }
}
20
Rob Mayhew

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
17
Hot Licks

Une mise en œuvre parfaite de la machine virtuelle Java ne plantera jamais.

Pour faire planter une machine virtuelle, en dehors de JNI, vous devez trouver un bogue dans la machine virtuelle VM). Une boucle infinie consomme tout simplement le processeur. Allouer de la mémoire à l'infini devrait uniquement entraîner la suppression de OutOfMemoryError dans une machine virtuelle bien construite. causerait probablement des problèmes pour d’autres threads, mais une bonne machine virtuelle ne devrait toujours pas planter.

Si vous trouvez un bogue dans le code source de la machine virtuelle et causez, par exemple, une erreur de segmentation dans l'utilisation de la mémoire par la mise en œuvre de la machine virtuelle, vous pouvez réellement le bloquer.

15
Dave L.

Si vous voulez faire planter la machine virtuelle Java, utilisez les éléments suivants dans Sun JDK 1.6_23 ou inférieur:

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

Cela est dû à un bug dans Sun JDK - également trouvé dans OpenJDK. Ceci est corrigé à partir d'Oracle JDK 1.6_24.

14

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

Vous pouvez faire une récursion infinie pour le faire manquer d'espace de pile, mais cela plantera "gracieusement". Vous obtiendrez une exception, mais la machine virtuelle elle-même se chargera de tout.

Vous pouvez également utiliser JNI pour appeler du code natif. Si vous ne le faites pas juste comme il faut, vous pouvez le rendre dur. Déboguer ces crashs est "amusant" (croyez-moi, je devais écrire un gros C++ DLL que nous appelons depuis un Java) signé). :)

10
Herms

Le livre Java Virtual Machine , de Jon Meyer, contient un exemple d'une série d'instructions de bytecode qui ont provoqué le vidage 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.

6
Soulfly

Le matériel cassé peut planter n’importe quel programme. Une fois, une application a planté de manière reproductible sur une machine spécifique alors que tout fonctionnait correctement sur d'autres machines dotées de la même configuration. Il s'avère que cette machine avait une RAM défectueuse.

5

Pas un crash, mais plus proche d'un crash que la réponse acceptée d'utiliser 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 points d'arrêt à l'arrêt et n'exécute pas de finaliseurs non invoqués si la finalisation à la sortie a été activée".

5
henry

chemin le plus court possible :)

public class Crash
{
    public static void main(String[] args)
    {
        main(args);
    }
}
5
RRM

sur winxpsp2 avec wmp10 jre6.0_7

Desktop.open (uriToAviOrMpgFile)

Cela provoque un fil spawned à jeter un hotspot Throwable non bloqué

YMMV

5
kitsuneymg

Si vous définissez un blocage comme un arrêt de processus dû à une situation non gérée (c.-à-d. No Java Exception ou Error), cela ne peut pas être effectué de l'intérieur Java (sauf si vous êtes autorisé à utiliser la classe Sun.misc.Unsafe): c'est tout le problème du code géré.

Les plantages typiques dans le code natif se produisent en dé-référençant les pointeurs aux zones de mémoire incorrectes (adresse nulle ou mal alignée). Une autre source pourrait être des instructions de machine illégales (opcodes) ou des signaux non gérés provenant d'appels à la bibliothèque ou au 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 entraîner des problèmes entraînant de véritables plantages (il était assez courant de provoquer un blocage lorsque vous utilisiez des fonctions Zip et que leur mémoire était insuffisante). Dans ces cas, le gestionnaire d'accidents de la machine virtuelle Java déclenche et exporte l'état. Il pourrait également générer un fichier de base du système d'exploitation (Dr. Watson sous Windows et core dump sous * nix).

Sous Linux/Unix, vous pouvez facilement faire planter une machine virtuelle Java en lui envoyant un signal au processus en cours. Remarque: vous ne devez pas utiliser SIGSEGV pour cela, car Hotspot capte ce signal et le relance sous la forme d'une exception NullPointerException dans la plupart des endroits. Il est donc préférable d’envoyer un SIGBUS par exemple.

4
eckes

voici une explication détaillée sur les causes de l’épuisement de la JVM (p.ex. crash): http://kb.Adobe.com/selfservice/viewContent.do?externalId=tn_17534

4
COTOHA

Si vous voulez faire semblant d'être à court de mémoire, vous pouvez le faire

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

Je connais un moyen de provoquer le vidage d'un fichier d'erreur par la machine virtuelle Java en appelant des méthodes natives (intégrées), mais il est probablement préférable de ne pas savoir comment procéder. ;)

3
Peter Lawrey

JNI est une source importante d'accidents. Vous pouvez également planter en utilisant l'interface JVMTI, car celle-ci doit également être écrite en C/C++.

3
Jared

Si vous créez un processus de thread qui génère infiniment plus de threads (ce qui génère plus de threads, qui ...), vous causerez éventuellement une erreur de débordement de pile dans la machine virtuelle 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:
# 
2
Lightfire228

Le plus court? Utilisez la classe Robot pour déclencher CTRL + BREAK. Je l’ai repéré lorsque j’essayais de fermer mon programme sans fermer la console (il n’existait aucune fonctionnalité de sortie).

1
user6022288

Je le fais maintenant, mais je ne sais pas trop comment ... :-) Parfois, la JVM (et mon application) disparaissent complètement. Aucune erreur jetée, rien enregistré. Va de travailler à ne pas courir du tout instantanément sans avertissement.

0
Brian Knoblauch

Si, par "crash", vous entendez un arrêt brutal de la machine virtuelle Java, par exemple, la machine virtuelle Java écrirait dans son fichier hs_err_pid% p.log, vous pouvez le faire de cette façon.

Définissez the-Xmx arg sur une valeur infime et indiquez à la machine virtuelle Java de forcer un arrêt brutal de la mémoire:

 -Xmx10m -XX:+CrashOnOutOfMemoryError

Pour être clair, sans le deuxième argument ci-dessus, il en résulterait simplement que jvm terminant avec une erreur OutOfMemoryError, mais cela ne "planterait" pas ou n'abandonnerait pas brutalement jvm.

Cette technique s'est avérée utile lorsque j'ai essayé de tester l'argument JVM -XX: ErrorFile, qui contrôle l'emplacement 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. Lorsque, plus tard, j’ai trouvé que ce qui précède fonctionnait le plus facilement, j’ai voulu l’ajouter à la liste.

Enfin, FWIW, si quelqu'un peut tester cela alors qu’il a déjà une valeur -Xms définie dans vos arguments (à une valeur supérieure à celle ci-dessus), vous voudrez également le supprimer ou le modifier, ou vous obtiendrez non pas un crash, mais simplement une erreur de démarrage de jvm, signalant "Taille initiale du segment définie sur une valeur supérieure à la taille maximale du segment". (Cela ne serait pas évident si vous exécutiez la machine virtuelle Java en tant que service, comme avec certains serveurs d'applications. Encore une fois, cela m’a mordu et j’ai voulu le partager.)

0
charlie arehart

Est-ce que ça compte?

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

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

Pour une raison quelconque, je ne reçois pas, ProcessHandle.current().destroyForcibly(); ne tue pas la machine virtuelle Java et jette Java.lang.IllegalStateException Avec le message la destruction du processus en cours n'est pas autorisée.

0
mszmurlo