JVM 是否为每个对象创建互斥体以实现“同步”?关键词?如果没有,怎么办?
作为一个越来越熟悉 Java 的 C++ 程序员,看到语言级支持锁定任意对象而没有任何类型的声明表明该对象支持此类锁定,这对我来说有点奇怪。为每个对象创建互斥体似乎是自动选择的高昂成本。除了内存使用之外,互斥体在某些平台上也是操作系统有限的资源。如果互斥锁不可用,您可以使用自旋锁,但其性能特征明显不同,我预计这会损害可预测性。
JVM 是否在所有情况下都足够聪明,能够识别特定对象永远不会成为同步关键字的目标,从而避免创建互斥锁?互斥体可以懒惰地创建,但这会带来一个引导问题,它本身就需要互斥体,即使解决了这个问题,我认为仍然会产生一些开销来跟踪互斥体是否已经创建。所以我假设如果这样的优化是可能的,它必须在编译时或启动时完成。在 C++ 中,由于编译模型的原因,这样的优化是不可能的(你无法知道对象的锁是否会跨库边界使用),但我对 Java 的编译和链接了解不够。如果同样的限制适用。
As a C++ programmer becoming more familiar with Java, it's a little odd to me to see language level support for locking on arbitrary objects without any kind of declaration that the object supports such locking. Creating mutexes for every object seems like a heavy cost to be automatically opted into. Besides memory usage, mutexes are an OS limited resource on some platforms. You could spin lock if mutexes aren't available but the performance characteristics of that are significantly different, which I would expect to hurt predictability.
Is the JVM smart enough in all cases to recognize that a particular object will never be the target of the synchronized keyword and thus avoid creating the mutex? The mutexes could be created lazily, but that poses a bootstrapping problem that itself necessitates a mutex, and even if that were worked around I assume there's still going to be some overhead for tracking whether a mutex has already been created or not. So I assume if such an optimization is possible, it must be done at compile time or startup. In C++ such an optimization would not be possible due to the compilation model (you couldn't know if the lock for an object was going to be used across library boundaries), but I don't know enough about Java's compilation and linking to know if the same limitations apply.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
作为一个研究过某些 JVM 实现锁的方式的人来说……
正常的方法是从对象头字中的几个保留位开始。如果对象从未被锁定,或者已锁定但没有争用,则它将保持这种状态。如果锁定的对象发生争用,JVM 会将锁膨胀为成熟的互斥数据结构,并在对象的生命周期内保持这种状态。
编辑 - 我刚刚注意到OP正在谈论操作系统支持的互斥体。在我看过的示例中,未膨胀的互斥体是直接使用 CAS 指令等实现的,而不是使用 pthread 库函数等。
Speaking as someone who has looked at the way that some JVMs implement locks ...
The normal approach is to start out with a couple of reserved bits in the object's header word. If the object is never locked, or if it is locked but there is no contention it stays that way. If and when contention occurs on a locked object, the JVM inflates the lock into a full-blown mutex data structure, and it stays that way for the lifetime of the object.
EDIT - I just noticed that the OP was talking about OS-supported mutexes. In the examples that I've looked at, the uninflated mutexes were implemented directly using CAS instructions and the like, rather than using pthread library functions, etc.
这实际上是 JVM 的一个实现细节,不同的 JVM 可能会有不同的实现。然而,这绝对不是可以在编译时优化的东西,因为 Java 在运行时链接,并且以前未知的代码有可能获得在旧代码中创建的对象并启动对其进行同步。
请注意,在 Java 行话中,同步原语称为“监视器”而不是互斥体,并且它由特殊字节码操作支持。 这里有一个相当详细的解释。
This is really an implementation detail of the JVM, and different JVMs may implement it differently. However, it is definitely not something that can be optimized at compile time, since Java links at runtime, and this it is possible for previously unknown code to get a hold of an object created in older code and start synchronizing on it.
Note that in Java lingo, the synchronization primitive is called "monitor" rather than mutex, and it is supported by special bytecode operations. There's a rather detailed explanation here.
你永远无法确定一个对象永远不会被用作锁(考虑反射)。通常,每个对象都有一个标头,其中有一些专用于锁的位。可以实现它,以便仅根据需要添加标头,但这会有点复杂,而且您可能仍然需要一些标头(类(相当于 C++ 中的“vtbl”和分配大小)、哈希码和垃圾收集) 。
这里有一个关于 OpenJDK 中同步实现的 wiki 页面。
(在我的文章中)意见,为每个对象添加锁是一个错误。)
You can never be sure that an object will never be used as a lock (consider reflection). Typically every object has a header with some bits dedicated to the lock. It is possible to implement it such that the header is only added as needed, but that gets a bit complicated and you probably need some header anyway (class (equivalent of "vtbl" and allocation size in C++), hash code and garbage collection).
Here's a wiki page on the implementation of synchronisation in the OpenJDK.
(In my opinion, adding a lock to every object was a mistake.)
JVM不能直接使用比较和交换指令吗?假设每个对象都有一个字段
lockingThreadId
存储锁定它的线程的 id,这是一个玩具模型,但它看起来并不太昂贵,并且不依赖于操作系统。
can't JVM use compare-and-swap instruction directly? let's say each object has a field
lockingThreadId
storing the id of the thread that is locking it,this is a toy model, but it doesn't seem too expensive, and does no rely on OS.