垃圾收集算法

发布时间 2023-11-30 18:19:54作者: twfplayer

垃圾收集算法

在Java内存运行时区域中,堆和方法区有着显著的不确定性:

  1. 接口的多个实现类需要的内存可能不同
  2. 方法执行中不同条件需要的内存空间也不同
    这部分内存的分配和回收是动态的。

两个问题,回收谁、怎么回收

回收谁——可回收垃圾(算法)

当对象没有被任何地方引用时,显然是可回收的,

引用计数算法

问题:

  1. 需要额外处理:被引用+1,引用失效-1,=0时失效
  2. 循环引用问题:A与B互相引用,且只有该引用,则实际不可访问

可达性分析算法

主流算法,以GCRoot的根对象作为起始集,以引用关系向下搜索,搜索路径称为引用链,如果对象不可达,就是不可用的(从根对象不能遍历到的对象),如此就解决了循环引用的问题

回收方法区

如题意:废弃的常量和不在使用的类型

废弃常量

没有任何对象引用该常量,虚拟机中也没有其他地方引用该量——>垃圾回收器判断有必要时,清理常量池(类(接口)、方法、字段。。。)

不在被使用的类

  1. 实例已被回收
  2. 类加载器被回收
  3. 对应的Class对象没有被任何地方引用

怎么回收——找到他、回收他

找到他-分代收集理论

  1. 大多数对象朝生夕死
  2. 越老越不死

因而:将java划分为不同的区域,根据年龄划分,分配到不同的区域。(回收时只关注需要存活的对象)
一般划分为老年代新生代
分代收集理论的逻辑大概是这样的,由于绝大多数对象朝生夕死,并且越老越难以消亡,那么将Java堆划分出不同的区域,根据对象熬过垃圾收集过程的次数,分配到不同的区域中存储。这样做的好处是每次回收时只需要关注少量能够存活的对象,将其复制移动到对应区域,然后刷新整个回收部分,这样就能以较低的代价回收大量空间。同时由于剩下的对象难以消亡,其相应的回收频率也可以降低。那么垃圾收集的时间和内存开销都可以得到有效的优化。

那对于我个人而言呢,有一种玄幻的理解方式,老年代可以是仙界,新生代可以是人间。
天上一日,人间一年。(新生代的垃圾收集较老年代而言相对频繁)。
众生渺渺有仙缘者难寻(朝生夕死的对象占绝大多数),
入我门来可以修仙(新生代也会划分两个区域,8:1的eden和survivor,可用内存容量为一个eden+一个survivor,另一个survivor用于回收前的数据迁移),
修仙难成,劫需15(新生代晋升到老年代,一般需要经历15次垃圾收集)。
特别内容,仙人思凡,降下天恩,凡入仙籍,寿岁延绵(跨代引用,新生代中的对象有可能会被老年代引用,那么为了保证该对象不会被垃圾收集误杀,就需要标记引用的对象——不过毕竟占少数,因此不需要扫描整个老年代,只需要在新生代上建立一个数据结构来记录这些存在跨代引用的老年对象)
这样是不是一下就好记了,嘿嘿

回收他-GC算法

标记-清除

简单的说就是。当需要回收或需要保留的对象分别标记,然后清除需要回收的对象保留其余对象
存在两个问题,如果对象太多。那标记和清除势必会导致效率下降,二是清除后会导致很多不连续的碎片,那么新的较大的对象无法插入势必会导致新一次的垃圾收集

人间大劫,天道有感,善者生恶者死,于是功德系统崩了。。。而且因为只有密度降低,还是导致了问题

标记-复制

分别是半区复制和appel式回收

半区复制是将内存划分为相同大小的两块,每次用完一块后,进行垃圾收集,将剩余的对象复制到另一块上,然后一次性清理该块空间

地球切两半,先把存活的或不能存活的挑出来,放到其中一半上,剩下一半直接洗地

appel式回收,就是先前提到的8:1,一共三块,一块大的eden两块小的survivor。如果。被用于留存的那一块survivor装不下可以分配一部分到老年代

人间大劫即将来临,有两个避难所,各自占整个世界10%,一个避难所上一次用过,这次也会被大劫波及,(所以实际上被回收算法扫描的是90%的空间),将能够存活的放到一个避难所,避难所放不下关系户的放到仙界(能存活的对象不止10%,老年代担保存放一部分)

这样做的好处是,垃圾回收是比较方便,并且回收后可以有大块的可利用内存空间

标记-整理

老年代保留的对象难以消亡,需要进行较多的复制操作,且标记复制算法需要一定的冗余空间。所以不使用标记复制算法,而使用标记整理算法——让所有可存活对象向空间一端移动,清理掉边界以外的内存

仙界资源重新分配,大佬都在33天上,其余部分直接洗地
人间大劫,天道有感,善者生恶者死

如有疏错,恳请指正。