it-swarm-pt.tech

Como você trava uma JVM?

Eu estava lendo um livro sobre habilidades de programação em que o autor pergunta ao entrevistado: "Como você colide com uma JVM?" Eu pensei que você poderia fazer isso escrevendo um loop for infinito que acabaria gastando toda a memória.

Alguém tem alguma ideia?

141
Shivasubramanian A

A coisa mais próxima de uma única "resposta" é System.exit(), que finaliza a JVM imediatamente sem limpeza adequada. Mas, além disso, código nativo e exaustão de recursos são as respostas mais prováveis. Como alternativa, você pode procurar no bug tracker da Sun por erros na sua versão da JVM, alguns dos quais permitem cenários de travamento repetíveis. Costumávamos ter falhas semi-regulares quando chegávamos ao limite de 4 Gb de memória nas versões de 32 bits (geralmente usamos 64 bits agora).

6
Leigh Caldwell

Eu não chamaria de lançar um OutOfMemoryError ou StackOverflowError uma falha. Essas são apenas exceções normais. Para realmente travar um VM, existem três maneiras:

  1. Use JNI e falhe no código nativo.
  2. Se nenhum gerenciador de segurança estiver instalado, você poderá usar a reflexão para travar a VM. Isso é VM específico, mas normalmente um VM armazena vários ponteiros para recursos nativos em campos privados (por exemplo, um ponteiro para o objeto de thread nativo é armazenado em um campo longo em - Java.lang.Thread). Apenas altere-os por reflexão e o VM falhará mais cedo ou mais tarde.
  3. Todas as VMs têm bugs, então você só precisa acionar uma.

Para o último método, tenho um pequeno exemplo, que trava um hotspot da Sun VM silenciosamente:

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

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

Isso leva a um estouro de pilha no GC, para que você não obtenha StackOverflowError, mas uma falha real, incluindo um arquivo hs_err *.

172
ralfs

JNI . De fato, com a JNI, travar é o modo padrão de operação. Você tem que trabalhar muito para que não caia.

123
Dan Dyer

Usa isto:

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

Essa classe deve estar no caminho de classe de inicialização porque está usando código confiável, portanto, execute assim:

Java -Xbootclasspath/p :. Batida

56
Dave Griffiths

Eu vim aqui porque também me deparei com essa questão em The Passionate Programmer , de Chad Fowler. Para aqueles que não têm acesso a uma cópia, a pergunta é enquadrada como uma espécie de filtro/teste para candidatos que entrevistam para uma posição que exige "realmente bons Java programadores".

Especificamente, ele pergunta:

Como você escreveria um programa, em Java puro, que causaria uma falha na máquina virtual Java?

Programei em Java por mais de 15 anos e achei essa pergunta intrigante e injusta. Como outros já apontaram, o Java, como linguagem gerenciada, é projetado especificamente para não travar . Obviamente, sempre existem erros da JVM, mas:

  1. Após mais de 15 anos de JREs no nível de produção, é raro.
  2. É provável que algum desses erros seja corrigido na próxima versão, então qual é a probabilidade de você, como programador, encontrar e recuperar os detalhes do conjunto atual de interruptores de exibição do JRE?

Como outros já mencionaram, algum código nativo via JNI é uma maneira de travar um JRE. Mas o autor mencionou especificamente em Java puro , então está pronto.

Outra opção seria alimentar os códigos de bytes falsos do JRE; é fácil despejar alguns dados binários de lixo em um arquivo .class e pedir ao JRE para executá-lo:

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

Isso conta? Quero dizer, o próprio JRE não caiu; ele detectou corretamente o código falso, relatou e saiu.

Isso nos deixa com os tipos mais óbvios de soluções, como explodir a pilha por recursão, ficar sem memória heap por alocações de objeto ou simplesmente lançar RuntimeException. Mas isso apenas faz com que o JRE saia com uma exceção StackOverflowError ou similar, que novamente não é realmente uma falha .

Então o que resta? Eu adoraria ouvir o que o autor realmente tinha em mente como uma solução adequada.

Atualização : Chad Fowler respondeu aqui .

PS: é um livro ótimo. Eu peguei o suporte moral enquanto aprendia Ruby.

32
George Armhold

Esse código travará a JVM de maneiras desagradáveis

import Sun.dc.pr.PathDasher; 

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

A última vez que tentei isso faria:

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

Primeira parte do arquivo de log gerado:

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

Uma implementação perfeita da JVM nunca falha.

Para travar uma JVM, além da JNI, é necessário encontrar um bug no próprio VM. Um loop infinito apenas consome CPU. A alocação infinita de memória deve causar apenas OutOfMemoryError em uma JVM bem construída. Isso provavelmente causaria problemas para outros encadeamentos, mas uma boa JVM ainda não deve travar.

Se você encontrar um bug no código-fonte da VM e, por exemplo, causar uma falha de segmentação no uso de memória da implementação da VM, poderá travá-lo.

15
Dave L.

Se você deseja travar a JVM - use o seguinte no Sun JDK 1.6_23 ou abaixo:

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

Isso ocorre devido a um bug no Sun JDK - também encontrado no OpenJDK. Isso foi corrigido a partir do Oracle JDK 1.6_24 em diante.

14
Prabath Siriwardena

Depende do que você quer dizer com acidente.

Você pode fazer uma recursão infinita para que fique sem espaço na pilha, mas isso trava "normalmente". Você receberá uma exceção, mas a própria JVM estará lidando com tudo.

Você também pode usar o JNI para chamar código nativo. Se você não fizer isso da maneira certa, poderá travar com força. Depurar essas falhas é "divertido" (confie em mim, tive que escrever um grande C++ DLL que chamamos de um applet Java assinado). :)

10
Herms

