java对象在gc后,地址变了,引用的值也要变,具体的机制是怎么样的?

yuxm909 2019-09-30 04:52:22

我们知道引用的值是指向对象的指针,也就是对象的地址,对象在gc之后,地址是有可能发生变化的,那么引用的值也需要做相应的变化,这里面具体的机制是怎么样的呢?
...全文
943 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_41888697 2019-10-04
  • 打赏
  • 举报
回复
路过,路过。
qybao 2019-10-03
  • 打赏
  • 举报
回复
引用 15 楼 yuxm909 的回复:
这句话让我有点糊涂:“系统类似于有个引用维护表(像可达性算法分析里提到的4种gc root和reference chain)”。我知道gc roots是一些对象,可达性算法以它们为起点来计算对象之间的引用关系,所以我就以为你说的引用维护表是用来保存对象之间的引用关系。
看你最新的回复,引用维护表其实是保存引用和对象的映射关系。

是的,是另外的管理表,只是像gc root和reference chain管理一样
你可以参考以下链接(里面的内容出自 thinking in java)
https://blog.csdn.net/wtopps/article/details/54408339
里面有段描述
当把对象从一处搬到另一处时,所有指向它的那些引用都必须修正。位于堆或静态存储区的引用可以直接被修正,但可能还有其他指向这些对象的引用,它们在遍历的过程中才能被找到。你可以想象有个表格,将旧地址映射到新地址,这样就可以在遍历的同时进行修改了。

如果你还想了解更多,就要找虚拟机原理一类的相关资料了

yuxm909 2019-10-03
  • 打赏
  • 举报
回复
引用 14 楼 qybao 的回复:
[quote=引用 10 楼 yuxm909 的回复:]
我又看了遍回复,是不是我的理解有误,感觉老哥你在说的是对象之间的引用关系(gc时jvm需要知道这些),我想了解的是A a = new A(); new出来的类A的对象在gc后地址发生变动,引用变量a的值是不是也会跟着改变,具体是在什么时候谁将它改变的。

上面不是说明白了吗,a的值变,在GC的STW(Stop The World)时期,通过遍历引用维护表(对象地址映射表)来改变的[/quote]
这句话让我有点糊涂:“系统类似于有个引用维护表(像可达性算法分析里提到的4种gc root和reference chain)”。我知道gc roots是一些对象,可达性算法以它们为起点来计算对象之间的引用关系,所以我就以为你说的引用维护表是用来保存对象之间的引用关系。
看你最新的回复,引用维护表其实是保存引用和对象的映射关系。
qybao 2019-10-03
  • 打赏
  • 举报
回复
引用 10 楼 yuxm909 的回复:
我又看了遍回复,是不是我的理解有误,感觉老哥你在说的是对象之间的引用关系(gc时jvm需要知道这些),我想了解的是A a = new A(); new出来的类A的对象在gc后地址发生变动,引用变量a的值是不是也会跟着改变,具体是在什么时候谁将它改变的。

上面不是说明白了吗,a的值变,在GC的STW(Stop The World)时期,通过遍历引用维护表(对象地址映射表)来改变的
qybao 2019-10-03
  • 打赏
  • 举报
回复
引用 10 楼 yuxm909 的回复:
我又看了遍回复,是不是我的理解有误,感觉老哥你在说的是对象之间的引用关系(gc时jvm需要知道这些),我想了解的是A a = new A(); new出来的类A的对象在gc后地址发生变动,引用变量a的值是不是也会跟着改变,具体是在什么时候谁将它改变的。

上面不是说了吗,a的值变,
oh_Maxy 2019-10-03
  • 打赏
  • 举报
回复
引用 11 楼 yuxm909 的回复:
[quote=引用 9 楼 oh_Maxy 的回复:]
简单理解如下:
GC做完内存清理后,会有很多空间不连续的碎片。
为了将小碎片连在一起,可能会对存活对象做位移操作的。

