【趣话编程】一个Java对象的回忆录:垃圾回收

简介: 趣话编程第三期,今天让我们一起去看看一个Java对象的回忆录:垃圾回收。

原文链接

对象的诞生

“你醒啦!”,迷迷糊糊中听到一个声音,我睁开了眼睛,发现一个小伙伴正看着我。

“这里是哪里,你是谁啊?”“这里是堆区,我是一个Ajax对象,叫我小A吧”
我慢慢坐了起来,举目四望,这里有好多形形色色的对象居住在这里,远处还有好多的线程在各自忙碌着,好一副热闹的景象!
image.png
“你好,我才刚醒,我还不知道我是什么对象呢”“这个简单,让我看看哈~~哦,原来你是一个APIController对象啊”,小A摸了摸我的头。“你怎么知道的?”
“你的头上这里有个64bit的Klass指针,喏,顺着这个方向看过去,那里记录了你所属的类信息,你看,那里写着APIController呢”
image.png
听他这么一说,我这才注意到我的头上有两个64bit的字段。
“唉,小A,旁边这个64bit的数字又是装的什么内容呢?”“那个叫MarkWord,是咱们Java对象的门面,里面的信息可重要了,你可要保管好了,这里面有。。。”

Minor GC


突然,不知从哪里传来一串警报声,随后听到广播:“各线程注意,请进入安全点等待,各分区注意,启动一次Minor GC”
image.png
听到广播的我莫名的紧张起来。
不知怎么回事,远处忙碌的线程们都仿佛被施了定身法一般,都停下了手头的活。只有少数几个还在活动,这几个线程大叔看上去跟他们有些不一样,其中有几个朝我们这边走了过来。
“这是要干什么啊?”我向小A打听情况。“我也不知道,我也比你先出生没多久,这情况我也是第一次见到”,小A好像也有一点慌张。
没过多久,来了一个凶巴巴的管理员线程,拿着喇叭吼着:“Eden区的对象们听着,念到名字的站起来”说完,便开始一个个点名,心里一阵忐忑,怕被叫到,又怕不被叫到。
image.png
念了很久,终于听到了小A和我的名字,我俩战战兢兢的站了起来。
没多久就念完了,我一瞅,站起来的是少数啊,心里有点不好的预感。“念到名字的跟我来,其他的交给我的助手处理”,说完大家跟着他开始移动。
在走的路上,碰上了另外一支队伍,和我们汇合了。
image.png
“唉,兄弟,怎么称呼,你们哪个单位的?”小A热情的上去和一个对象攀谈了起来。“叫我小B吧,我们这波是Survivor From区的,你们Eden区来的吧,我半小时前还在你们那儿待过呢”,这个自称是小B的也很随和。
“小B哥您好,咱们这是要去哪里啊?”我也上前搭了句话。“前面是Survivor To区”
“咱们是怎么被挑出来的?”“这里的管理员会通过一种叫GC Roots的对象,顺藤摸瓜,找出所有还有引用关系的对象,咱们就是幸存者,说明咱们还有价值”
image.png
“那留下的对象怎么办?”“他们的命运多半悬了,因为没有别的对象引用他们了,需要把他们清理掉,腾出空间来”
我似懂非懂,一边走一边担心着,很快我们就到了传说中的Survivor To区,管理员安排我们都坐下,“这里好小啊”
“那可不,比起你原来的Eden区,这里只有八分之一大小”,我一回头,刚才路上碰到的小B居然就在我和小A的旁边,巧了不是。“唉,小B哥,咱们这么折腾一圈是在做什么啊?”
image.png
“这叫做垃圾回收GC,你们开始待那地方叫Eden区,对象出生的地方都在那里。咱们所在的地方是一个叫Java Virtual Machine的世界,程序员只管创建对象,不管释放,这对象越来越多,Eden区放不下了,自然就要腾出空间来了。”
我和小A都点了点头,心里庆幸躲过一劫,抬头望去,不知什么时候,那些定住的线程们又开始忙活起来了。
“还没恭喜你们呢,长大一岁了”,小B拍了拍我俩的肩膀,我俩面面相觑,满脸问号。“这是从何说起呢?”,小A先开口了。
“你们头上的MarkWord第3-6位记录的就是你们的年龄,经过一次GC就长大一岁了!”我俩互相看了看,又看了看小B的GC年龄位置,居然已经15岁了。
image.png
“小B哥,难怪你见多识广,都一把年纪了呀。咦,这表示GC年纪的只有4位,最大只能表示到15,等会儿要是再来一次GC,这不要装不下了吗?”,看着小B的脑袋,我陷入了思考。“再来一次GC我要是还能幸存,我就要进入老年代区域了,就不能陪你们玩儿了”,小B看着我们眨了眨眼睛。
“老年代,那是什么地方,我们不能去吗?”“都说了是老年代了,是我这种老年对象去的地方,你们新来的还要在Survivor To区和Survivor From区兜兜转转好些回合呢,等你们到我这把年纪就能过去了”
image.png
“啊,为什么这么麻烦,设置这么多区都是干嘛的啊?”小A急着问。小B把手搭在小A的肩说到:“这里的管理员用的是标记-复制算法来清理空间,所以需要在Eden区之外再设一个地方接收复制活下来的对象。”
“那加一个Survivor区就够了啊,干嘛弄两个Survivor区?”我也抛了一个问题。小B把另一只手搭在了我的肩上,“这是为了让存活的对象能够在这边反复流转,不要着急去老年代区域”
“那为什么SurvivorEden小那么多?”,我继续问到“根据他们统计发现,98%的对象都活不过一轮GC,留下来的都是少数。而且两个Survivor区有一个要空着,如果太大就太浪费了。”
听着小B的话我们俩都陷入了沉思。
image.png
没过一会儿,广播又响了起来:“各线程注意,请进入安全点等待,各分区注意,启动一次Minor GC”,刚刚平静的心又一次悬了起来。管理员又开始点名,这一次,我和小B都被点到了,而没有听到小A的名字。
我们跟小A告别了,离开了Survivor To区,走到分叉路口,小B也跟我道别:“再见了朋友,如果有机会,老年代等你来再聚”
接下来就只剩我一个对象了,跟随陌生的对象队伍来到了Survivor From区,这里跟刚才的To区规模相当,只是队伍比起之前那次又小了很多。
来到自己的位置坐好,看了看头上的GC年龄位,我2岁了。

