垃圾回收算法(思想)

栈的出栈相当于删除数据,所以是不需要进行垃圾回收的,但是堆内存以及方法区是需要对那些不再使用的对象进行垃圾回收,就需要讨论常见的垃圾回收算法。

垃圾回收算法需要注意到内存碎片的问题。 一。标记想要回收的垃圾对象算法(标记对象以便清楚无效的垃圾对象)

1、引用计数法:

引用计数器:为对象增加一个引用计数器,当对象被引用就增加1, 当这个引用失效了就减少1,为0的对象就不可以被使用,此时就可以 进行回收; 但是两个对象相互引用时,会导致引用计数器永远为1,所以这两个 对象就永远不能被回收。就出现了循环引用的问题,这个不是代表就 一无是处,根据业务需求来看。(这种方法简单高效,但是会造成循环引用的问题,python中使用的就是此种垃圾回收算法,但是python为解决循环引用的问题,使得python的垃圾回收器可以打破之间的循环。)

补充:应用计数器方法如何打破循环引用的问题?

2、根可达算法(gc_root)(java默认会使用这种算法标记垃圾对象)

alt

从根出发,向下寻找, 找到就标记为1,这样最 终内存中就存在一些为0 ,一些唯一的,那么可 被标记的就是可以检索 的,没被标记的就是不 能被引用的,当下一次垃圾回收执行时,这些对象会被清除掉。

jvm中对哪些对象能作为gc根对象的类型有一定的规定:

  • 虚拟机栈中引用的对象
  • 方法区中静态属性引用的对象
  • 方法区中常量的引用对象

二、垃圾回收算法

2.1标记-清楚算法

这种方法是指使用垃圾标记算法标记完需要清除的对象时,直接对垃圾对象删除。这种方法会产生许多的问题:1.产生碎片问题2.标记和清楚的过程效率都不高。

alt

2.2标记-整理算法(时间换空间,整理很复杂),适用于不是特别经常性的垃圾回收区域负责垃圾回收。

这种方法是为了解决2.1中的会产生内存碎片问题而产生的,它不是直接将不可用的对象直接删除,而是将可用的对象往堆内存的一侧移动,这时,我们只需要知道可用对象范围的末端位置即可,然后对范围外的内存区域直接清除。

2.3复制算法(空间换时间),适合某片内存区域中的不可用的垃圾对象占比比较多的情况,这时复制算法只需要较少次的复制那些可用的对象即可。

将堆内存区域划分为两个等内存大小的区域A和B,我们每次分配内存时只使用其中的一个区域,当区域中内存不足时,会将区域中的可用内存复制到另外的区域(依次复制),当复制完成时,会直接将之前的内存区域直接删除,这就解决了内存碎片问题。缺点是:1.一次只能利用一半的内存大小;2.当内存中的可用对象特别的多时可能会占用大量资源。

2.4分代垃圾回收算法(年轻代(对象的年纪还很小的时候就被回收掉,这种的对象一般存放在新生代)、幸存区、老年代),java的垃圾回收算法!(不同的对象有着不同的生命周期,不同的收集算法有着不同的特点,所以使用分代来让不同的区域存在着不同的垃圾回收算法。)

alt

new一个对象之后的内存分配的流程:

分代垃圾回收算法有以下几个特点:

1.对象优先分配在伊甸园区,这点很好理解,反过来的意思是对象可能不分配在伊甸园,他有可能分派在栈中(不用垃圾回收),也可能由于新对象的长度过大而把这个对象分配在老年代之中。

2.MinorGC和FullGC分别是指发生在年轻代和老年代上的垃圾回收,MinorGC由于是发生在年轻代中的它的垃圾回收是比较快速度的,也比较频繁。而fullGCs是发生在老年代中的垃圾回收,这个回收的动作一旦发生,由于fullg垃圾回收动作是使用编辑整理算法实现的,所以一旦发生垃圾回收意味着系统将会在未来的一点时间中出现资源占用,出现STW,stop the world这个现象。

3.大对象直接进入老年代!

这种大对象意味着需要大量的连续的内存空间,在编码时应当避免这种大对象的出现,因为其可能出现在内存依旧会有许多空间时由于存放不下大对象而触发垃圾回收。

4.长期存活的对象进入老年代(在幸存区中长期被复制算法GC之后依旧能存活时他将会进入老年代,java默认的老年代中的对象为15岁(次))

5.动态对象年龄,当幸存区中同年龄对象占据幸存区的一半时,此时不必等待对象的年龄到达15,就可以直接进入老年代。

6.空间分配担保 在发生ygc之前,jvm会检查老年代的可用内存空间是否大于新生代,如果大于才进行yGC否则就会fullgc 。

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务