"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Compreendendo as otimizações de bloqueio JVM

Compreendendo as otimizações de bloqueio JVM

Publicado em 2024-11-08
Navegar:571

Understanding JVM Lock Optimizations

A simultaneidade é muito crítica para o desenvolvimento de aplicativos robustos e escalonáveis ​​que podem executar várias operações simultâneas. No entanto, um preço precisa ser pago por isso em termos de sincronização. Ele incorre em custos de desempenho devido às despesas gerais de aquisição e liberação de bloqueios. Para aliviar esses custos de desempenho, diversas otimizações foram incorporadas à JVM, em diversos sabores, como bloqueio tendencioso, eliminação de bloqueio, aumento de bloqueio e a noção de bloqueios leves e pesados.

Neste artigo, vemos essas otimizações com mais detalhes, abordando como elas melhoram a sincronização em aplicativos Java multithread.

Noções básicas de bloqueio de Java

Em Java, a sincronização de blocos ou métodos garante que apenas um thread possa executar uma seção crítica de código por vez. Isto é importante em particular quando se considera o compartilhamento de recursos no ambiente multithread. Java implementa isso contando com bloqueios intrínsecos - ou às vezes, eles são chamados de monitores associados a objetos ou classes que ajudam a gerenciar o acesso aos threads usando os blocos sincronizados.

Embora a sincronização seja uma necessidade para a segurança do thread, ela pode ser bastante cara quando a contenção é baixa ou completamente ausente. É aí que as otimizações da JVM entram em cena. Assim, isso reduz o custo de bloqueio e melhora o desempenho geral.

1. Bloqueio tendencioso

O que é bloqueio tendencioso?

O bloqueio tendencioso é uma otimização que visa a redução da sobrecarga de aquisição de bloqueio. Ele é otimizado para reduzir o custo de aquisição de bloqueio, que é dominado por um único thread ou acessado em grande parte por um único thread. Esses programas geralmente adquirem e liberam bloqueios pelo mesmo thread, sem contenção de outros threads. A JVM pode reconhecer esse padrão e direcionar o bloqueio para esse encadeamento específico; a aquisição do bloqueio é quase gratuita.

Como funciona o bloqueio tendencioso?

Se o bloqueio tendencioso estiver ativado, na primeira vez que um thread adquirir um bloqueio, ele será direcionado para esse thread. A identidade do thread é registrada no cabeçalho do objeto de bloqueio, e as aquisições subsequentes de bloqueio por esse thread não envolvem nenhuma sincronização - elas apenas verificam se o bloqueio está direcionado para o thread atual, o que é uma operação muito rápida e sem bloqueio. .

Se outro thread tentar adquirir o bloqueio, a polarização será cancelada e a JVM retornará a um mecanismo de bloqueio imparcial padrão. Neste estágio, agora é um bloqueio padrão e o segundo thread terá que adquiri-lo por meio de um processo de bloqueio padrão.
Benefícios do bloqueio tendencioso

Desempenho: A aquisição do mesmo thread em um bloqueio tendencioso é quase uma aquisição de bloqueio gratuita.

Portanto, o tratamento de contenção não é necessário porque outros threads não têm chance de estar envolvidos na aquisição do bloqueio.

Lower Overhead: O estado do bloqueio não precisa ser alterado ou os metadados relacionados à sincronização serem modificados, exceto em caso de contenção. 
 

Quando o bloqueio tendencioso é usado?

O bloqueio tendencioso é útil em aplicativos onde os bloqueios são acessados ​​principalmente pelo mesmo thread, como aplicativos de thread único ou um aplicativo que tem baixa contenção de bloqueio em multithreading. Está habilitado por padrão na maioria das JVMs.

Como desativar o bloqueio tendencioso

O bloqueio tendencioso é habilitado por padrão, mas também pode ser desabilitado com o sinalizador JVM como abaixo:

-XX:-UseBiasedLocking

2. Eliminação de bloqueio

O que é eliminação de bloqueio?

A eliminação de bloqueio é uma otimização muito poderosa na qual a JVM elimina completamente algumas sincronizações desnecessárias (bloqueios). Ele inspecionará o código em busca de quaisquer oportunidades durante sua compilação JIT, onde descobrirá que a sincronização não é necessária. Isso geralmente ocorre quando o bloqueio foi acessado por apenas um thread ou o objeto que a JVM será usada para sincronizar não compartilha o mesmo objeto em threads diferentes. Assim que a JVM considerar que não é mais necessário, ela elimina o bloqueio.

Como funciona a eliminação de bloqueio?

Na fase de análise de escape da compilação JIT, a JVM verifica se o objeto está confinado a um único thread ou é usado apenas em um contexto local. Se a sincronização nesse objeto puder ser removida porque um objeto não escapa do escopo do thread que o criou, então será assim.

