Exploração de objetos de máquinas virtuais HotSpot (6)

Criação de Objetos
  • Quando a máquina virtual encontra uma nova instrução, primeiro verifica se o parâmetro desta instrução pode localizar uma referência de símbolo de uma classe no pool constante e se a classe representada por essa referência de símbolo foi carregada, resolvida e inicializada. Caso contrário, primeiro execute o processo de carregamento de classe correspondente.

  • Após a verificação do carregamento da classe, a máquina virtual precisa alocar memória para os novos objetos. O tamanho da memória necessário para o objeto pode ser completamente determinado após a conclusão do carregamento da classe.A tarefa de alocar memória para o objeto é equivalente a dividir um determinado tamanho de memória da pilha java. Supondo que a memória heap Java seja completamente regular, toda a memória usada será colocada de um lado, a memória livre será colocada do outro lado e um ponteiro será colocado no meio como um indicador do ponto de demarcação. Esse método de alocação se torna " 指针碰撞". Se o espaço da pilha java não for regular, a memória usada e a memória livre estiverem interceptadas entre si, não há como simplesmente usar colisão de ponteiro, a máquina virtual deve manter uma lista para registrar qual memória está disponível ao alocar memória Localize na lista um espaço grande o suficiente para o objeto e atualize o registro na lista.Este método de alocação se torna " 空闲列表". A escolha do método de alocação é determinada se o heap Java é regular e se o heap Java é regular é determinado se o coletor de lixo usado possui uma função de compactação e classificação. Portanto, coletores com processos Compactos como Serial e ParNew usam colisão de ponteiro, enquanto coletores baseados no algoritmo Mark-Sweep, como CMS, geralmente usam listas gratuitas.

  • Além da divisão do espaço disponível, a criação de objetos na máquina virtual é um comportamento muito frequente. Mesmo que apenas o local de um ponteiro seja modificado, ele não é seguro para threads em cenários simultâneos. Pode estar alocando a memória exigida pelo objeto A. , O ponteiro não teve tempo para modificar o ponteiro, o objeto B também usa o ponteiro original para alocar memória. Existem duas soluções para resolver esse problema: uma é que o objeto aloca espaço de memória para o processamento da sincronização - o uso real do CAS com um mecanismo de repetição com falha para garantir a atomicidade da operação de atualização; a outra é atribuir a ação de alocação de memória de acordo com o encadeamento A divisão é realizada em espaços diferentes, ou seja, cada encadeamento aloca uma parte da memória no heap Java antecipadamente e se torna 本地线程分配缓冲(Thread Local Allocate Buffer,TLAB). Qual thread precisa alocar memória está alocado no TLAB de qual thread. Somente quando o TLAB é usado e redistribuído, é necessário determinar de forma síncrona se a máquina virtual usa ou não o TLAB, que pode ser -XX: +/- UseTLABdefinida pelos parâmetros dos parâmetros.

    TLAB: O
    TLAB evita conflitos de vários threads Ao alocar memória para um objeto, cada thread usa seu próprio TLAB, o que evita a sincronização de threads e melhora a eficiência da alocação de objetos. O próprio TLAB ocupa espaço na área eEden.Com o TLAB ativado, a máquina virtual aloca um espaço TLAB para cada encadeamento Java. O parâmetro -XX: + UseTLAB ativa o TLAB. A JVM é ativada por padrão. A memória do espaço TLAB é muito pequena. Por padrão, ocupa apenas 1% de todo o espaço Eden. É possível definir o espaço EDEN ocupado pelo espaço TLAB através da opção -XX: TLABWasteTargetPercent O tamanho da porcentagem. O espaço TLAB geralmente não é muito grande, portanto, objetos grandes não podem ser alocados no TLAB e sempre são alocados diretamente no heap. Como o espaço TLAB é relativamente pequeno, é fácil preenchê-lo. Por exemplo, um espaço de 100K já usou 80KB e, quando um objeto de 30KB precisar ser alocado, ele certamente ficará sem energia. No momento, a máquina virtual tem duas opções: primeiro, o TLAB atual é descartado, para desperdiçar 20 KB de espaço; segundo, o objeto de 30 KB é alocado diretamente no heap e o TLAB atual é retido, para que você possa esperar menos de 20 KB no futuro. Os pedidos de alocação de objetos podem usar esse espaço diretamente. De fato, um valor chamado refill_waste é mantido dentro da máquina virtual. Quando o objeto solicitado é maior que refill_waste, ele escolhe alocar na pilha. Se for menor que esse valor, o TLAB atual será descartado e um novo TLAB será criado para alocar o objeto. Esse limite pode ser ajustado usando TLABRefillWasteFraction, que representa a proporção de tais resíduos permitidos no TLAB. O valor padrão é 64, o que significa que cerca de 1/64 do espaço TLAB é usado como refill_waste. Por padrão, TLAB e refill_waste serão ajustados continuamente em tempo de execução para otimizar o estado operacional do sistema. Se você desejar desativar o ajuste automático do tamanho do TLAB, poderá usar -XX: -ResizeTLAB para desativar o ResizeTLAB e usar -XX: TLABSize para especificar manualmente o tamanho de um TLAB. -XX: + PrintTLAB pode rastrear o uso do TLAB. Geralmente, não é recomendável modificar manualmente os parâmetros relacionados ao TLAB; é recomendável usar o comportamento padrão da máquina virtual.

  • Em seguida, a máquina virtual precisa definir os parâmetros necessários do objeto, como qual instância de classe do objeto, como encontrar as meta informações da classe, o código de hash do objeto e a idade de geração do GC. Essas informações serão definidas na cabeça do objeto ( Cabeçalho do Objeto)

  • Após a conclusão do trabalho acima, um novo objeto é criado a partir da perspectiva da máquina virtual.A partir da perspectiva do java, a criação do objeto está apenas começando - o método <init> não foi executado e todos os campos ainda são 0, então execute new Depois que o comando é inicializado de acordo com os desejos do programador, um objeto verdadeiramente utilizável é calculado.

