A saída -1 torna-se um corte no circuito

okali:

Surpreendentemente, as seguintes saídas de código:

/
-1

O código:

public class LoopOutPut {

    public static void main(String[] args) {
        LoopOutPut loopOutPut = new LoopOutPut();
        for (int i = 0; i < 30000; i++) {
            loopOutPut.test();
        }

    }

    public void test() {
        int i = 8;
        while ((i -= 3) > 0) ;
        String value = i + "";
        if (!value.equals("-1")) {
            System.out.println(value);
            System.out.println(i);
        }
    }

}

Eu tentei muitas vezes para determinar quantas vezes isso iria ocorrer, mas, infelizmente, acabou por ser incerto, e eu achei que a saída de -2 por vezes transformado em um período. Além disso, também tentou remover o while loop e de saída -1, sem quaisquer problemas. Quem pode me dizer por quê?


JDK informações sobre a versão:

HopSpot 64-Bit 1.8.0.171
IDEA 2019.1.1
riqueza:

Isso pode ser reproduzida de forma confiável (ou não reproduzidos, dependendo do que você quiser) com openjdk version "1.8.0_222"(usado na minha análise), OpenJDK 12.0.1(de acordo com Oleksandr Pyrohov) e OpenJDK 13 (de acordo com Carlos Heuberger).

Eu corri o código com -XX:+PrintCompilationtempos suficientes para obter ambos os comportamentos e aqui estão as diferenças.

implementação Buggy (exibe saída):

 --- Previous lines are identical in both
 54   17       3       java.lang.AbstractStringBuilder::<init> (12 bytes)
 54   23       3       LoopOutPut::test (57 bytes)
 54   18       3       java.lang.String::<init> (82 bytes)
 55   21       3       java.lang.AbstractStringBuilder::append (62 bytes)
 55   26       4       java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
 55   20       3       java.lang.StringBuilder::<init> (7 bytes)
 56   19       3       java.lang.StringBuilder::toString (17 bytes)
 56   25       3       java.lang.Integer::getChars (131 bytes)
 56   22       3       java.lang.StringBuilder::append (8 bytes)
 56   27       4       java.lang.String::equals (81 bytes)
 56   10       3       java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)   made not entrant
 56   28       4       java.lang.AbstractStringBuilder::append (50 bytes)
 56   29       4       java.lang.String::getChars (62 bytes)
 56   24       3       java.lang.Integer::stringSize (21 bytes)
 58   14       3       java.lang.String::getChars (62 bytes)   made not entrant
 58   33       4       LoopOutPut::test (57 bytes)
 59   13       3       java.lang.AbstractStringBuilder::append (50 bytes)   made not entrant
 59   34       4       java.lang.Integer::getChars (131 bytes)
 60    3       3       java.lang.String::equals (81 bytes)   made not entrant
 60   30       4       java.util.Arrays::copyOfRange (63 bytes)
 61   25       3       java.lang.Integer::getChars (131 bytes)   made not entrant
 61   32       4       java.lang.String::<init> (82 bytes)
 61   16       3       java.util.Arrays::copyOfRange (63 bytes)   made not entrant
 61   31       4       java.lang.AbstractStringBuilder::append (62 bytes)
 61   23       3       LoopOutPut::test (57 bytes)   made not entrant
 61   33       4       LoopOutPut::test (57 bytes)   made not entrant
 62   35       3       LoopOutPut::test (57 bytes)
 63   36       4       java.lang.StringBuilder::append (8 bytes)
 63   18       3       java.lang.String::<init> (82 bytes)   made not entrant
 63   38       4       java.lang.StringBuilder::append (8 bytes)
 64   21       3       java.lang.AbstractStringBuilder::append (62 bytes)   made not entrant

prazo correto (sem exibição):

 --- Previous lines identical in both
 55   23       3       LoopOutPut::test (57 bytes)
 55   17       3       java.lang.AbstractStringBuilder::<init> (12 bytes)
 56   18       3       java.lang.String::<init> (82 bytes)
 56   20       3       java.lang.StringBuilder::<init> (7 bytes)
 56   21       3       java.lang.AbstractStringBuilder::append (62 bytes)
 56   26       4       java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
 56   19       3       java.lang.StringBuilder::toString (17 bytes)
 57   22       3       java.lang.StringBuilder::append (8 bytes)
 57   24       3       java.lang.Integer::stringSize (21 bytes)
 57   25       3       java.lang.Integer::getChars (131 bytes)
 57   27       4       java.lang.String::equals (81 bytes)
 57   28       4       java.lang.AbstractStringBuilder::append (50 bytes)
 57   10       3       java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)   made not entrant
 57   29       4       java.util.Arrays::copyOfRange (63 bytes)
 60   16       3       java.util.Arrays::copyOfRange (63 bytes)   made not entrant
 60   13       3       java.lang.AbstractStringBuilder::append (50 bytes)   made not entrant
 60   33       4       LoopOutPut::test (57 bytes)
 60   34       4       java.lang.Integer::getChars (131 bytes)
 61    3       3       java.lang.String::equals (81 bytes)   made not entrant
 61   32       4       java.lang.String::<init> (82 bytes)
 62   25       3       java.lang.Integer::getChars (131 bytes)   made not entrant
 62   30       4       java.lang.AbstractStringBuilder::append (62 bytes)
 63   18       3       java.lang.String::<init> (82 bytes)   made not entrant
 63   31       4       java.lang.String::getChars (62 bytes)

Podemos notar uma diferença significativa. Com a execução correta que compilar test()duas vezes. Uma vez no início, e mais uma vez depois (presumivelmente porque os avisos JIT quão quente o método é). Na execução de buggy test()é compilado (ou compilado) 5 vezes.

Além disso, correndo com -XX:-TieredCompilation(que tanto interpreta, ou usos C2) ou com -Xbatch(o que obriga a compilação para executar no thread principal, em vez de em paralelo), a saída é garantida e com 30000 iterações imprime um monte de coisas, de modo que o C2compilador parece para ser o culpado. Isto é confirmado por correr com -XX:TieredStopAtLevel=1, que desativa C2e não produz saída (parando no nível 4 mostra o bug novamente).

Na execução correta, o método é primeiro compilado com Nível 3 compilação, em seguida, depois com Nível 4.

Na execução de buggy, as compilações anteriores são discared ( made non entrant) e é novamente compilado no Nível 3 (que é C1, veja o link anterior).

Por isso definitivamente é um bug no C2, embora eu não estou absolutamente certo de que o fato de que ele vai voltar para o Nível 3 compilação afeta (e por que é que vai voltar para o nível 3, tantas incertezas ainda).

Você pode gerar o código de montagem com a seguinte linha para ir ainda mais fundo no buraco do coelho (ver também este para ativar a impressão de montagem).

java -XX:+PrintCompilation -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly LoopOutPut > broken.asm

Neste momento eu estou começando a ficar sem habilidades, o comportamento de buggy começa a apresentar quando as versões anteriores compilados são descartados, mas o pouco montagem habilidades que eu tenho são da década de 90, então eu vou deixar alguém mais esperto que eu levá-la daqui.

É provável que já existe um relatório de bug sobre isso, já que o código foi apresentado ao OP por outra pessoa, e como todo o código C2 não é sem erros . Espero que esta análise tem sido o mais informativo para os outros como tem sido para mim.

Como o apangin venerável apontou nos comentários, este é um bug recente . Muito obrigado a todas as pessoas interessadas e votos :)

Acho que você gosta

Origin http://43.154.161.224:23101/article/api/json?id=321775&siteId=1
Recomendado
Clasificación