”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 了解 JVM 锁优化

了解 JVM 锁优化

发布于2024-11-08
浏览:688

Understanding JVM Lock Optimizations

并发对于开发可以执行多个并发操作的健壮、可扩展的应用程序非常关键。然而,为此需要付出同步方面的代价。由于获取和释放锁的随之而来的开销,它会产生性能成本。为了减轻这些性能成本,JVM 中融入了多种优化,例如偏向锁定、锁定消除、锁定粗化以及轻量级和重量级锁定的概念。

在本文中,我们将更详细地了解这些优化,并探讨它们如何改进多线程 Java 应用程序中的同步。

Java 锁定基础知识

在Java中,块或方法的同步确保一次只有一个线程可以执行代码的关键部分。当考虑多线程环境中的资源共享时,这一点尤其重要。 Java 通过依赖内在锁来实现这一点,或者有时,它们被称为与对象或类关联的监视器,通过使用同步块来帮助管理对线程的访问。

虽然同步是线程安全的必要条件,但当争用较低或完全不存在时,同步的成本可能会相当高。这就是 JVM 优化介入的地方。因此,这降低了锁定成本并提高整体性能。

1。偏向锁定

什么是偏向锁定?

偏向锁是一种旨在减少锁获取开销的优化。它进行了优化,以降低锁获取的成本,锁获取的成本由单个线程主导或大部分由单个线程访问。此类程序通常由同一线程获取和释放锁,而不会与其他线程发生争用。 JVM 可以识别这种模式并将锁偏向于该特定线程;接下来的锁获取几乎是免费的。

偏向锁定如何工作?

如果启用偏向锁定,那么当线程第一次获取锁时,它会使该锁偏向于该线程。线程的身份记录在锁对象的标头中,该线程后续的锁获取不涉及任何同步 - 它们只是检查锁是否偏向当前线程,这是一个非常快速的非阻塞操作.

如果另一个线程尝试获取锁,那么偏向就会被取消,JVM 会退回到标准的无偏锁机制。在此阶段,它现在是一个标准锁,第二个线程必须通过标准锁定过程来获取它。
偏向锁定的好处

性能:同一个线程在偏向锁上的获取几乎是免费锁的获取。

因此,不需要争用处理,因为其他线程没有机会参与获取锁。

较低的开销: 除非发生争用,否则不需要更改锁的状态或修改与同步相关的元数据。 
 

何时使用偏向锁定?

偏向锁在锁主要由同一线程访问的应用程序中很有用,例如单线程应用程序或多线程下锁争用较低的应用程序。它在大多数 JVM 中默认启用。

如何禁用偏向锁定

偏向锁定默认启用,但也可以使用 JVM 标志禁用,如下所示:

-XX:-UseBiasedLocking

2.锁定消除

什么是锁消除?

锁消除是一种非常强大的优化,JVM 完全消除了一些不必要的同步(锁)。它将在 JIT 编译期间检查代码是否有任何机会,其中发现同步是不必要的。当锁仅被一个线程访问时,或者 JVM 将用于同步的对象在不同线程中不共享同一对象时,通常会发生这种情况。一旦 JVM 认为不再需要它,它就会消除锁。

锁消除是如何工作的?

在JIT编译的逃逸分析阶段,JVM检查对象是否仅限于单个线程或仅在本地上下文中使用。如果因为对象没有逃逸创建它的线程的范围而可以删除该对象上的同步,那么情况就会如此。

例如,如果一个对象完全在一个方法中创建和使用(并且不在线程之间共享),那么 JVM 就会意识到没有其他线程可以访问该对象,因此所有同步都是多余的。在这种情况下,JIT 编译器只是完全消除锁。

零锁定开销:消除不必要的同步也将阻止 JVM 首先支付获取和释放锁的成本。

更高的吞吐量:死同步有时会导致应用程序更高的吞吐量,特别是当代码包含许多同步块时。

看一下这段代码:

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

在这种情况下,sb 上的同步是不必要的,因为 StringBuilder 仅在 someMethod 中使用,并且不在其他线程之间共享。通过查看这个,JVM可以执行逃逸分析来移除锁。

3.锁定粗化

什么是锁粗化?