做了位移操作之后,对象的地址就变了,对象的引用的值也要跟着变吧(A a = new A(); a的值也会跟着变)。
突然对这里面的机制有些好奇。[/quote]
这是虚拟机给我们封装好了,我们只要正常的使用a即可。
a就好比一个电视遥控器,电视进维修厂修理过后,很多元件换了,但是我们的遥控器依然可以操作电视。
如果好奇,你可以学习参观修理厂,是怎么修电视的。
yuxm909 2019-10-03
  • 打赏
  • 举报
回复
引用 9 楼 oh_Maxy 的回复:
简单理解如下:
GC做完内存清理后,会有很多空间不连续的碎片。
为了将小碎片连在一起,可能会对存活对象做位移操作的。

做了位移操作之后,对象的地址就变了,对象的引用的值也要跟着变吧(A a = new A(); a的值也会跟着变)。
突然对这里面的机制有些好奇。
yuxm909 2019-10-03
  • 打赏
  • 举报
回复
引用 6 楼 qybao 的回复:
[quote=引用 3 楼 yuxm909 的回复:]
按我的理解,复制和标记整理都是会移动对象的,那么地址也就会发生改变吧,不太明白老哥为啥让我再理解下算法,请指点一哈。


晕,上面文章没有提到gc算法的区别,所以也没有提及对象地址改变后如何修正引用指向。
参考以下文章
http://tang.love/2017/10/29/gc_category_in_jvm/?from=singlemessage&isappinstalled=0
在算法比较章节里提到标记压缩(Mark-Compact)和复制(Copying)算法对引用修正的区别,一般引用的修正发生在STW(Stop The World)期间,性能不好的系统会有停顿卡机的感觉就是因为这个。
其实说白了,就是系统类似于有个引用维护表(像可达性算法分析里提到的4种gc root和reference chain),gc对象发生移动后,一般对于堆区或静态区的引用,直接修改引用的指向(A->B,直接修改为A->C);其他的引用,通过修改映射关系来间接改变指向(A->映射关系AB->B,修改为A->旧映射关系AB->新映射关系BC->C)。
为什么有些引用直接修改指向,有些引用修改映射关系?这应该也是一个性能平衡考虑,就好比新生代用复制gc算法,老生代用标记压缩gc算法一样,单纯的修改引用指向,如果引用很多,比如多个引用同时指向同一个对象,遍历每个引用效率会很低,而如果只改变映射关系(旧地址到新地址映射),那么改变一个地方,所有的引用就都能同时改变为指向对象新地址,这样效率就大大提高。
[/quote]
我又看了遍回复,是不是我的理解有误,感觉老哥你在说的是对象之间的引用关系(gc时jvm需要知道这些),我想了解的是A a = new A(); new出来的类A的对象在gc后地址发生变动,引用变量a的值是不是也会跟着改变,具体是在什么时候谁将它改变的。
oh_Maxy 2019-10-03
  • 打赏
  • 举报
回复 1
简单理解如下:
GC做完内存清理后,会有很多空间不连续的碎片。
为了将小碎片连在一起,可能会对存活对象做位移操作的。
qybao 2019-10-03
  • 打赏
  • 举报
回复
引用 7 楼 yuxm909 的回复:
感谢指点,大概明白了引用修正。但是链接里就提了一句“Mark-Compact 可能要先计算一次对象的目标地址,然后修正指针,然后再移动对象”,没其它细节了。

有兴趣你可以研究一下jvm的代码,或者有本书叫什么自己动手开发虚拟机来的,你可以看看
qybao 2019-10-02
  • 打赏
  • 举报
回复
引用 3 楼 yuxm909 的回复:
按我的理解,复制和标记整理都是会移动对象的,那么地址也就会发生改变吧,不太明白老哥为啥让我再理解下算法,请指点一哈。