O livro Java Virtual Machine de Jon Meyer tem um exemplo de uma série de instruções de bytecode que fizeram com que a JVM fizesse core dump. Não consigo encontrar minha cópia deste livro. Se alguém por aí tiver um, procure e publique a resposta.

6
Soulfly

O hardware quebrado pode travar qualquer programa. Certa vez, um aplicativo travou de forma reproduzível em uma máquina específica enquanto funcionava bem em outras máquinas com a mesma configuração exata. Acontece que a máquina tinha RAM com defeito.

5
Michael Borgwardt

Não é uma falha, mas está mais próxima de uma falha do que a resposta aceita do uso de _System.exit_

Você pode interromper a JVM chamando

Runtime.getRuntime().halt( status )

De acordo com os documentos: -

"este método não faz com que ganchos de desligamento sejam iniciados e não executa finalizadores não solicitados se a finalização na saída estiver ativada".

5
henry

maneira mais curta possível :)

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

no winxpsp2 com wmp10 jre6.0_7

Desktop.open (uriToAviOrMpgFile)

Isso faz com que um thread gerado jogue um Throwable não capturado e trava o ponto de acesso

YMMV

5
kitsuneymg

Se você definir uma falha como um processo interrompido devido a uma situação não tratada (ou seja, sem Java Exceção ou Erro), isso não poderá ser feito a partir de Java (a menos que você tenha permissão para use a classe Sun.misc.Unsafe). Esse é o ponto principal do código gerenciado.

Falhas típicas no código nativo acontecem desmarcando os ponteiros para áreas de memória incorretas (endereço nulo ou desalinhado). Outra fonte pode ser instruções ilegais da máquina (códigos de operação) ou sinais não manipulados de chamadas de biblioteca ou kernel. Ambos podem ser acionados se a JVM ou as bibliotecas do sistema tiverem erros.

Por exemplo, código JITed (gerado), métodos nativos ou chamadas do sistema (driver gráfico) podem ter problemas que levam a falhas reais (era bastante comum ocorrer uma falha quando você usava as funções Zip e elas ficavam sem memória). Nesses casos, o manipulador de falhas da JVM entra em ação e despeja o estado. Também poderia gerar um arquivo principal do sistema operacional (Dr. Watson no Windows e dump principal no * nix).

No Linux/Unix, você pode facilmente travar a JVM enviando um sinal para o processo em execução. Nota: você não deve usar SIGSEGV para isso, pois o Hotspot captura esse sinal e o lança novamente como um NullPointerException na maioria dos lugares. Portanto, é melhor enviar um SIGBUS por exemplo.

4
eckes

aqui está uma explicação detalhada sobre o que faz com que a JVM faça o dump principal (ou seja, falha): http://kb.Adobe.com/selfservice/viewContent.do?externalId=tn_17534

4
COTOHA

Se você quiser fingir que ficou sem memória, pode fazer

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

Conheço algumas maneiras de fazer com que a JVM despeje um arquivo de erro chamando métodos nativos (aqueles que são incorporados), mas provavelmente é melhor você não saber como fazer isso. ;)

3
Peter Lawrey

JNI é uma grande fonte de falhas. Você também pode travar usando a interface JVMTI, pois ela também precisa ser gravada em C/C++.

3
Jared

Se você criar um processo de encadeamento que gera infinitamente mais encadeamentos (que geram mais encadeamentos, que ...), você eventualmente causará um erro de estouro de pilha na própria 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();
    }
}

Isso me deu a saída (depois de 5 minutos, observe seu carneiro)

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

Mais curto? Use a classe Robot para acionar CTRL + BREAK. Vi isso quando estava tentando fechar meu programa sem fechar o console (ele não tinha a funcionalidade de 'saída').

1
user6022288

Estou fazendo isso agora, mas não sei ao certo como ... :-) Às vezes, a JVM (e meu aplicativo) desaparece completamente. Nenhum erro lançado, nada registrado. Passa do trabalho para não funcionar instantaneamente sem aviso.

0
Brian Knoblauch

Se por "travamento" você quer dizer um cancelamento abrupto da JVM, como faria com que a JVM escrevesse em seu hs_err_pid% p.log, você pode fazê-lo desta maneira.

Defina the-Xmx arg como um valor minúsculo e diga à JVM para forçar uma falha na memória excedente:

 -Xmx10m -XX:+CrashOnOutOfMemoryError

Para ficar claro, sem o segundo argumento acima, isso resultaria no jvm terminando com um OutOfMemoryError, mas não "travaria" ou abortaria abruptamente o jvm.

Essa técnica se mostrou útil quando eu estava tentando testar o argumento JVM -XX: ErrorFile, que controla onde esse log hs_err_pid deve ser gravado. Eu encontrei este post aqui, enquanto tentava encontrar maneiras de forçar esse acidente. Mais tarde, quando descobri que o acima funcionava como o mais fácil para minha necessidade, queria adicioná-lo à lista aqui.

Por fim, FWIW, se alguém puder testar isso quando já tiver um valor -Xms definido em seus argumentos (para um valor maior que o acima), convém remover ou alterar isso também, ou você não sofrerá uma falha, mas simplesmente uma falha na inicialização da jvm, relatando "Tamanho inicial do heap configurado para um valor maior que o tamanho máximo do heap". (Isso não seria óbvio se a JVM fosse executada como serviço, como em alguns servidores de aplicativos. Novamente, isso me mordeu, então eu queria compartilhá-lo.)

0
charlie arehart

Isso conta?

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

Funciona apenas para Linux e do Java 9.

Por alguma razão eu não entendo, ProcessHandle.current().destroyForcibly(); não mata a JVM e lança Java.lang.IllegalStateException com a mensagem não é permitido destruir o processo atual.

0
mszmurlo