锁粗化是一种优化,其中 JVM 扩展锁的范围以覆盖更多代码块,而不是在循环或小部分代码中连续获取和释放锁。

锁定粗化工作

如果 JVM 发现紧密循环或多个相邻代码块过于频繁地获取和释放锁,它可以通过在循环外或跨多个代码块获取锁来粗化锁。这使得重复获取和释放无锁变得昂贵,并使线程能够持有锁以进行更多迭代。

代码示例:锁定粗化

考虑这个代码片段:

for (int i = 0; i 



锁粗化将锁获取推到循环之外,因此线程仅获取锁一次:

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



JVM 可以通过避免更多的锁获取和释放来显着提高性能。

锁定粗化的好处

更少的锁定自由度开销:粗化可以避免锁的获取和释放,特别是在热点代码中,例如已经迭代了数千次的循环。

性能改进:
与不加锁、多次获取和释放此类锁的情况相比,较长时间的锁定可以提高性能。

4。轻型和重型锁

JVM 根据线程之间的争用程度使用两种不同的锁定技术。此类技术包括轻量级锁和重量级锁。

轻量级锁定

轻量级锁定发生在没有争用锁的情况下,这意味着只有一个线程试图获取该锁。在这种情况下,JVM 在尝试获取锁时会使用 CAS 操作来优化获取,这可能在没有重量级同步的情况下发生。

重量级锁定

如果多个线程想要获得同一个锁;也就是说,存在争用,JVM 将其升级为重量级锁定。这将涉及在操作系统级别阻塞线程并使用操作系统级别同步原语来管理它们。重量级锁速度较慢,因为它们实际上需要操作系统执行上下文切换以及管理线程。

锁定升级

如果轻量级锁出现争用,JVM 可能会将其升级为重量级锁。这里的升级意味着从快速的用户级锁切换到更昂贵的操作系统级锁,其中包括线程阻塞。

轻量级锁的好处

快速获取锁:在没有争用的情况下,轻量级锁比重量级锁快得多,因为它们避免了操作系统级同步。

减少阻塞:在没有争用的情况下,线程不会阻塞并以较低的延迟线性增加。

重量级锁的缺点

性能开销:重量级锁会产生线程阻塞、上下文切换和唤醒线程的成本,在非常高的争用情况下性能会下降。

所有这些优化都有助于 JVM 提高多线程应用程序的性能,因此开发人员现在可以编写安全的并发代码,而无需牺牲太多同步开销。了解这些优化可以帮助开发人员设计更高效的系统,特别是在因锁定而导致高性能损失的情况下。

版本声明 本文转载于:https://dev.to/arashariani/understanding-jvm-lock-optimizations-4l5i?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 除了“if”语句之外:还有什么地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    除了“if”语句之外:还有什么地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    无需强制转换即可上下文转换为 bool您的类定义了对 bool 的显式转换,使您能够在条件语句中直接使用其实例“t”。然而,这种显式转换提出了一个问题:“t”在哪里可以在不进行强制转换的情况下用作 bool?上下文转换场景C 标准指定了四种值可以根据上下文转换为的主要场景bool:语句:if、whi...
    编程 发布于2024-12-25
  • 在 Go 中使用 WebSocket 进行实时通信
    在 Go 中使用 WebSocket 进行实时通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSocke...
    编程 发布于2024-12-25
  • 如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 识别今天生日的用户使用 MySQL 确定今天是否是用户的生日涉及查找生日匹配的所有行今天的日期。这可以通过一个简单的 MySQL 查询来实现,该查询将存储为 UNIX 时间戳的生日与今天的日期进行比较。以下 SQL 查询将获取今天有生日的所有用户: FROM USERS ...
    编程 发布于2024-12-25
  • HTML 格式标签
    HTML 格式标签
    HTML 格式化元素 **HTML Formatting is a process of formatting text for better look and feel. HTML provides us ability to format text without us...
    编程 发布于2024-12-25
  • 尽管代码有效,为什么 POST 请求无法捕获 PHP 中的输入?
    尽管代码有效,为什么 POST 请求无法捕获 PHP 中的输入?
    解决 PHP 中的 POST 请求故障在提供的代码片段中:action=''而不是:action="<?php echo $_SERVER['PHP_SELF'];?>";?>"检查 $_POST数组:表单提交后使用 var_dump 检查 $_POST 数...
    编程 发布于2024-12-25
  • 如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    MySQL配置不正确:相对路径的问题在Django中运行python manage.py runserver时,可能会遇到以下错误:ImproperlyConfigured: Error loading MySQLdb module: dlopen(/Library/Python/2.7/site-...
    编程 发布于2024-12-25
  • Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta:列偏移的删除和恢复Bootstrap 4 在其 Beta 1 版本中引入了重大更改柱子偏移了。然而,随着 Beta 2 的后续发布,这些变化已经逆转。从 offset-md-* 到 ml-auto在 Bootstrap 4 Beta 1 中, offset-md-*...
    编程 发布于2024-12-25
  • 大批
    大批
    方法是可以在对象上调用的 fns 数组是对象,因此它们在 JS 中也有方法。 slice(begin):将数组的一部分提取到新数组中,而不改变原始数组。 let arr = ['a','b','c','d','e']; // Usecase: Extract till index p...
    编程 发布于2024-12-25
  • 插入数据时如何修复“常规错误:2006 MySQL 服务器已消失”?
    插入数据时如何修复“常规错误:2006 MySQL 服务器已消失”?
    插入记录时如何解决“一般错误:2006 MySQL 服务器已消失”介绍:将数据插入 MySQL 数据库有时会导致错误“一般错误:2006 MySQL 服务器已消失”。当与服务器的连接丢失时会出现此错误,通常是由于 MySQL 配置中的两个变量之一所致。解决方案:解决此错误的关键是调整wait_tim...
    编程 发布于2024-12-25
  • 如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    在 PHP 中组合关联数组在 PHP 中,将两个关联数组组合成一个数组是一项常见任务。考虑以下请求:问题描述:提供的代码定义了两个关联数组,$array1 和 $array2。目标是创建一个新数组 $array3,它合并两个数组中的所有键值对。 此外,提供的数组具有唯一的 ID,而名称可能重合。要求...
    编程 发布于2024-12-25
  • 如何将 Pandas DataFrame 字符串条目分解(拆分)为单独的行?
    如何将 Pandas DataFrame 字符串条目分解(拆分)为单独的行?
    将 Pandas DataFrame 字符串条目分解(拆分)为单独的行在 Pandas 中,一个常见的要求是将逗号分隔的值拆分为文本字符串列并为每个条目创建一个新行。这可以通过各种方法来实现。使用Series.explode()或DataFrame.explode()对于Pandas版本0.25.0...
    编程 发布于2024-12-25
  • Java中如何使用Selenium WebDriver高效上传文件?
    Java中如何使用Selenium WebDriver高效上传文件?
    在 Java 中使用 Selenium WebDriver 上传文件:详细指南将文件上传到 Web 应用程序是软件测试期间的一项常见任务。 Selenium WebDriver 是一种流行的自动化框架,它提供了一种使用 Java 代码上传文件的简单方法。然而,重要的是要明白,在 Selenium 中...
    编程 发布于2024-12-24
  • 使用 GNU Emacs 进行 C 语言开发
    使用 GNU Emacs 进行 C 语言开发
    Emacs is designed with programming in mind, it supports languages like C, Python, and Lisp natively, offering advanced features such as syntax highli...
    编程 发布于2024-12-24
  • 如何在 PHP 中打印单引号内的变量?
    如何在 PHP 中打印单引号内的变量?
    无法直接回显带有单引号的变量需要在单引号字符串中打印变量?直接这样做是不可能的。如何在单引号内打印变量:方法 1:使用串联追加 为此,请使用点运算符将变量连接到字符串上:echo 'I love my ' . $variable . '.';此方法将变量追加到字符串中。方法 2:使用双引号或者,在字...
    编程 发布于2024-12-24
  • std::vector 与普通数组:性能何时真正重要?
    std::vector 与普通数组:性能何时真正重要?
    std::vector 与普通数组:性能评估虽然人们普遍认为 std::vector 的操作与数组类似,但最近的测试对这一概念提出了挑战。在本文中,我们将研究 std::vector 和普通数组之间的性能差异,并阐明根本原因。为了进行测试,实施了一个基准测试,其中涉及重复创建和修改大型数组像素对象。...
    编程 发布于2024-12-24

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3