Apple a = new Apple();
我们在new一个对象时会返回我们一个对象地址的引用,我们可以根据这个引用去找到对象的真实地址,我们是将这个引用赋值给了一个引用类型变量。比如这里a就是存贮我们new出来这个对象的引用类型变量。多数情况下,虚拟机会将对象创建到堆内存中,当我们需要访问这些对象时,会根据引用找到具体的虚拟内存地址,这个寻找对象的实现方式有两种,分别是句柄访问和直接指针访问。
还需要明白的一件事情是,我们存储在堆中的对象存储的是对象的非静态属性信息,如果我们只是找到内存中的这部分数据我们是不能确定这个对象具体是属于哪一个类,所以我们还需要去定位这个对象的类信息数据,类信息数据是存储在方法区中的。为什么要分开存储我就不多说了。
简言:根据引用找对象 = 找对象数据 + 类型数据
直接指针访问
引用指向的内存存储对象的实例,这个实例在存储实例数据同时还存储了对象的类型数据,我们去寻对象的时候是找到堆中的对象,再根据对象中存储的类信息指针去找到方法区具体的类型信息。
句柄访问
可以把句柄池看做一张路由表,上面的每组信息就是一个对象的引用,栈中存储的就是这些引用,而这些信息又记录了对象的实例数据和类型数据的地址。在这种方式下我们去寻找一个对象的过程大概就是,根据栈中的引用去定位句柄池对应的记录,根据记录分别去堆中找对象实例数据和去方法区中找对象的类型数据。
直接寻址的好处
先从寻址的次数来讲,直接访问只需要两次寻址就能完成整个流程,通过句柄方式的话则需要三次;
然后是通过句柄方式访问的话我们需要额外维护一个句柄池,这在内存和性能上都会有额外的开销,直接寻址就简单不少,只需要在创建对象时添加类型数据就可以了;
句柄访问的好处
貌似从前面看句柄访问有些吃力不讨好的感觉了,但是既然存在说明肯定有存在的意义。拉出去先。
我们先简单介绍一下jvm的垃圾回收,内存优化(感觉有点长,到时候专门写一篇,假装这里是一个链接地址)。
拉回来,所以在程序执行的过程中堆中的实例数据位置是会改变的,而咱们一个实例可能是会被多个线程多个方法栈引用(比如web中的context),如果使用直接内存访问的话,当我们虚拟机进行内存整理后我们就要去更改这些栈中的引用,这个开销不要太大了简直。但是要是使用句柄的话就简单了不少,我们只需要去修改句柄池中的记录,下一次这些栈来找对象的时候就能直接根据我更新好的记录找到内存调整后的位置信息。哇塞,突然变得香了起来对不!
结语
自己写着玩,有些是不懂或者没理解好,有些是懂但是懒得敲,这些也是自己看书看的,周志明老师的《深入理解Java虚拟机》。但是我是自己梳理一遍说出来的,要是感兴趣想深入研究还是去看大佬的权威版本。拜拜