¿Cómo se bloquea una JVM?


144

Estaba leyendo un libro sobre habilidades de programación en el que el autor le pregunta al entrevistado: "¿Cómo se bloquea una JVM?" Pensé que podría hacerlo escribiendo un bucle for infinito que eventualmente agotaría toda la memoria.

Alguien tiene alguna idea?



stackoverflow.com/questions/30072883/… "Si uso JDK1.8_40 o más reciente (Oracle u OpenJDK hacen lo mismo), el siguiente código junto con un cambio de tamaño del cuadro de diálogo bloqueará la aplicación (probé Windows 7, x64, 64 bits JDK)" - El código tiene solo 40 líneas y causa un bloqueo adecuado de la JVM.
Presidente de Dreamspace

Respuestas:


6

Lo más parecido a una sola "respuesta" es System.exit()que termina la JVM inmediatamente sin una limpieza adecuada. Pero aparte de eso, el código nativo y el agotamiento de los recursos son las respuestas más probables. Alternativamente, puede buscar errores en el rastreador de errores de Sun en su versión de la JVM, algunos de los cuales permiten escenarios de fallas repetibles. Solíamos tener bloqueos semi-regulares cuando nos acercamos al límite de memoria de 4 Gb en las versiones de 32 bits (generalmente usamos 64 bits ahora).


71
sin una limpieza adecuada? ¿Estás seguro? La documentación dice "Termina la máquina virtual Java actualmente en ejecución iniciando su secuencia de apagado ... todos los ganchos de apagado registrados, si los hay, se inician ... se ejecutan todos los finalizadores no invocados" - ¿no es esa la limpieza adecuada?
user85421

51
Esto no está bloqueando la JVM, está comenzando deliberadamente y explícitamente un cierre ordenado de la ejecución.
BenM

9
Más cerca de bloquear el jvm es Runtime.getRuntime (). Halt (status). De acuerdo con los documentos, "este método no hace que se inicien los ganchos de cierre y no ejecuta finalizadores no invocados si se ha habilitado la finalización al salir". Todavía no es un accidente, pero está más cerca que System.exit.
Henry

48
Esta es realmente una mala respuesta.
Antonio

174

No llamaría lanzar un OutOfMemoryError o StackOverflowError como un bloqueo. Estas son solo excepciones normales. Para realmente bloquear una VM, hay 3 formas:

  1. Use JNI y bloquee el código nativo.
  2. Si no hay un administrador de seguridad instalado, puede usar la reflexión para bloquear la VM. Esto es específico de VM, pero normalmente una VM almacena un montón de punteros a recursos nativos en campos privados (por ejemplo, un puntero al objeto de hilo nativo se almacena en un campo largo en java.lang.Thread ). Simplemente cámbielos por reflexión y la VM se bloqueará tarde o temprano.
  3. Todas las máquinas virtuales tienen errores, por lo que solo debe activar uno.

Para el último método, tengo un breve ejemplo, que bloqueará muy bien una máquina virtual Sun Hotspot:

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

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

Esto conduce a un desbordamiento de pila en el GC, por lo que no obtendrá StackOverflowError sino un bloqueo real que incluye un archivo hs_err *.


9
¡Guauu! Esto bloquea Sun Java 5, Sun Java 6 y OpenJDK 6 (en Ubuntu 9.04) sin el archivo hs_err * pero solo con un "¡Error de segmentación!" ...
Joachim Sauer

1
System.exit () es una forma mucho más fácil de bloquear una JVM (a menos que esté instalado un administrador de seguridad)
instantsetsuna

44
No sé cuándo se solucionó, pero acaba de probar en 1.7.0_09 y está bien. Acabo de recibir lo esperado: Excepción en el hilo "main" java.lang.OutOfMemoryError: espacio de almacenamiento dinámico de Java en CrashJVM.main (CrashJVM.java:7)
Chad NB

2
Probé el código anterior en Intel Core i7 2.4 GHz / 8 GB RAM / JDK1.7 64bit pero JVM aún funciona después de 20 minutos. (Divertido: el ventilador de mi computadora portátil era más fuerte que un avión de combate en el cielo). ¿Se solucionó este problema en JDK 1.7+?
realPK

3
En JDK 1.8_u71 esto causa unjava.lang.OutOfMemoryError: GC overhead limit exceeded
Clashsoft


56

Utilizar este:

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();
    }
}

Esta clase debe estar en el classpath de arranque porque está usando un código de confianza, por lo que debe ejecutarse así:

java -Xbootclasspath / p :. Choque


14
En lugar de usar -Xbootclasspath, también puedes hacer esto: Field f = Unsafe.class.getDeclaredField( "theUnsafe" ); f.setAccessible( true ); unsafe = (Unsafe) f.get( null );funcionó muy bien para mí.
agresivo

