垃圾标记算法、垃圾清除算法、内存分配算法

垃圾标记

垃圾标记阶段:
gc只回收死亡对象,所以在垃圾回收之前,首先需要区分出堆中哪些是存活对象,哪些是已经死亡的对象,这个过程就是垃圾标记阶段。

垃圾标记的两种方式:
判断对象是否存活一般有两种方式:引用计数算法可达性分析算法

引用计数算法

什么是引用计数算法?
为每个对象设置一个整型的引用计数器。用于记录对象被引用的个数。
比如有一个对象A
只要有任何一个对象引用了A,则A的引用计数器就+1;
当引用失效时,引用计数器就-1。
只要对象A的引用计数器的值为0,即表示对象A不再被使用,可进行回收。

引用计数算法有一个致命缺点:就是它无法处理循环引用的情况,导致在Java的垃圾回收器中没有使用这类算法。

什么是循环引用?
a引用b,b引用a

可达性分析算法(jvm选用)

可达性分析算法不存在循环引用的问题,因此java选择使用可达性分析算法

算法的主要思想是:
首先确定一系列肯定不能回收的对象作为GC roots,比如虚拟机栈里面的引用对象、本地方法栈引用的对象等,然后以GC roots作为起始节点,从这些节点开始向下搜索,去寻找它的直接和间接引用的对象,当遍历完之后如果发现有一些对象不可到达,那么就认为这些对象已经没有用了,需要被回收
在这里插入图片描述

那些对象可以作为GCRoots的对象

  • 虚拟机栈中引用的对象
    比如:User user=new User();

  • 方法区中类静态属性引用的对象
    比如:static User user=new User();

  • 方法区中常量引用的对象
    比如:final User user=new User();

  • 本地方法栈中JNI引用的对象

垃圾清除算法

当成功区分出内存中的存活对象和死亡对象后,GC接下来的任务就是执行垃圾回收,释放掉死亡对象所占用的内存空间。

目前在JVM中比较常见的三种垃圾收集算法是

  • 复制算法
  • 标记一清除算法
  • 标记-清除-整理算法

复制算法(适合新生代)

算法设计原理
将内存空间分为两块,每次只使用其中一块,在垃圾回收时,假设现在使用A内存块,B内存块空闲。
将A中存活的对象复制到B中,然后清除A中的死亡对象,即完成一次垃圾收集。
下次垃圾收集时AB的角色交换,循环往复。
在这里插入图片描述

优点

  • 没有内存碎片
  • 回收速度快

缺点
需要两倍的内存空间

标记-清除算法

先标记、后清除

标记
垃圾收集器从根节点开始遍历,标记所有被引用的对象。其实就是可达性分析算法

清除
垃圾收集器对堆内存从头到尾进行遍历,如果发现某个对象没有被标记为可达对象,则将其回收。

清除并不是真的置空,而是把需要清除的对象地址保存在空闲列表里。下次有新对象需要占用空间时,判断空闲列表中记录的可以被清除的对象所占的位置空间是否够,如果够,就用新对象覆盖原有的对象。

标记清除如下图示:

在这里插入图片描述

缺点
有内存碎片

优点
空间开销比复制算法小
回收速度比标记整理算法小
中庸型算法

标记-清除-整理算法

就是在标记-清除算法的基础之上再加一个整理

在这里插入图片描述

优点

  • 消除了复制算法当中,需要两倍内存的缺点。
  • 消除了标记-清除算法中造成内存碎片的缺点。

缺点
需要标记和整理两个阶段,加上移动对象时,如果对象被其他对象引用,则还需要调整引用的地址,所以算法执行的时间是三种清除算法中最久的

总结:标记整理算法 为了节约内存+不产生内存碎片,牺牲了时间。

分代收集算法

根据新生代和老年代的特点不同,
新生代采用复制算法
老年代采用标记-清除|标记-整理算法

年轻代的特点:区域相对较小,对象生命周期短、存活率低,回收频繁。
老年代的特点:区域较大,对象生命周期长、存活率高,回收不及年轻代频繁。

增量收集算法(少量多餐)

如果一次性将所有的垃圾进行处理,会造成系统长时间的停顿,为解决这一问题,可以分多次进行垃圾收集,让垃圾收集线程和应用程序线程交替执行,每次垃圾收集线程只收集一小片区域的内存空间,接着切换到应用程序线程。依次反复,直到垃圾收集完成。

分区算法(G1)

分代算法按新生代和老年代分别回收垃圾

分区算法将堆空间进一步划分成多个不同的小区间。每一个小区间都独立使用,独立回收。

如下将一块大堆空间划分为若干小区间:
在这里插入图片描述

复制、标清、标整、

特点总结:

复制标记清除标记整理
速率最快中等最慢
空间开销多(2倍空间)少,但有内存碎片少,没有内存碎片

使用场景:

新生代基本采用复制算法,老年代采用标记整理算法。cms采用标记清理

新生代为何适合用复制算法?
因为复制算法需要复制存活对象,所以存活对象越多,复制的越多,效率越低下。老年代有大量的对象存活,就不适合用复制算法。
相反,新生代的对象大多朝生夕死,所以一次通常可以回收70% - 99% 的内存空间。相应的需要复制的存活对象就很少。很适合使用复制算法。

老年代为何采用标记整理算法
因为老年代存活对象多,不适合复制,标清又有内存碎片。

cms为何选择标清呢
因为cms同时回收新生代和老年代,二者都要兼顾,相比复制节约内存,相比标整更快。

内存分配:指针碰撞+空闲列表

垃圾回收之后就可以为新对象分配内存了,分配方式分两种:指针碰撞和空闲列表

选择哪种分配方式由Java堆是否规整所决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定

  • 指针碰撞法
    所有用过的内存在一边,空闲的内存放另外一边,中间放着一个指针作为分界点的指示器,分配内存时,仅需要把指针往空闲那边挪动一段与对象大小相等的距离即可。

  • 空闲列表分配
    意思是虚拟机维护了一个列表,记录哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的内容。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值