垃圾收集器与内存分配策略

来源:互联网 发布:剑灵人物数据 编辑:程序博客网 时间:2024/06/07 03:20

1、判断对象是否需要回收

1.1 引用计数法

为对象添加一个引用计数器,有地方引用它时,计数器+1。当引用失效时,计数器-1。当计数器值为0时,表示对象不再被应用,可以回收。
引用计数法简单、效率高
但是对于两个对象互相引用,容易造成内存泄漏

1.2 可达性分析算法

使用一系列根节点(GC Root)的对象作为起始点,向下搜索,搜索所走过的路径称为引用链。当一个对象到根节点不可达时,表示这个对象没有被引用

作为根节点的对象:
虚拟机栈中引用的对象
方法区中类的静态引用对象
方法区中常量引用的对象
本地方法栈中JNI引用的对象

1.3 引用的分类

1.3.1 强引用
普通new的对象,只要对象有强引用,垃圾收集器永远不会回收该对象

1.3.2 软引用 SoftReference
用来描述一些还有用但是非必需的对象。对于软引用关联的对象,在系统将要发生内存溢出异常之前,将会对软引用的对象进行回收。如果还是没有足够的空间,将抛出OOM
即内存足够,软引用的对象一直存在。内存不足,将软引用的对象回收

1.3.3 弱引用 WeakReference
用来描述非必需的对象,它的强度比软引用弱。当垃圾回收器回收时,无论内存是否充足,都会回收弱引用关联的对象

1.3.4 虚引用 PhantomReference
虚引用的唯一目的就是这个对象被垃圾回收器回收时,收到一个系统通知

1.4 finalize 对象最终的回收

使用可达性搜索算法发现一个对象不可达时,这个对象将被第一次标记并第一次筛选。如果对象没有重写finalize方法或已经被调用过,则虚拟机认为没有必要执行finalize,将被直接回收。
如果需要执行对象的finalize方法,虚拟机会将对象放入F-Queue的队列中,并由一个由虚拟机创建的,低优先级的Finalizer线程执行队列中对象的finalize方法。如果对象再次被引用,虚拟机进行第二次标记时会将该对象从F-Queue移出。否则对象将被回收。
虚拟机不会等F-Queue执行完才进行再次标记,因为finalize方法或许执行很慢,或许有死循环等
finalize方法运行的代价高昂,不确定性大,应该避免使用

2、垃圾收集算法

2.1 标记-清除算法
算法分标记和清除两个阶段。
不足:标记和清除效率不高、内存碎片的问题

2.2 标记-复制算法
将堆内存分为相等的两块,每次使用其中一块。如果一块用完,将存活的对象放入另一块,再将这块内存清理掉。实现简单、运行高效、不用考虑内存碎片问题。不足是每次只能使用内存的一半。

2.3 标记-整理算法
将存活的对象移动到内存的一端。然后清理掉另外一端的内存。

2.4 分代收集算法
将内存划分几块。一般java堆分为新生代和老生代。
新生代每次垃圾回收,都会有大量的对象死亡,使用标记-复制算法。
老年代存活率高,使用标记-清除或者标记整理算法

3、垃圾收集器

3.1 Serial收集器 (串行收集器)
最基本的收集器,它是单线程的。它进行垃圾回收时,将暂停其他所有的工作线程,直到回收完毕。
简单、高效、停顿时间稍长,在新生代使用,使用标记-复制算法

3.2 ParNew (串行收集器)
Serial的多线程版,其他与Serial相同

3.3 Parallel Scavenge收集器 (吞吐量优先收集器)
使用标记-复制算法
吞吐量:运行用户代码的时间/(运行用户代码的时间+垃圾回收时间)
类似串行收集器(多线程),但是更加关注吞吐量

3.4 Serial Old收集器 (串行收集器)
Serial的老年代版本,使用标记-整理算法

3.5 Parallel Old收集器
Parallel Scavenge的老年代版本,使用标记-整理算法。

3.6 CMS收集器
以最短回收停顿时间为目标的收集器,使用标记-清除算法,CMS是针对老年代的收集器。
它的处理分四个阶段:
1、初始标记:需要暂停应用程序,快速标记存活对象。
2、并发标记:恢复应用程序,并发跟踪GC Roots。
3、重新标记:需要暂停应用程序,重新标记跟踪遗漏的对象。
4、并发清除:恢复应用程序,并发清除未标记的垃圾对象
并发标记和并发清除这两个阶段,而这两个阶段也是整个GC阶段中耗时最长的阶段,不过由于这两个阶段皆是与应用程序并发执行的,因此CMS搜集器造成的停顿时间是非常短暂的。
但是它也有3个明显的特点:
① 对CPU资源非常敏感。在并发阶段,CMS虽然没有停顿,但是在并发标记和并发清除阶段会占用CPU资源,导致应用程序变慢、吞吐量降低。在CPU小于4个时,CMS对应用程序的影响比较大。
② CMS无法处理浮动垃圾。CMS并发清理阶段,用户程序还在运行,自然有新的垃圾对象不断产生,这部分垃圾对象是在标记过程之后,CMS无法清除它们,称它们为浮动垃圾。因为浮动垃圾的存在,需要预留一部分内存提前进CMS回收。如果预留内存无法满足应用需要,就会触发Concurrect Mode failure,这是虚拟机会启用临时方案:Serial Old进行老年代的回收,导致停顿时间变长。
③CMS使用标记-清除算法,会导致许多内存碎片的存在。当空闲内存不足以提供足够大的内存存放大对象时,会导致full GC,导致停顿时间变长。

3.7 G1收集器
。。。

4、gc日志

http://blog.csdn.net/yxc135/article/details/12137663
http://www.cnblogs.com/jing99/p/6091431.html

5、内存分配策略

这里写图片描述
5.1 新生代分为一个eden区和两个survivor区。大部分的对象都是在eden中产生。如果eden满了,将发生新生代的GC,就将eden中存活的对象复制到survivor区的其中一个s0中。对象继续在eden中产生,eden再次满时,再次发生新生代的GC,将eden和s0中存活的的对象复制到s1中,如此往复。多次垃圾回收还存活的对象将放入老年代

5.2 大对象需要大量连续的内存空间。新生代gc需要标记-复制操作,对大对象效率不高,所以直接分配到老年代。大对象如数组、长字符串等。应该避免大对象频繁的创建、回收

5.3 虚拟机为每个对象定义了一个年龄计数器。每一次gc,计数器+1,当年龄增加到一定的阈值(hotspot默认15),对象将进入老年代。

5.4 动态对象年龄判定
如果survivor中相同年龄的所有对象大小总和大于survivor空间的一半,则大于或等于该年龄的对象可以直接进入老年代,而不需要计数器增加到阈值。

5.5 空间分配担保
在新生代GC之前,虚拟机会检查老年代的最大连续可用空间是否大于新生代的总空间。
如果大于,则新生代GC安全
如果不大于,则虚拟机会判断是否允许担保失败。如果允许,再判断老年代的最大连续空间是否大于历次晋升老年代对象的平均大小。
如果成立,则进行新生代GC
否则进行Full GC
所做的就是尽量避免Full GC

主要介绍垃圾回收算法和垃圾回收器。内存分配策略

原创粉丝点击