Unsafees, por definición, "inseguro". Esto es un poco tramposo.
Hot Licks

1
Se confirmó que funcionaba con el getDeclaredFieldtruco en JDK 8u131 en Linux x64, incluida la producción de hs_err_pid*.loga SIGSEGV.
Jesse Glick

Usar Unsafeno es una trampa. OP no está buscando una solución 'limpia' para un problema de programación. Necesita que el JVM se estrelle de la manera más fea posible. Hacer cosas nativas desagradables es lo que puede hacer que eso suceda, y es exactamente lo que Unsafehace.
jvdneste

34

Vine aquí porque también me encontré con esta pregunta en The Passionate Programmer , de Chad Fowler. Para aquellos que no tienen acceso a una copia, la pregunta se enmarca como una especie de filtro / prueba para los candidatos que se entrevistan para un puesto que requiere "realmente buenos programadores de Java".

Específicamente, él pregunta:

¿Cómo escribiría un programa, en Java puro, que causaría el bloqueo de la máquina virtual Java?

He programado en Java durante más de 15 años, y esta pregunta me pareció desconcertante e injusta. Como otros han señalado, Java, como lenguaje administrado, está específicamente diseñado para no bloquearse . Por supuesto, siempre hay errores de JVM, pero:

  1. Después de más de 15 años de JRE de nivel de producción, es raro.
  2. Es probable que dichos errores se reparen en la próxima versión, entonces, ¿qué tan probable es que usted, como programador, se encuentre y recuerde los detalles del conjunto actual de JRE show-stoppers?

Como otros han mencionado, algunos códigos nativos a través de JNI son una forma segura de bloquear un JRE. Pero el autor mencionó específicamente en Java puro , así que eso está fuera.

Otra opción sería alimentar los códigos de bytes falsos de JRE; es bastante fácil volcar algunos datos binarios basura en un archivo .class y pedirle al JRE que lo ejecute:

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

¿Eso cuenta? Quiero decir que el JRE en sí no se ha estrellado; detectó correctamente el código falso, lo informó y salió.

Esto nos deja con los tipos más obvios de soluciones, como volar la pila a través de la recursión, quedarse sin memoria de almacenamiento dinámico a través de asignaciones de objetos o simplemente lanzar RuntimeException. Pero esto solo hace que el JRE salga con una StackOverflowErrorexcepción o similar, que, de nuevo, no es realmente un bloqueo .

Entonces, ¿qué queda? Realmente me encantaría escuchar lo que el autor realmente tenía en mente como una solución adecuada.

Actualización : Chad Fowler respondió aquí .

PD: es un gran libro. Lo recogí como apoyo moral mientras aprendía Ruby.


1
Estas preguntas de la entrevista sirven como pruebas de fuego para los desarrolladores de Java que han existido el tiempo suficiente para 1) haber presenciado (muchos) bloqueos de JVM y 2) han sacado algunas conclusiones o, mejor aún, a medida que los expertos Java (autoproclamados) han intentado comprender razones subyacentes para un accidente. Chad Fowler también afirma en su libro que los autoproclamados programadores de Java "ni siquiera pudieron encontrar la respuesta incorrecta", es decir, no han intentado pensar en una clase entera de problemas potenciales. El resultado final: esta pregunta es segway a "¿Cómo evitar accidentes JVM?" lo cual es claramente aún mejor saber.
Shonzilla

1
Buscaría personas que simplemente respondan (como lo hizo la mayoría aquí) "desbordar la pila", system.exit () u otro apagado "Normal" ya que realmente no entienden la JVM o la palabra "Crash". Reconocer (como lo hizo) que esto es muy anormal es una muy buena manera de identificar un programador más avanzado. Estoy de acuerdo con su primera declaración, creo que es una pregunta excelente y totalmente justa para hacer o que se les haga: las que no tienen respuestas concretas son siempre las mejores.
Bill K

20

Este código bloqueará la JVM de maneras desagradables

import sun.dc.pr.PathDasher; 

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

1
Obteniendo un error de compilación al usar JDK 1.7 con este código: Restricción de acceso: El tipo PathDasher no es accesible debido a la restricción en la biblioteca requerida C: \ Archivos de programa \ Java \ jdk1.7.0_51 \ jre \ lib \ rt.jar
realPK

77
Esto arroja un InternalErrorJDK 1.8. JVM ya no falla.
Sotirios Delimanolis

18

La última vez que intenté esto lo haría:

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();
        }
    }
}

Primera parte del archivo de registro generado:

#
# 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

Estoy un poco divertido con el voto negativo, dado que esta es una de las dos únicas respuestas que muestran cómo hacerlo únicamente con código Java e incluye el código completo.
Hot Licks

