"Si un ouvrier veut bien faire son travail, il doit d'abord affûter ses outils." - Confucius, "Les Entretiens de Confucius. Lu Linggong"
Page de garde > La programmation > Comprendre les optimisations de verrouillage JVM

Comprendre les optimisations de verrouillage JVM

Publié le 2024-11-08
Parcourir:162

Understanding JVM Lock Optimizations

La concurrence est très essentielle au développement d'applications robustes et évolutives capables d'effectuer plusieurs opérations simultanées. Cependant, cela a un prix en termes de synchronisation. Cela entraîne des coûts de performances en raison des frais généraux liés à l’acquisition et à la libération des verrous. Pour atténuer ces coûts de performances, plusieurs optimisations ont été intégrées à la JVM, sous plusieurs formes, telles que le verrouillage biaisé, l'élimination des verrous, le grossissement des verrous et la notion de verrous légers et lourds.

Dans cet article, nous voyons ces optimisations plus en détail, en expliquant comment elles améliorent la synchronisation dans les applications Java multithread.

Bases du verrouillage Java

En Java, la synchronisation des blocs ou des méthodes garantit qu'un seul thread peut exécuter une section critique de code à la fois. Cela est particulièrement important lorsqu’on considère le partage de ressources au sein de l’environnement multithread. Java implémente cela en s'appuyant sur des verrous intrinsèques - ou parfois, ils sont appelés moniteurs associés à des objets ou des classes qui aident à gérer l'accès aux threads en utilisant les blocs synchronisés.

Bien que la synchronisation soit une nécessité pour la sécurité des threads, elle peut s'avérer assez coûteuse lorsque les conflits sont faibles ou totalement absents. C’est là que les optimisations JVM entrent en scène. Ainsi, cela réduit le coût du verrouillage et améliorera les performances globales.

1. Verrouillage biaisé

Qu'est-ce que le verrouillage biaisé ?

Le verrouillage biaisé est une optimisation visant à réduire la surcharge liée à l'acquisition du verrou. Il est optimisé pour réduire le coût d'acquisition des verrous, qui est dominé par un seul thread ou largement accessible par un seul thread. De tels programmes acquièrent et libèrent souvent des verrous par le même thread sans conflit avec d'autres threads. La JVM peut reconnaître ce modèle et polariser le verrou sur ce thread particulier ; l'acquisition du verrou suivant est presque gratuite.

Comment fonctionne le verrouillage biaisé ?

Si le verrouillage biaisé est activé, la première fois qu'un thread acquiert un verrou, ce verrou est biaisé vers ce thread. L'identité du thread est enregistrée dans l'en-tête de l'objet verrou, et les acquisitions de verrous ultérieures par ce thread n'impliquent aucune synchronisation : elles vérifient simplement si le verrou est orienté vers le thread actuel, ce qui est une opération très rapide et non bloquante. .

Si un autre thread tente d'acquérir le verrou, le biais est annulé et la JVM revient à un mécanisme de verrouillage impartial standard. A ce stade, il s'agit désormais d'un verrou standard, et le deuxième thread devra l'acquérir via un processus de verrouillage standard.
Avantages du verrouillage biaisé

Performance : L'acquisition du même thread sur un verrou biaisé est presque une acquisition de verrou gratuite.

Par conséquent, la gestion des conflits n'est pas nécessaire car les autres threads n'ont aucune chance d'être impliqués dans l'acquisition du verrou.

Surcharge inférieure : Il n'est pas nécessaire que l'état du verrou change ou que les métadonnées liées à la synchronisation soient modifiées, sauf en cas de conflit. 
 

Quand le verrouillage biaisé est-il utilisé ?

Le verrouillage biaisé est utile dans les applications où les verrous sont principalement accessibles par le même thread, comme les applications monothread ou une application qui a un faible conflit de verrous en multithread. Il est activé par défaut dans la plupart des JVM.

Comment désactiver le verrouillage biaisé

Le verrouillage biaisé est activé par défaut mais peut également être désactivé avec l'indicateur JVM comme ci-dessous :

-XX : -UseBiasedLocking

2. Élimination du verrouillage

Qu'est-ce que l'élimination du verrouillage ?

L'élimination des verrous est une optimisation très puissante dans laquelle la JVM élimine complètement certaines synchronisations (verrous) inutiles. Il inspectera le code pour détecter toute opportunité lors de sa compilation JIT et découvrira que la synchronisation n'est pas nécessaire. Cela se produit généralement lorsque le verrou a été accédé par un seul thread ou que l'objet que la JVM sera utilisée pour synchroniser ne partage pas le même objet dans différents threads. Une fois que la JVM estime que ce n'est plus nécessaire, elle élimine le verrou.

Comment fonctionne l'élimination du verrouillage ?

Dans la phase d'analyse d'échappement de la compilation JIT, JVM vérifie si l'objet est confiné à un seul thread ou s'il est utilisé uniquement dans un contexte local. Si la synchronisation sur cet objet peut être supprimée parce qu'un objet n'échappe pas à la portée du thread qui l'a créé, alors il en sera ainsi.

