estratégia de alocação de memória

Quando um objeto é criado, ele é alocado no heap ou na pilha? Isso tem a ver com duas coisas: o tipo do objeto e onde ele existe na classe Java.

Os objetos Java podem ser divididos em tipos de dados básicos e objetos comuns.

  • Para objetos comuns, a JVM primeiro criará o objeto no heap e depois usará sua referência em outro lugar. Por exemplo, salve esta referência na tabela de variáveis ​​locais da pilha da máquina virtual.
  • Para tipos de dados básicos (byte, short, int, long, float, double, char) existem dois casos. Quando você declara um objeto de tipo de dados básico no corpo do método, ele será alocado diretamente na pilha; em outros casos, será alocado no heap.

No entanto, com o desenvolvimento de compiladores JIT e a maturidade da tecnologia de análise de escape, a otimização 栈上分配e 标量替换a otimização também farão com que nossos objetos sejam provavelmente alocados na pilha.
insira a descrição da imagem aqui

1. Alocação na pilha

Então, sob quais circunstâncias nossos objetos serão alocados na pilha? Ou seja, nossa tecnologia de análise de escape descobre que esse objeto não escapa de um método, então esse objeto pode ser alocado na pilha, para que nosso objeto fique na pilha, e ele morra com o final do nosso método, de modo que não será. Se nosso GC precisar ser reciclado, ele se destruirá junto com o método, o que pode melhorar o desempenho.

O princípio da análise de escape: analisar o escopo dinâmico dos objetos.Quando um objeto é definido em um método, ele pode ser referenciado por um método externo.
Por exemplo: parâmetros de chamada passados ​​para outros métodos, isso é chamado de método escape. Pode até ser acessado por threads externos, como atribuir valores a variáveis ​​​​acessadas em outros threads, isso é chamado de escape de thread. De sem escape a escape de método e escape de thread, são chamados os diferentes graus de escape de objetos, de baixo a alto.

Fazer com que o objeto aloque memória na pilha pode melhorar a eficiência da JVM se houver certeza de que um objeto não escapará do encadeamento.

public class Client {
    
    

    public User newUser(){
    
    
        User user = new User();
        user.setName("Rocky");
        user.setAge(18);
        return user;
    }
    
    public static class User{
    
    
        private String name;
        private int age;

        //省略Getter、Setter方法
        ...
    }
}

Mesmo que o objeto user em nosso método newUser acima escape, ele não pode ser alocado na pilha, mas só pode ser alocado no heap, porque no método newUser o objeto user é finalmente retornado, o que significa que pode ser obtido por outros métodos O objeto de usuário.


A seguir, deixe-me dar uma olhada naqueles que não escapam, ou seja, os objetos podem ser alocados na pilha, da seguinte forma

public class Client {
    
    

    public void test(){
    
    
        User user = new User();
        user.setName("Rocky");
        user.setAge(18);
    }

    public static class User{
    
    
        private String name;
        private int age;

        //省略Getter、Setter方法
        ...
    }
}

Aqui testaremos a diferença de desempenho entre o uso e o não uso da tecnologia de análise de escape, então primeiro observe o código de teste a seguir

public class Client {
    
    

    public static class User {
    
    
        private String name;
        private int age;

        //省略Getter、Setter方法
        ...
    }

    public static void newUser() {
    
    
        User user = new User();
        user.setName("Rocky");
        user.setAge(18);
    }

    public static void main(String[] args) {
    
    
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
    
    
            newUser();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("耗时:" + (endTime - startTime) + "毫秒");
    }
}

Em seguida queremos executar nosso código de teste, aqui precisamos definir alguns parâmetros antes de executar
insira a descrição da imagem aqui
-servidorUm dos modos em que a JVM é executada, o modo servidor pode realizar análise de escape, e o modo em que a JVM é executada também inclui mix/client
-Xmx10mDefina o valor mínimo do heap para 10m
-Xms10mDefina o valor máximo do heap para 10m
-XX:+DoEscapeAnalysisHabilitar análise de escape (ativado por padrão)
-XX:+ImprimirGCimprimir registro do GC
-XX:+Eliminar alocaçõesSubstituição escalar (ativada por padrão)
-XX: -UseTLABFechar buffer de alocação de thread local
insira a descrição da imagem aqui

Usando a tecnologia de análise de escape, leva 3 milissegundos para alocar objetos na pilha, então vamos desligar a tecnologia de análise de escape e dar uma olhada: só precisamos desligar a análise de habilitação de escape em nossos parâmetros de configuração.
insira a descrição da imagem aqui
insira a descrição da imagem aqui
A alocação no heap aqui é que nosso GC realizou muita reciclagem, o que levou um total de 1.148 milissegundos.


