大家好,我是小米,一个爱码代码、爱研究JVM底层的java工程师~今天我们来聊聊一个超级硬核但又超级有趣的 JVM 话题:对象的访问定位方式。
听起来是不是有点陌生?别急,咱们边喝奶茶边聊技术,小米保证你能轻轻松松地搞懂 JVM 是如何在内存里"找到"一个对象的。
那天我 Debug 到了内存深处...
上周,我在排查一个线上OOM问题。堆栈里明明显示有大量对象还在,但我用 jmap dump下来看,却找不到关键的引用链。
那一刻,我脑海里突然冒出一个问题:
"到底JVM是怎么定位一个对象的?对象在内存里是怎么被访问到的?"
说干就干,我打开了一整套 HotSpot 源码和虚拟机设计书,开始了我的"对象寻址"之旅!
对象访问定位是个啥?
我们在Java里写过无数次这种代码:
但你有没有想过,当你写下 obj.callMethod() 的时候,JVM 是怎么通过 obj 找到真正的那块对象内存的?
这背后,其实就是 JVM 的"访问定位方式"在起作用。
目前在 HotSpot 中,对象访问主要有两种方式:
- 句柄访问方式(Handle)
- 直接指针访问方式(Direct Pointer,又叫作对象指针)
方式一:句柄访问,类似中介人
我们先来看看"句柄"访问方式。
想象一下,你在租房,你不会直接去找房东,而是通过一个中介,这个中介手上有一份租房名单,名单上写着每套房子的真实地址。
在 JVM 里,这个"中介"就是句柄池。
JVM 会在内存中专门划一块区域,叫句柄池(Handle Pool),每个对象的引用会指向句柄池中的一个句柄对象,句柄再保存两个重要地址:对象数据地址、类型元数据地址。
简单示意图如下:
优点是什么?对象在移动的时候,引用地址不用变,只需更新句柄里的地址,简单方便。
但是,缺点也明显:每次访问对象都要多跳一次中介,性能略慢。
方式二:指针访问,直来直去
再来看第二种方式:指针访问(也叫直接访问) 。
这就像你有了房子的确切地址,直接导航过去,不用中介。
引用变量中直接存的是对象的地址(在 HotSpot 中,是对象头),对象头里保存了类型指针等信息。
示意图:
好处是:访问速度快,不用额外跳转。
但问题也来了:对象如果因为 GC 被移动了,引用变量也要跟着更新地址,这就对 GC 提出了更高的要求。
那 JVM 现在到底用哪种?
说了这么多,那问题来了:
"JVM 到底现在是用句柄,还是用指针呢?"
答案是------HotSpot 虚拟机默认采用的是指针访问方式!
也就是说,引用变量中保存的是对象的实际地址。
你也可以通过 VM 参数修改它,比如加上 -XX:+UseCompressedOops(开启压缩对象指针)来节省内存空间,尤其是在 64 位 JVM 下。
不过,句柄访问在一些其他的 JVM 实现(比如 Azul Vega)中还可能会使用。
为什么选择指针访问?
你可能还在好奇:
指针访问要 GC 时更新引用,不麻烦吗?为什么 HotSpot 还是选择它?
原因其实很简单:
现代的 HotSpot JVM 使用了分代 GC,大部分对象都是在年轻代分配并快速回收, 这些对象基本不会移动。即使对象被晋升到老年代,也是在 GC 阶段完成一次复制,所以引用更新的代价不算太大。
相比之下,节省那一次跳转,性能收益更大。
两种方式的对比总结
来来来,表格虽然不写,口头总结必须安排上!
小米的内存思考
我记得刚入行那年,我只知道"new 一个对象就是创建了一个对象",从没想过 JVM 怎么找到它、怎么管理它。
但随着项目变复杂、线上问题变多,我们不得不去理解那些"藏在冰山下的细节"。
比如你做逃逸分析、做JIT优化、做GC调优,其实都和对象的定位方式息息相关!
这就像修车一样,不光得会踩油门,更要懂发动机是怎么运转的。
延伸知识点:压缩对象指针(Compressed Oops)
这里再偷偷告诉你一个有意思的知识点:
在 64 位 JVM 中,对象地址是 64 位,但实际上很多时候只用到 32 位就够了。
为了节省空间,JVM 支持"压缩对象指针",也就是把地址压缩成 32 位的偏移量,这个就叫 Compressed Oops。
你可以通过 VM 参数 -XX:+UseCompressedOops 来查看是否开启,它对性能和内存占用都有不小影响。
END
对象访问方式,看似只是 JVM 内部的一个技术细节,但它却在系统性能、内存管理、甚至调试工具中都扮演着重要角色。
每一个引用背后,其实都是 JVM 精心安排的访问路径。你看到的是变量,JVM看到的,是地址、偏移、类型指针和优化策略的综合体。
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号"软件求生",获取更多技术干货!