JVM执行一个名为 lock elision 的巧妙技巧,以避免锁定仅对一个线程可见的对象的成本。
这里有一个很好的描述:
http://www.ibm.com/developerworks/java/library/j-jtp10185/
.Net CLR是否做了类似的事情?如果不是那么为什么不呢?
答案 0 :(得分:8)
它很整洁,但有用吗?我很难想出一个例子,编译器可以证明锁是本地线程的。默认情况下,几乎所有类都不使用锁定,当您选择锁定时,在大多数情况下,它会从某种静态变量引用,无论如何都会阻碍编译器优化。
另一件事是java vm在其证明中使用了逃逸分析。而AFAIK .net尚未实施逃逸分析。转义分析的其他用途,例如用堆栈分配替换堆分配听起来更有用,应首先实现。
IMO目前不值得编码。 .net VM中有许多区域没有很好地优化并且影响更大。
SSE向量指令和委托内联是两个例子,我的代码从这个例子中获益远远超过这个优化。
答案 1 :(得分:4)
编辑:好的,以下解释过于简化,但实际上至少有一些基础:)请参阅Joe Duffy's blog post以获取更详细的信息。
我不记得我在哪里阅读 - 可能是“CLR通过C#”或“Windows上的并发编程” - 但我相信 CLR只是在需要时懒惰地将对象块分配给对象。当一个对象的监视器从未被争议时被锁定,对象标题将通过比较交换操作进行原子更新,以表示“我已被锁定”。如果另一个线程尝试获取锁定,CLR将能够确定它已经被锁定,并且基本上将该锁定升级为“完全”锁定,并将其分配为同步块。
当对象具有“完全”锁定时,锁定操作比锁定和解锁其他无争议对象更昂贵。
如果我对此是正确的(并且这是一个非常朦胧的记忆),只要锁从不重叠(即,那就是锁定和解锁不同线程上的监视器 应该是可行的)没有争论)。
我会看看我是否可以为此找到一些证据......
答案 2 :(得分:3)
回答你的问题:不,CLR \ JIT不执行“锁定省略”优化,即CLR \ JIT不会从只对单个线程可见的代码中删除锁。这可以通过代码上的简单单线程基准来轻松确认,其中锁定省略应该像您在Java中所期望的那样应用。
为什么它不这样做可能有很多原因,但主要是因为在.Net框架中这可能是一种不常见的应用优化,所以不值得实施。< / p>
同样在.Net无竞争锁定非常快,因为它们是非阻塞的并且在用户空间中执行(JVM似乎对非竞争锁具有类似的优化,例如IBM)。引用C# 3.0 In A Nutshell's线程章节
锁定速度很快:您可以期望获得并释放锁定 如果锁是无竞争的,则在3 GHz计算机上超过100纳秒“
可以应用锁省略的几个示例场景,以及为什么它不值得:
在您自己的代码中的方法中使用锁,这些方法完全依赖于本地人
首先在这种情况下使用锁定并不是一个很好的理由,因此与提升循环不变量或方法inling等优化不同,这是一种非常罕见的情况,也是不必要使用锁的结果。运行时不应该关注优化不常见和极端恶劣的用法。
使用其他人声明为内部使用锁定的本地类型
虽然这听起来更有用,但.Net框架的一般设计理念是将锁定责任留给客户端,因此很少有类型具有任何内部锁使用。实际上,当涉及到没有专门设计和广告为并发的类型的实例方法时,.Net框架在病理上是不同步的。另一方面,Java具有包括同步的常见类型,例如StringBuffer和Vector。由于.Net BCL在很大程度上是不同步的,因此锁定省略可能没什么影响。
<强>摘要强>
我认为总的来说,.Net中锁定省略会引起攻击的案例较少,因为根本没有那么多地方存在线程本地锁。锁更可能用于多线程可见的位置,因此不应被省略。此外,无竞争锁定非常快。
我很难找到任何真实的证据证明锁定elision实际上在Java中提供了很多性能优势(for example...),并且至少Oracle JVM状态的最新文档表明elision是并不总是应用于线程本地锁,暗示它不是总是给定的优化。
我怀疑锁定省略是通过在JVM中引入转义分析而实现的,但对于性能并不像EA分析是否可以在堆栈上分配引用类型那样重要。