Par exemple, si un objet est créé et utilisé entièrement dans une méthode (et non partagé entre les threads), la JVM se rend compte qu'aucun autre thread ne peut accéder à l'objet et que donc toute synchronisation est redondante. Dans un tel cas, le compilateur JIT élimine simplement complètement le verrou.

Zéro surcharge de verrouillage : L'élimination des synchronisations inutiles empêchera également la JVM de payer le coût d'acquisition et de libération des verrous en premier lieu.

Débit plus élevé : La synchronisation morte peut parfois conduire à un débit plus élevé de l'application, surtout si le code contient de nombreux blocs synchronisés.

Jetez un œil à ce morceau de code :

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

Dans ce cas, la synchronisation sur sb n'est pas nécessaire puisque le StringBuilder est utilisé uniquement dans someMethod et n'est pas partagé entre d'autres threads. En regardant cela, la JVM peut effectuer une analyse d'échappement pour supprimer le verrou.

3. Verrouiller le grossissement

Qu'est-ce que le verrouillage grossissant ?

Le grossissement du verrouillage est une optimisation dans laquelle la JVM étend la portée d'un verrou pour couvrir davantage de morceaux de code au lieu d'acquérir et de libérer continuellement le verrou dans des boucles ou de petites sections de code.

Travail de grossissement des verrous

Si la JVM constate qu'une boucle étroite ou plusieurs blocs de code adjacents acquièrent et libèrent un verrou trop fréquemment, elle peut rendre le verrou plus grossier en le plaçant en dehors de la boucle ou sur plusieurs blocs de code. Cela rend l'acquisition et la libération répétées du sans verrou coûteuses et permet à un thread de maintenir le verrou pendant plus d'itérations.

Exemple de code : verrouillage grossissant

Considérez cet extrait de code :

for (int i = 0; i 



Le grossissement du verrou pousse l'acquisition du verrou en dehors de la boucle, de sorte que le thread n'acquiert le verrou qu'une seule fois :

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



La JVM peut améliorer considérablement les performances en évitant davantage d'acquisitions et de libérations du verrou.

Avantages du grossissement des verrous

Moins de liberté de surcharge de verrouillage : Le grossissement évite les acquisitions et les libérations de verrous, en particulier dans le code hotspot, comme les boucles qui ont été itérées des milliers de fois.

Performances améliorées :
Le verrouillage pendant une période plus longue améliore les performances par rapport au scénario dans lequel, sans verrouillage, un tel verrou serait acquis et libéré plusieurs fois.

4. Serrures légères et lourdes

La JVM utilise deux techniques de verrouillage différentes en fonction du degré de conflit entre les threads. Ces techniques incluent les serrures légères et les serrures lourdes.

Verrouillage léger

Le verrouillage léger a lieu en l'absence de verrou de contention, ce qui signifie qu'un seul thread tente d'acquérir ce verrou. Dans de tels scénarios, la JVM optimise l'acquisition à l'aide d'une opération CAS lors de la tentative d'acquisition du verrou, ce qui peut se produire sans synchronisation lourde.

Verrouillage lourd

Dans le cas où plusieurs threads souhaitent obtenir le même verrou ; c'est-à-dire qu'il y a conflit, la JVM élève cela au verrouillage lourd. Cela impliquerait de bloquer les threads au niveau du système d'exploitation et de les gérer à l'aide de primitives de synchronisation au niveau du système d'exploitation. Les verrous lourds sont plus lents car ils nécessitent en fait que le système d'exploitation effectue un changement de contexte, ainsi que pour gérer les threads.

Escalade de verrouillage

Si un conflit survient au niveau d'un verrou léger, la JVM peut le transformer en verrou lourd. L'escalade signifie ici passer du verrouillage rapide au niveau de l'utilisateur à un verrouillage plus coûteux au niveau du système d'exploitation qui inclut le blocage des threads.

Avantages des verrous légers

Acquisition rapide d'un verrou : Lorsqu'il n'y a pas de conflit, les verrous légers sont beaucoup plus rapides que les verrous lourds car ils évitent la synchronisation au niveau du système d'exploitation.

Blocage réduit : Sans conflits, les threads ne bloquent pas et augmentent linéairement avec une latence plus faible.

Inconvénients des verrous lourds

Surcharge de performances : les verrous lourds entraînent le coût du blocage des threads, du changement de contexte et de la réactivation des threads avec une dégradation des performances dans des régimes de contention très élevés.

Toutes ces optimisations aident la JVM à améliorer les performances dans les applications multithread, de sorte que les développeurs peuvent désormais écrire du code simultané et sécurisé sans trop sacrifier la surcharge de synchronisation. Comprendre ces optimisations peut aider les développeurs à concevoir des systèmes plus efficaces, en particulier dans les cas où le verrouillage entraîne une pénalité de haute performance.

Déclaration de sortie Cet article est reproduit sur : https://dev.to/arashariani/understanding-jvm-lock-optimizations-4l5i?1 En cas de violation, veuillez contacter [email protected] pour le supprimer.
Dernier tutoriel Plus>

Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.

Copyright© 2022 湘ICP备2022001581号-3