对象访问两种方式PK:句柄 VS 指针,谁才是JVM最爱?



大家好,我是小米,一个爱码代码、爱研究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岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号"软件求生",获取更多技术干货!

相关推荐
JiaJZhong18 分钟前
力扣.最长回文子串(c++)
java·c++·leetcode
该用户已不存在22 分钟前
不知道这些工具,难怪的你的Python开发那么慢丨Python 开发必备的6大工具
前端·后端·python
Xy91028 分钟前
开发者视角:App Trace 一键拉起(Deep Linking)技术详解
java·前端·后端
嘻嘻哈哈开森33 分钟前
技术分享:深入了解 PlantUML
后端·面试·架构
爱学习的茄子35 分钟前
JavaScript闭包实战:解析节流函数的精妙实现 🚀
前端·javascript·面试
vvw&38 分钟前
Linux 中的 .bashrc 是什么?配置详解
linux·运维·服务器·chrome·后端·ubuntu·centos
Dgua38 分钟前
🚀Promise 从入门到手写:核心方法实现全指南
前端·面试
一个混子程序员43 分钟前
Mockito不常用的方法
java
厚道44 分钟前
Elasticsearch 的存储原理
后端·elasticsearch