Finalizer对象


没有了熟悉的朋友,独自发着呆,等待着线程们来访问我。突然,有人拍了拍我的肩膀,我回头看去,居然是小A,他跑的气喘吁吁的。
image.png
“你不是没有被念到名字,没有对象再引用你了吗,居然没有被清理?”,再次看见小A,我有点难以置信。“刚才真的好险,我都吓死了,没想到事情出现了转机!”
“发生了什么,快告诉我!”,我迫不及待的想知道这一切究竟是怎么回事。小A喘了几口气继续说到:“就在你们走后,管理员又拿出了另外一份名单,我的名字居然在上面,我一打听才知道原来检查到有一个Finalizer对象还在引用我~”
“奇怪了,不是说没有对象吗,怎么又冒出了一个Finalizer对象?这是什么?”“后来我见到了那个Finalizer对象,就在开始我们没多远的位置,听他说是因为我所属的类有覆盖finalize方法,所以在我出生的时候,他也一同诞生,并且一直持有我的引用
image.png
“那后来呢?”,我继续追问。“后来啊,他被管理员放进了一个ReferenceQueue的队列去了,他们把那地方叫F-Queue监狱。等待一个名字也叫Finalizer的线程大叔去处理,通过Finalizer对象来调用我的finalize方法,之后就把我们之间的联系切断了”“他要倒霉了!不过好在还是救了你一命”“躲得了初一,躲不过十五,现在连Finalizer对象也没引用我了,下一次GC我铁定要完蛋的”,小A说完又低下了头。“别想那么多,做对象最重要的就是开心了,说不定下一次我陪你一起完蛋呢”
image.png
我俩刚刚说完,熟悉的广播又想起了:“各线程注意,请进入安全点等待,各分区注意,启动一次Minor GC”很快,管理员就念到了我的名字,看来我还能撑下去。快到结束的时候,管理员居然神奇般的念到了小A的名字。
“小A,你听到了吗,居然还有对象在引用你!”我高兴的对小A说到,小A也使劲点点头。“我知道了,一定是Finalizer线程大哥在执行我的finalize方法的时候,又把我和谁建立了联系,对,一定是这样!”
我们再一次从To区来到了From区,这一次又少了很多旧面孔,不过从Eden区来了不少新面孔。
往后的一段时间里,我们在这兜兜转转了好多轮,终于看到头上的年龄标记慢慢变成了15岁。

