赞
踩
垃圾其实是指那些没有被引用或者没有被任何指向的对象,那么这个这个对象就会被称为垃圾
如果我们不及时的将垃圾进行回收,就有可能会造成内存泄漏,因为这些垃圾一直占用着我们的内存直达我们的应用程序结束,并且其他的对象无法进来到内存当中。
- 垃圾回收(GC)为什么会被需要呢
首先得话,任何高级语言我们如果我们不进行垃圾回收的话,我们的内存迟早是会被消耗完的,因为我们要不断地给新的对象分配空间,所以我们需要GC帮助我们把那些不需要或者用不上的对象给回收,产生新的内存给我新对象使用
在java中拥有自动的内存管理,无需开发人员手动的参与内存的分配与回收,这样的就降低了内存泄漏和溢出的风险,从回收角度来看,java的堆是回收的重点,从我们的次数来看的话,频繁收集新生代,较少回收老年代,基本不会动元空间
因为当我们需要垃圾回收的时候,我们要知道哪些是垃圾可以被回收。
一般2种方式 引用计数算法和可达性分析算法
对每一个对象保存一个整型的引用计数器属性,用于记录对象被引用的情况
优点: 实现简单,垃圾对象便于识别,判断效率高,回收没有延迟性
缺点:1.需要单独的字段作为存储计数器,这样的做法会增加内存的开销 2.每一次的赋值都需要更新计数器,会增加我们的时间开销 3.无法处理循环引用的问题
- 1
- 2
- 3
特点:
对于引用计数算法来说,不仅仅是简单高效,而且它解决了循环引用的问题,防止了内存泄漏
它是一个追踪性垃圾收集器以对象集合为起点(GC Root)为起始点,按从上到下的方式搜索跟踪目标对象,看看能否到达, 在内存当中根对象和内存中对象,直接或者间接的连接,被称为引用链,如果没有的话,说明这个 垃圾对象(死亡对象),将其清除
- 1
- 2
- 3
那什么对象可以被称之为GC Roots对象呢
当成功区分出内存中存活对象和死亡对象后,GC接下来的任务就是执行垃圾回收、释放掉垃圾对象所占用的内存,以便有足够的可用空间为新对象分配内存。
目前在JVM中比较常见的三种垃圾回收算法是:
1.标记 - 清楚算法
标记-清除(Mark - Sweep)算法是最基础的收集算法,它分为“标记”和“清除”两个阶段:
标记:从GC Roots开始遍历,标记所有被引用的对象。一般是在对象头中记录是否是可达对象
清除:对堆内存从头到尾遍历,如果发现某个对象的对象头中没有标记为可达对象,则将其回收
优点:
不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效
缺点:
标记和清除过程的效率都不算高
这种方法需要使用一个空闲列表(空闲列表记录哪些内存是没有被占用状态,空闲的)来记录所有的空闲区域以及大小,对空闲列表的管理会增加分配对象时的工作量
标记清除后会产生大量不连续的内存碎片
2.复制算法
核心思想:
将内存空间分为两块,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的对象,交换两个内存的角色,最后完成垃圾回收
对于这种算法来说,如果存活的对象过多的话则要执行较多的复制操作,效率会变低,因此它适合存活率较低的情况。事实上在年轻代中就是使用的复制算法。
优点:
没有标记和清除的过程,实现简单,运行高效
复制过去以后保证空间的连续性,不会出现“碎片”问题
缺点:
需要两倍的内存空间,比较浪费
如果存活对象较多,那么复制操作就比较多,效率相对会降低
3.标记-整理算法
标记-整理分为“标记”和“整理”两个阶段:
标记:和标记清除算法一样,从GC Roots开始标记所有被引用的对象
整理:将所有的存活对象压缩到内存的一端,按顺序排放。之后清理外边界的空间(清理垃圾)
标记-整理算法的最终效果等同于标记-清除算法执行后,再进行一次内存碎片整理,因此也可以把它称为标记-清除-压缩算法
可以看到,标记的存活对象将会被整理,按照内存地址依次排序。如此一来,当我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可,这比维护一个空闲列表显然少了许多开销
优点:
消除了标记-清除算法中,内存区域分散的缺点(内存碎片)
缺点:
移动对象的同时,如果对象被其他对象引用,还需要调整引用的地址
移动过程中,需要全程暂停用户应用程序。即STW
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。