Aqui adicionamos o que mencionamos na configuração acima 标量替换, ou seja, quando determinamos que o objeto no método não escapa e pode ser alocado no método, a JVM não criará o objeto, mas decomporá as variáveis ​​​​membro do objeto em vários são substituídos por variáveis ​​de membro usadas por este método, como segue:
insira a descrição da imagem aqui


2. Os objetos são alocados primeiro na área do Éden

Na maioria dos casos, os objetos são alocados na área do Éden da nova geração. Quando a área Eden não tiver espaço suficiente para alocação, a máquina virtual iniciará um Minor GC.


3. Objetos grandes entram diretamente na velhice

O chamado objeto grande refere-se a um objeto Java que requer uma grande quantidade de espaço de memória contínua. O objeto grande mais clássico é uma string muito longa ou um array com um grande número de elementos. A razão para evitar objetos grandes na máquina virtual Java é que existem as seguintes desvantagens ao alocar espaço:

  • É fácil acionar a coleta de lixo antecipadamente quando ainda há muito espaço na memória, de forma a obter espaço contínuo suficiente para colocá-los
  • Objetos grandes significam alta sobrecarga de cópia de memória ao copiar objetos

A máquina virtual também fornece -XX:PretenureSizeThresholdum parâmetro (válido apenas para coletores de lixo Serial e ParNew), especificando que objetos maiores que este valor de configuração são alocados diretamente na velhice, e o padrão é 0, o que significa que nunca será alocado diretamente em a velhice. Isso é feito para os seguintes propósitos:

  • Evite a coleta de lixo com antecedência, obviamente há espaço na memória para alocação
  • Evite copiar para frente e para trás entre a área Eden e as duas áreas Survivor, resultando em um grande número de operações de cópia de memória

4. Objetos sobreviventes de longo prazo entrarão na velhice

A maioria dos coletores na máquina virtual HotSpot usa coleção geracional para gerenciar a memória heap, portanto, quando a memória é reciclada, ela deve ser capaz de decidir quais objetos sobreviventes devem ser colocados na geração mais jovem e quais objetos sobreviventes devem ser colocados na geração antiga. Para fazer isso, a máquina virtual define um contador de idade do objeto (Age) para cada objeto, que é armazenado no cabeçalho do objeto.
insira a descrição da imagem aqui

Se o objeto nascer na área do Éden e ainda sobreviver após o primeiro GC Menor, e puder ser acomodado pelo Sobrevivente, ele será movido para o espaço do Sobrevivente, e a idade do objeto será definida como 1. Cada vez que o objeto passar através do Minnor GC no Survivor, a idade aumentará, aumentará em 1 ano, e quando sua idade aumentar até certo ponto (o padrão é 15 anos), será promovido para a geração antiga. O limite de idade para o objeto a ser promovido para a geração antiga pode ser -XX:MaxTenuringThresholddefinido (o máximo é 15 anos, pois 4 bits são usados ​​para armazenar a idade no cabeçalho do objeto Mark Word, e o número máximo que pode ser representado por 4 bits é 15).


5. Determinação da idade de objetos dinâmicos

Para melhor se adaptar ao status da memória dos diferentes programas, a máquina virtual nem sempre exige que a idade do objeto seja promovida para a MaxTenuringThresholdgeração antiga.Se a soma do tamanho de todos os objetos da mesma idade no espaço Survivor é maior que a metade do espaço do Sobrevivente, a idade é maior que Ou objetos iguais a esta idade podem entrar diretamente na velhice sem esperar pela MaxTenuringThresholdidade exigida.


6. Garantia de Alocação de Espaço

Há um grande número de objetos sobreviventes na nova geração, e o espaço Survivor não é suficiente.Quando um grande número de objetos ainda sobrevive após o MinorGC (o caso mais extremo é que todos os objetos da nova geração sobrevivem após a recuperação de memória), a velha geração precisa fornecer garantias de alocação.Colocar objetos que não podem ser acomodados no Survivor diretamente na velhice.


Antes de ocorrer o Minor GC, a máquina virtual primeiro verifica se o espaço contínuo máximo disponível na geração antiga é maior que o espaço total de todos os objetos na nova geração.Se esta condição for verdadeira, o Minor GC pode garantir que é seguro. Caso contrário, a máquina virtual verifica HandlePromotionFailurese o valor da configuração permite falha de garantia. Se for permitido, continuará verificando se o espaço contínuo máximo disponível na geração antiga é maior que o tamanho médio dos objetos promovidos para a geração antiga. Se for maior, tentará realizar um Minor GC, embora isso GC Menor é arriscado. Se a garantia falhar, um GC Completo será realizado; se for menor ou HandlePromotionFailurea configuração não permitir risco, então um GC Completo também deverá ser realizado neste momento.

Acho que você gosta

Origin blog.csdn.net/rockvine/article/details/124615040
Recomendado
Clasificación