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

了解 JVM 锁优化

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

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]删除
最新教程 更多>
  • 如何在Java字符串中有效替换多个子字符串?
    如何在Java字符串中有效替换多个子字符串?
    在java 中有效地替换多个substring,需要在需要替换一个字符串中的多个substring的情况下,很容易求助于重复应用字符串的刺激力量。 However, this can be inefficient for large strings or when working with nu...
    编程 发布于2025-04-17
  • 我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    将我的加密库从mcrypt升级到openssl 问题:是否可以将我的加密库从McRypt升级到OpenSSL?如果是这样,如何?答案:是的,可以将您的Encryption库从McRypt升级到OpenSSL。可以使用openssl。附加说明: [openssl_decrypt()函数要求iv参...
    编程 发布于2025-04-17
  • PHP与C++函数重载处理的区别
    PHP与C++函数重载处理的区别
    作为经验丰富的C开发人员脱离谜题,您可能会遇到功能超载的概念。这个概念虽然在C中普遍,但在PHP中构成了独特的挑战。让我们深入研究PHP功能过载的复杂性,并探索其提供的可能性。在PHP中理解php的方法在PHP中,函数超载的概念(如C等语言)不存在。函数签名仅由其名称定义,而与他们的参数列表无关。...
    编程 发布于2025-04-17
  • 如何处理PHP文件系统功能中的UTF-8文件名?
    如何处理PHP文件系统功能中的UTF-8文件名?
    在PHP的Filesystem functions中处理UTF-8 FileNames 在使用PHP的MKDIR函数中含有UTF-8字符的文件很多flusf-8字符时,您可能会在Windows Explorer中遇到comploreer grounder grounder grounder gro...
    编程 发布于2025-04-17
  • 在PHP中如何高效检测空数组?
    在PHP中如何高效检测空数组?
    在PHP 中检查一个空数组可以通过各种方法在PHP中确定一个空数组。如果需要验证任何数组元素的存在,则PHP的松散键入允许对数组本身进行直接评估:一种更严格的方法涉及使用count()函数: if(count(count($ playerList)=== 0){ //列表为空。 } 对...
    编程 发布于2025-04-17
  • 大批
    大批
    [2 数组是对象,因此它们在JS中也具有方法。 切片(开始):在新数组中提取部分数组,而无需突变原始数组。 令ARR = ['a','b','c','d','e']; // USECASE:提取直到索引作...
    编程 发布于2025-04-17
  • 如何使用不同数量列的联合数据库表?
    如何使用不同数量列的联合数据库表?
    合并列数不同的表 当尝试合并列数不同的数据库表时,可能会遇到挑战。一种直接的方法是在列数较少的表中,为缺失的列追加空值。 例如,考虑两个表,表 A 和表 B,其中表 A 的列数多于表 B。为了合并这些表,同时处理表 B 中缺失的列,请按照以下步骤操作: 确定表 B 中缺失的列,并将它们添加到表的末...
    编程 发布于2025-04-17
  • JavaScript计算两个日期之间天数的方法
    JavaScript计算两个日期之间天数的方法
    How to Calculate the Difference Between Dates in JavascriptAs you attempt to determine the difference between two dates in Javascript, consider this s...
    编程 发布于2025-04-17
  • 为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    查询模式实现缺失:解决“无法找到”错误在Silverlight应用程序中,尝试使用LINQ建立LINQ连接以错误而实现的数据库”,无法找到查询模式的实现。”当省略LINQ名称空间或查询类型缺少IEnumerable 实现时,通常会发生此错误。 解决问题来验证该类型的质量是至关重要的。在此特定实例中...
    编程 发布于2025-04-17
  • 在GO中构造SQL查询时,如何安全地加入文本和值?
    在GO中构造SQL查询时,如何安全地加入文本和值?
    在go中构造文本sql查询时,在go sql queries 中,在使用conting and contement和contement consem per时,尤其是在使用integer per当per当per时,per per per当per. [&​​&&&&&&&&&&&&&&&默元组方法在...
    编程 发布于2025-04-17
  • 如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
    如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
    Transform Pandas DataFrame Column to DateTime FormatScenario:Data within a Pandas DataFrame often exists in various formats, including strings.使用时间数据时...
    编程 发布于2025-04-17
  • 在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    For Each Loop vs. Iterator: Efficiency in Collection TraversalIntroductionWhen traversing a collection in Java, the choice arises between using a for-...
    编程 发布于2025-04-17
  • 如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    为有效的slug生成首先,该函数用指定的分隔符替换所有非字母或数字字符。此步骤可确保slug遵守URL惯例。随后,它采用ICONV函数将文本简化为us-ascii兼容格式,从而允许更广泛的字符集合兼容性。接下来,该函数使用正则表达式删除了不需要的字符,例如特殊字符和空格。此步骤可确保slug仅包含...
    编程 发布于2025-04-17
  • 如何使用Python有效地以相反顺序读取大型文件?
    如何使用Python有效地以相反顺序读取大型文件?
    在python 反向行读取器生成器 == ord('\ n'): 缓冲区=缓冲区[:-1] 剩余_size- = buf_size lines = buffer.split('\ n'....
    编程 发布于2025-04-17
  • 使用jQuery如何有效修改":after"伪元素的CSS属性?
    使用jQuery如何有效修改":after"伪元素的CSS属性?
    在jquery中了解伪元素的限制:访问“ selector 尝试修改“:”选择器的CSS属性时,您可能会遇到困难。 This is because pseudo-elements are not part of the DOM (Document Object Model) and are th...
    编程 发布于2025-04-17

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

Copyright© 2022 湘ICP备2022001581号-3