Por exemplo, se um objeto for criado e usado inteiramente dentro de um método (e não compartilhado entre threads), a JVM percebe que nenhum outro thread pode acessar o objeto e, portanto, toda a sincronização é redundante. Nesse caso, o compilador JIT simplesmente elimina completamente o bloqueio.

Zero Locking Overhead: Eliminar a sincronização desnecessária também impedirá que a JVM pague o custo de aquisição e liberação de bloqueios em primeiro lugar.

Maior rendimento: A sincronização morta às vezes pode levar a um rendimento mais alto do aplicativo, especialmente se o código contiver muitos blocos sincronizados.

Dê uma olhada neste trecho de código:

public void someMethod() {
    StringBuilder sb = new StringBuilder();
    synchronized (sb) {
        sb.append("Hello");
        sb.append("World");
    }
}

Nesse caso, a sincronização em sb não é necessária, pois o StringBuilder é usado apenas dentro do someMethod e não é compartilhado entre outros threads. Observando isso, a JVM pode realizar uma análise de escape para remover o bloqueio.

3. Bloqueio grosseiro

O que é bloqueio grosseiro?

O espessamento de bloqueio é uma otimização em que a JVM expande o escopo de um bloqueio para cobrir mais pedaços de código em vez de adquirir e liberar continuamente o bloqueio em loops ou pequenas seções de código.

Bloquear trabalho de desbaste

Se a JVM descobrir que um loop apertado ou vários blocos de código adjacentes adquirem e liberam um bloqueio com muita frequência, ela pode tornar o bloqueio mais grosseiro, levando-o para fora do loop ou através de vários blocos de código. Isso torna cara a aquisição e liberação repetida do lockless e permite que um thread mantenha o bloqueio para mais iterações.

Exemplo de código: bloqueio grosseiro

Considere este trecho de código:

for (int i = 0; i 



O bloqueio grosseiro empurra a aquisição do bloqueio para fora do loop, de modo que o thread adquire o bloqueio apenas uma vez:

synchronized (lock) {
  for (int i = 0; i 



A JVM pode melhorar drasticamente o desempenho, evitando mais aquisições e liberações do bloqueio.

Benefícios da redução de espessura

Menos liberdade de sobrecargas de bloqueio: O enfraquecimento evita aquisições e liberações de bloqueio, especialmente em código de ponto de acesso, como loops que foram iterados milhares de vezes.

Desempenho melhorado:
O bloqueio por um período mais longo melhora o desempenho quando comparado ao cenário em que, sem bloqueio, tal bloqueio seria adquirido e liberado várias vezes.

4. Fechaduras leves e pesadas

A JVM usa duas técnicas de bloqueio diferentes com base no grau de contenção entre os threads. Essas técnicas incluem fechaduras leves e fechaduras pesadas.

Bloqueio leve

O bloqueio leve ocorre na ausência de um bloqueio de contenção, o que significa que apenas um thread está tentando adquirir esse bloqueio. Nesses cenários, a JVM otimiza a aquisição usando uma operação CAS ao tentar adquirir o bloqueio, o que pode acontecer sem sincronização pesada.

Bloqueio de Peso Pesado

Caso vários threads desejem obter o mesmo bloqueio; isto é, há contenção, a JVM escala isso para bloqueio pesado. Isso envolveria o bloqueio de threads no nível do sistema operacional e o gerenciamento deles usando primitivas de sincronização no nível do sistema operacional. Bloqueios pesados ​​são mais lentos porque, na verdade, exigem que o sistema operacional execute a alternância de contexto, bem como gerencie threads.

Bloquear escalonamento

Se surgir contenção em um bloqueio leve, a JVM pode escalá-la para um bloqueio pesado. A escalada aqui significa mudar do bloqueio rápido no nível do usuário para um bloqueio no nível do sistema operacional mais caro, que inclui bloqueio de thread.

Benefícios das fechaduras leves

Aquisição rápida de um bloqueio: Quando não há contenção, os bloqueios leves são muito mais rápidos do que os bloqueios pesados ​​porque evitam a sincronização no nível do sistema operacional.

Bloqueio reduzido: Sem contenções, os threads não bloqueiam e aumentam linearmente com menor latência.

Desvantagens das fechaduras pesadas

Sobrecarga de desempenho: bloqueios pesados ​​incorrem no custo de bloqueio de threads, troca de contexto e ativação de threads com degradação de desempenho em regimes de contenção muito altos.

Todas essas otimizações ajudam a JVM a melhorar o desempenho em aplicativos multithread, para que os desenvolvedores agora possam escrever código simultâneo e seguro sem sacrificar muito em termos de sobrecarga de sincronização. Compreender essas otimizações pode ajudar os desenvolvedores a projetar sistemas mais eficientes, especialmente em casos que apresentam uma penalidade de alto desempenho por bloqueio.

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/arashariani/understanding-jvm-lock-optimizations-4l5i?1 Se houver alguma violação, entre em contato com [email protected] para excluí-la
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3