Fluxograma de alocação de memória do objeto
  • Processo de carregamento de classe e alocação de memória
    Processo de carregamento de classe e alocação de memória
  • Fluxograma de alocação de objetos na memória heap
    Fluxograma de alocação de objetos na memória heap
Layout da memória do objeto

Na máquina virtual HotSpot, objetos armazenados na memória para encontrar o layout pode ser dividido em três regiões: 对象头(Hesder), 实例数据(Instance)e 对其填充(Padding).

  • 对象头:As informações do cabeçalho incluem principalmente duas partes: a primeira parte são os dados de tempo de execução do usuário, como código de hash (HashCode), idade da geração do GC, sinalizador de status do bloqueio, bloqueio retido pelo encadeamento, ID do encadeamento, ID do encadeamento, carimbo de data e hora enviesado, etc. O comprimento de alguns dados é de 32 bits e 64 bits em máquinas virtuais de 32 e 64 bits (sem a compactação de ponteiro ativada), respectivamente. Oficialmente chamado "Mark word". Os objetos precisam armazenar muitos dados de tempo de execução; de fato, excedeu o limite de estruturas de Bitmap de 32 e 64 bits que podem ser gravadas, mas as informações do cabeçalho do objeto são um custo de armazenamento extra independente dos dados definidos pelo próprio objeto, considerando o espaço da máquina virtual Eficiência, o Mark Word foi projetado como uma estrutura de dados de comprimento não fixo, para armazenar o máximo de informações possível em um espaço muito pequeno, reutilizando seu espaço de armazenamento de acordo com o estado do objeto. A outra parte é o ponteiro de tipo, ou seja, o ponteiro do objeto para os metadados de sua classe. A máquina virtual usa esse ponteiro para determinar a instância da classe do objeto. Nem todas as implementações de máquinas virtuais devem manter o ponteiro de tipo nos dados do objeto. Em outras palavras, encontrar informações de metadados de um objeto não passa necessariamente pelo próprio objeto. Se o objeto for uma matriz, o cabeçalho do objeto também deverá ter um dado usado para registrar o comprimento da matriz.
    O Mark Word armazena conteúdo diferente em diferentes estados de bloqueio, para que seja armazenado na JVM de 32 bits:
    Informações do cabeçalho do objeto
  • 实例数据:São as informações efetivas que o objeto realmente armazena e também os vários tipos de conteúdo de campo definidos no código do programa. Seja ele herdado da classe pai ou definido pela subclasse, ele precisa ser registrado. A ordem de armazenamento desta parte será afetada pela ordem dos parâmetros da estratégia de alocação da máquina virtual (FiledsAllocationStype) e pelos campos definidos no código-fonte Java. A estratégia de alocação do HotSpot é longs / double, ints, shorts / char, bytes, booleanos, oops (ponteiros de objetos comuns) Como pode ser visto na estratégia de alocação, campos da mesma largura serão alocados juntos, sob a premissa de atender a essa condição , Variáveis ​​definidas na classe pai também aparecerão antes da classe filho. Se o valor do parâmetro CompactFields for verdadeiro (o padrão é verdadeiro), as variáveis ​​mais estreitas nas subclasses também poderão ser inseridas nas lacunas das variáveis ​​categóricas.
  • 对象填充:O preenchimento de objeto não existe necessariamente e não tem significado especial, é apenas um espaço reservado. Principalmente porque o sistema de gerenciamento de memória HotSpot JVM exige que o endereço inicial do objeto seja um múltiplo inteiro de 8, ou seja, o tamanho do objeto deve ser múltiplo inteiro de 8, e a parte do cabeçalho do objeto é exatamente um múltiplo inteiro de 8 (1 ou 2 vezes), portanto o objeto Se a parte dos dados da instância não estiver alinhada, ela deverá ser concluída preenchendo.