Estoy de acuerdo en que un voto negativo no está justificado; este es un verdadero accidente, pero ¿cómo respondería a la pregunta de la entrevista? A menos que haya investigado y memorizado esto anteriormente, no podría darlo como respuesta en la entrevista y, si pudiera, consideraría una respuesta neutral a pobre de todos modos, no muestra cómo abordaría el problema. Espero que lo que hiciste fue google para exploits de errores de jvm e implemente uno que es una excelente respuesta.
Bill K

@BillK - No, lo anterior es completamente mi propio trabajo, aunque se me ocurrió hace varios años, experimentando con varias cosas. En una entrevista, probablemente diría "Lo he hecho, usando try / catch y recursión, pero no puedo recitar el código exacto en este momento".
Hot Licks

1
¿Cuáles son los detalles (versión JDK, cualquier argumento de VM)? No estoy seguro de qué versión es "11.2-b0". Estoy ejecutando esto, pero solo consume mucha CPU.
Stefan Reich

@ Hot Licks: ¿Cuál es el concepto en el ejemplo de bloqueo JVM? Por qué JVM se bloquea sobre el código. Todo el hilo tiene un hilo de pila separado ...
VJS

15

Una implementación perfecta de JVM nunca se bloqueará.

Para bloquear un JVM, aparte de JNI, debe encontrar un error en la propia máquina virtual. Un bucle infinito solo consume CPU. La asignación infinita de memoria solo debería causar OutOfMemoryError en una JVM bien construida. Esto probablemente causaría problemas para otros subprocesos, pero una buena JVM aún no debería fallar.

Si puede encontrar un error en el código fuente de la máquina virtual y, por ejemplo, causa un error de segmentación en el uso de la memoria de la implementación de la máquina virtual, puede bloquearlo.


14

Si desea bloquear JVM, utilice lo siguiente en Sun JDK 1.6_23 o inferior:

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

Esto se debe a un error en Sun JDK, que también se encuentra en OpenJDK. Esto se corrige desde Oracle JDK 1.6_24 en adelante.


10

Depende de lo que entiendas por choque.

Puedes hacer una recursión infinita para que se quede sin espacio de pila, pero eso se bloqueará "con gracia". Obtendrá una excepción, pero la propia JVM se encargará de todo.

También puede usar JNI para llamar a código nativo. Si no lo hace bien, puede hacer que se bloquee duro. La depuración de esos bloqueos es "divertida" (confía en mí, tuve que escribir una gran DLL de C ++ que llamamos desde un applet de Java firmado). :)


6

El libro Java Virtual Machine de Jon Meyer tiene un ejemplo de una serie de instrucciones de código de bytes que causaron que la JVM volcara el núcleo. No puedo encontrar mi copia de este libro. Si alguien tiene uno, búsquelo y publique la respuesta.


5

en winxpsp2 con wmp10 jre6.0_7

Desktop.open (uriToAviOrMpgFile)

Esto hace que un hilo generado arroje un Throwable no capturado y se bloquea el punto de acceso

YMMV


5

El hardware roto puede bloquear cualquier programa. Una vez tuve un bloqueo de la aplicación reproducible en una máquina específica mientras funcionaba bien en otras máquinas con exactamente la misma configuración. Resulta que la máquina tenía RAM defectuosa.


5

forma más corta posible :)

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

No choca Da error de tiempo de compilación Exception in thread "main" java.lang.StackOverflowError at Test.main. Estoy usando jdk1.8.0_65
Quazi Irfan

5

No es un choque, pero está más cerca de un choque que la respuesta aceptada de usar System.exit

Puede detener la JVM llamando

Runtime.getRuntime().halt( status )

Según los documentos: -

"este método no hace que se inicien los ganchos de cierre y no ejecuta finalizadores no invocados si se ha habilitado la finalización al salir".



4

Si quieres fingir que te has quedado sin memoria puedes hacerlo

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

Conozco un par de maneras de hacer que la JVM voltee un archivo de error llamando a métodos nativos (los que están integrados), pero probablemente sea mejor que no sepa cómo hacerlo. ;)


4

Si define un bloqueo como un proceso de aborto debido a una situación no controlada (es decir, sin excepción o error de Java), esto no se puede hacer desde Java (a menos que tenga permiso para usar la clase sun.misc.Unsafe). Este es todo el punto del código administrado.

Los bloqueos típicos en el código nativo ocurren al desreferenciar los punteros a áreas de memoria incorrectas (dirección nula o desalineada). Otra fuente podría ser instrucciones de máquina ilegales (códigos de operación) o señales no manejadas de la biblioteca o llamadas del núcleo. Ambos pueden activarse si la JVM o las bibliotecas del sistema tienen errores.