Full GC


没过多久,广播再次响起,我和小A幸运的再次被点到名字,随后,管理员检查了我和小A,发现我俩超龄了,直接给我们赶到了一条新的路上,我知道前面就是传说中的老年代了。
来到这个陌生的地方,放眼望去,这里可比我待过的Eden区、From区、To区加起来还要大,里面有好多的对象,密密麻麻的,不过看上去一个个都不是省油的灯,毕竟能来到这里的对象,比起Eden区的那些萌新都是些老油条了。
image.png
我俩找到自己的位置坐下开始闲聊,这时,从不远处走过来一个身影,走近一看正是之前的小B。“唉,这不是小A吗,记得你不是被清理了吗,怎么来这儿了?”,对于小A的出现,小B哥有些惊讶。
随后小A也向小B聊起了之前的那段惊险经历~正在闲聊的时候,管理员突然进来圈了一大片空闲的位置,创建了一个巨大的对象!众对象都惊呆了!
image.png
“小B哥,这哥们什么来路,空降啊,直接到老年代!”我好奇的问到。“没办法,谁叫人家体格大,Eden区要么装不下,要么嫌给他复制过去复制过来太费劲,所以直接给安排到这里了,不像我们要一点点熬过来。”
“各线程注意,请进入安全点等待,各分区注意,启动一次Full GC“,熟悉的广播又一次响起,只不过这一次听到的是Full GC。“看来内存吃紧了啊!”小B叹了口气。
管理员又开始点名了,这一次,幸运不再眷顾小A。


未完待续~~

彩蛋

一个线程小姐姐迎面向我走了过来。
“Hi,小朋友你好,我是3002号线程,现在我要来锁定你,别动哦,让我检查下你的MarkWord”“lock位是01,Good!让我再看看偏向锁标记位,呀!是个1,糟糕”,小姐姐皱起了眉头。
欲知后事如何,请关注后续精彩......

来源 | 编程技术宇宙
作者 | 轩辕之风

相关文章
|
2月前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界中,异常处理是代码健壮性的守护神。本文将带你从异常的基本概念出发,逐步深入到高级用法,探索如何优雅地处理程序中的错误和异常情况。通过实际案例,我们将一起学习如何编写更可靠、更易于维护的Java代码。准备好了吗?让我们一起踏上这段旅程,解锁Java异常处理的秘密!
|
2月前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
71 0
|
2月前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
2月前
|
算法 Java 调度
java并发编程中Monitor里的waitSet和EntryList都是做什么的
在Java并发编程中,Monitor内部包含两个重要队列:等待集(Wait Set)和入口列表(Entry List)。Wait Set用于线程的条件等待和协作,线程调用`wait()`后进入此集合,通过`notify()`或`notifyAll()`唤醒。Entry List则管理锁的竞争,未能获取锁的线程在此排队,等待锁释放后重新竞争。理解两者区别有助于设计高效的多线程程序。 - **Wait Set**:线程调用`wait()`后进入,等待条件满足被唤醒,需重新竞争锁。 - **Entry List**:多个线程竞争锁时,未获锁的线程在此排队,等待锁释放后获取锁继续执行。
85 12
|
2月前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
221 2
|
3月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
3月前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
2月前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
2月前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
72 3
|
2月前
|
缓存 监控 算法
Python内存管理:掌握对象的生命周期与垃圾回收机制####
本文深入探讨了Python中的内存管理机制,特别是对象的生命周期和垃圾回收过程。通过理解引用计数、标记-清除及分代收集等核心概念,帮助开发者优化程序性能,避免内存泄漏。 ####
64 3

热门文章

最新文章