晕,上面文章没有提到gc算法的区别,所以也没有提及对象地址改变后如何修正引用指向。
参考以下文章
http://tang.love/2017/10/29/gc_category_in_jvm/?from=singlemessage&isappinstalled=0
在算法比较章节里提到标记压缩(Mark-Compact)和复制(Copying)算法对引用修正的区别,一般引用的修正发生在STW(Stop The World)期间,性能不好的系统会有停顿卡机的感觉就是因为这个。
其实说白了,就是系统类似于有个引用维护表(像可达性算法分析里提到的4种gc root和reference chain),gc对象发生移动后,一般对于堆区或静态区的引用,直接修改引用的指向(A->B,直接修改为A->C);其他的引用,通过修改映射关系来间接改变指向(A->映射关系AB->B,修改为A->旧映射关系AB->新映射关系BC->C)。
为什么有些引用直接修改指向,有些引用修改映射关系?这应该也是一个性能平衡考虑,就好比新生代用复制gc算法,老生代用标记压缩gc算法一样,单纯的修改引用指向,如果引用很多,比如多个引用同时指向同一个对象,遍历每个引用效率会很低,而如果只改变映射关系(旧地址到新地址映射),那么改变一个地方,所有的引用就都能同时改变为指向对象新地址,这样效率就大大提高。
yuxm909 2019-10-02
  • 打赏
  • 举报
回复
引用 4 楼 rumlee 的回复:
对象在执行gc之后,内存地址当然是是可能会改变的,改变了之后原来的引用依然会指向新的地址(否则就乱套了)。所以对于java程序层面来说,考虑这个问题其实没啥意义,这是虚拟机该去考虑的问题。

单纯好奇
rumlee 2019-10-02
  • 打赏
  • 举报
回复
对象在执行gc之后,内存地址当然是是可能会改变的,改变了之后原来的引用依然会指向新的地址(否则就乱套了)。所以对于java程序层面来说,考虑这个问题其实没啥意义,这是虚拟机该去考虑的问题。
yuxm909 2019-10-02
  • 打赏
  • 举报
回复
引用 6 楼 qybao 的回复:
[quote=引用 3 楼 yuxm909 的回复:]
按我的理解,复制和标记整理都是会移动对象的,那么地址也就会发生改变吧,不太明白老哥为啥让我再理解下算法,请指点一哈。


晕,上面文章没有提到gc算法的区别,所以也没有提及对象地址改变后如何修正引用指向。
参考以下文章
http://tang.love/2017/10/29/gc_category_in_jvm/?from=singlemessage&isappinstalled=0
在算法比较章节里提到标记压缩(Mark-Compact)和复制(Copying)算法对引用修正的区别,一般引用的修正发生在STW(Stop The World)期间,性能不好的系统会有停顿卡机的感觉就是因为这个。
其实说白了,就是系统类似于有个引用维护表(像可达性算法分析里提到的4种gc root和reference chain),gc对象发生移动后,一般对于堆区或静态区的引用,直接修改引用的指向(A->B,直接修改为A->C);其他的引用,通过修改映射关系来间接改变指向(A->映射关系AB->B,修改为A->旧映射关系AB->新映射关系BC->C)。
为什么有些引用直接修改指向,有些引用修改映射关系?这应该也是一个性能平衡考虑,就好比新生代用复制gc算法,老生代用标记压缩gc算法一样,单纯的修改引用指向,如果引用很多,比如多个引用同时指向同一个对象,遍历每个引用效率会很低,而如果只改变映射关系(旧地址到新地址映射),那么改变一个地方,所有的引用就都能同时改变为指向对象新地址,这样效率就大大提高。
[/quote]
感谢指点,大概明白了引用修正。但是链接里就提了一句“Mark-Compact 可能要先计算一次对象的目标地址,然后修正指针,然后再移动对象”,没其它细节了。
yuxm909 2019-10-01
  • 打赏
  • 举报
回复
引用 2 楼 qybao 的回复:
你可以参考以下文章,java的内存模型以及垃圾回收原理,好好理解新生代老年代的算法
https://www.cnblogs.com/yueshutong/p/9768298.html

按我的理解,复制和标记整理都是会移动对象的,那么地址也就会发生改变吧,不太明白老哥为啥让我再理解下算法,请指点一哈。
qybao 2019-10-01
  • 打赏
  • 举报
回复
你可以参考以下文章,java的内存模型以及垃圾回收原理,好好理解新生代老年代的算法
https://www.cnblogs.com/yueshutong/p/9768298.html
yuxm909 2019-09-30
  • 打赏
  • 举报
回复
找到一篇 https://stackoverflow.com/questions/35548337/does-object-reference-change-after-gc?r=SearchResults 里面只是提及引用的值确实会变

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