Por ejemplo, el código JITed (generado), los métodos nativos o las llamadas al sistema (controlador de gráficos) pueden tener problemas que conducen a bloqueos reales (era bastante común que se bloqueara cuando usaba funciones ZIP y se quedaban sin memoria). En esos casos, el controlador de fallas de la JVM entra y deja el estado. También podría generar un archivo de núcleo del sistema operativo (Dr. Watson en Windows y volcado de núcleo en * nix).

En Linux / Unix, puede realizar fácilmente un bloqueo de JVM enviándole una señal al proceso en ejecución. Nota: no debe usar SIGSEGVpara esto, ya que Hotspot capta esta señal y la vuelve a lanzar como una NullPointerException en la mayoría de los lugares. Por lo tanto, es mejor enviar un SIGBUSpor ejemplo.


3

JNI es una gran fuente de accidentes. También puede bloquearse usando la interfaz JVMTI ya que eso también debe escribirse en C / C ++.


2

Si crea un proceso de subproceso que genera infinitamente más subprocesos (que generan más subprocesos, que ...) eventualmente causará un error de desbordamiento de pila en la propia JVM.

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();
    }
}

Esto me dio la salida (después de 5 minutos, mira tu ram)

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:
# 

1

Si por "bloqueo" se refiere a un aborto abrupto de la JVM, como si la JVM escribiera en su hs_err_pid% p.log , puede hacerlo de esta manera.

Establezca el argumento Xmx en un valor pequeño y dígale a la JVM que fuerce un bloqueo en memoria:

 -Xmx10m -XX:+CrashOnOutOfMemoryError

Para ser claros, sin el segundo argumento anterior, solo resultaría en la terminación de jvm con un OutOfMemoryError, pero no se "bloqueará" o anulará abruptamente la jvm.

Esta técnica demostró ser útil cuando intentaba probar el argumento JVM -XX: ErrorFile, que controla dónde se debe escribir un registro de hs_err_pid. Encontré esta publicación aquí, mientras trataba de encontrar formas de forzar tal caída. Cuando más tarde descubrí que lo anterior funcionaba como lo más fácil para mi necesidad, quería agregarlo a la lista aquí.

Finalmente, FWIW, si alguien puede probar esto cuando ya tiene un valor -Xms establecido en sus argumentos (a un valor mayor que el anterior), también querrá eliminarlo o cambiarlo, o no obtendrá un bloqueo, sino simplemente un error de jvm para iniciar, informando "Tamaño de almacenamiento dinámico inicial establecido en un valor mayor que el tamaño de almacenamiento dinámico máximo". (Eso no sería obvio si ejecutara la JVM como servicio, como con algunos servidores de aplicaciones. Nuevamente, me mordió, así que quería compartirlo).


0

Si cambia ese bucle for infinito a una llamada recursiva a la misma función, obtendrá una excepción de desbordamiento de pila:

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

public void causeStackOverflow() {
    causeStackOverflow();
}

0

Lo estoy haciendo ahora, pero no estoy completamente seguro de cómo ... :-) JVM (y mi aplicación) a veces simplemente desaparecen por completo. No se arrojan errores, no se registra nada. Pasa de trabajar a no ejecutarse instantáneamente sin previo aviso.


¡Comience a verificar su hardware, especialmente su memoria!
Thorbjørn Ravn Andersen

Desafortunadamente, está en múltiples máquinas que de lo contrario funcionan bien. Es solo esta aplicación en particular la que lo hace (y no requiere mucha memoria ni procesador).
Brian Knoblauch, el

0

Más corto? Use la clase Robot para activar CTRL + BREAK. Encontré esto cuando intentaba cerrar mi programa sin cerrar la consola (no tenía funcionalidad de 'salida').


Vieja pregunta: espero que alguien se beneficie de su respuesta en el futuro.
dbmitch

0

¿Esto cuenta?

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

Solo funciona para Linux y desde Java 9.

Por alguna razón no entiendo, ProcessHandle.current().destroyForcibly();no mata la JVM y tira java.lang.IllegalStateExceptioncon el mensaje destruir del proceso actual no permitido .


0

Se encuentra con este problema al intentar replicar el bloqueo de JVM.

Jni funciona, pero necesita ser ajustado para diferentes plataformas. Finalmente, uso esta combinación para hacer que JVM se bloquee

  1. Inicie la aplicación con estas opciones de JVM -XX:+CrashOnOutOfMemoryError
  2. Use a long[] l = new long[Integer.MAX_VALUE];para activar el OOM

Entonces JVM se bloqueará y generará el registro de bloqueo.


-2

Si un 'Crash' es algo que interrumpe el programa jvm / de la terminación normal, entonces una excepción no manejada podría hacer esto.

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
  }

Entonces, ¿depende de qué tipo de CRASH?


3
Lanzar una excepción no es un accidente.
Quazi Irfan

Sin embargo, las excepciones de tiempo de ejecución no controladas son bloqueos, que ArithmeticExceptionson
mediados del
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.