Posicionamento de acesso a objetos

O objeto é criado principalmente para usar o objeto. Nosso programa Java usa principalmente os dados de referência na pilha para operar o objeto específico no heap. O Reference especifica apenas uma referência ao objeto e não define como o objeto de referência está localizado. E o acesso ao local específico dos objetos no heap, portanto, o acesso a objetos depende da implementação da própria máquina virtual. Os principais métodos de corrente 句柄e 直接指针.

  • 句柄访问: Usando o acesso do identificador, o heap Java será dividido em uma memória como um conjunto de identificadores.A referência é armazenada no conjunto de identificadores do objeto e o conjunto de identificadores contém as informações de endereço específicas dos dados da instância do objeto e do tipo de dados, conforme mostrado na figura:
    Lidar com acesso
  • 直接指针:Ao usar o acesso direto ao ponteiro, o layout do objeto de heap Java deve considerar como colocar informações relevantes sobre os dados do tipo de acesso, e o endereço armazenado diretamente na Referência é o endereço do objeto, conforme mostrado na figura:
    Ponteiro direto

As vantagens e desvantagens do acesso ao identificador e acesso direto ao ponteiro: A maior vantagem do
uso 句柄é que a referência é armazenada no endereço implícito do identificador, quando o objeto é movido (mover objetos durante a coleta de lixo é um comportamento muito comum), apenas a instância no identificador será alterada O ponteiro de dados e a própria referência não precisam ser modificados. 直接指针A maior vantagem é a velocidade rápida, que economiza a sobrecarga do posicionamento do ponteiro.Como o acesso a objetos é muito frequente em Java, o acúmulo dessa sobrecarga também é um custo considerável de execução. A máquina virtual Sun HotSpot usa principalmente
ponteiros diretos para acessar objetos. Da perspectiva do escopo geral de desenvolvimento de software, também é muito comum que vários idiomas e estruturas usem o acesso de identificador.

Insira a descrição da imagem aqui

Publicado 41 artigos originais · Gostei 14 · Visitantes 10.000+

Acho que você gosta

Origin blog.csdn.net/Yunwei_Zheng/article/details/105171632
Recomendado
Clasificación