您的位置:首页 > 编程语言 > Java开发

Java GC基本算法

2016-04-11 17:43 399 查看
1、JVM内存组成结构

JVM内存结构由堆、栈、本地方法栈、方法区等部分组成,结构图如下所示:



1)堆

所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。堆被划分为新生代和旧生代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由FromSpace和ToSpace组成,结构图如下所示:



新生代。新建的对象都是用新生代分配内存,Eden空间不足的时候,会把存活的对象转移到Survivor中,新生代大小可以由-Xmn来控制,也可以用-XX:SurvivorRatio来控制Eden和Survivor的比例。

旧生代用于存放新生代中经过多次垃圾回收(也即Minor GC)仍然存活的对象

2)栈

每个线程执行每个方法的时候都会在栈中申请一个栈帧,每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的临时变量、参数和中间结果

3)本地方法栈

用于支持native方法的执行,存储了每个native方法调用的状态

4)方法区

存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用持久代(PermanetGeneration)来存放方法区,可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值。介绍完了JVM内存组成结构,下面我们再来看一下JVM垃圾回收机制。

2、Java GC基本算法

2.1、引用计数(reference counting)

原理:此对象有一个引用,则+1;删除一个引用,则-1。只用收集计数为0的对象。

缺点:
(1)无法处理循环引用的问题。如:对象A和B分别有字段b、a,令A.b=B和B.a=A,除此之外这2个对象再无任何引用,那实际上这2个对象已经不可能再被访问,但是引用计数算法却无法回收他们。 (2)引用计数的方法需要编译器的配合。编译器需要为此对象生成额外的代码。如赋值函数将此对象赋值给一个引用时,需要增加此对象的引用计数。还有就是,当一个引用变量的生命周期结束时,需要更新此对象的引用计数器。

引用计数的方法由于存在显著的缺点,实际上并未被JVM所使用。

2.2、复制(copying)

原理:把内存空间划分为2个相等的区域,每次只使用一个区域。垃圾回收时,遍历当前使用区域,把正在使用的对象复制到另外一个区域。

优点:不会出现碎片问题。

缺点:

(1)暂停整个应用。

(2)需要2倍的内存空间。

2.3、标记-清扫(Mark-and-sweep)

原理:对于“活”的对象,一定可以追溯到其存活在堆栈、静态存储区之中的引用。这个引用链条可能会穿过数个对象层次,算法基于有向图,采用深度优先搜索。

第一阶段:从GC roots开始遍历所有的引用,对有活的对象进行标记。

第二阶段:对堆进行遍历,把未标记的对象进行清除。

优点:解决了循环引用的问题。

缺点:

(1)暂停整个应用;

(2)会产生内存碎片。

(3)不管你这个对象是不是可达的,即是不是垃圾,都要在清楚阶段被检查一遍,非常耗时.

sun前期版本就是用这个技术。

2.4、标记-压缩(Mark-Compact)

第一阶段:同标记-清扫,标记活的对象,

第二阶段:这个阶段将所有做了标记的活动对象整理到堆的底部

优点:

(1)避免标记扫描的碎片问题;

(2)避免停止复制的空间问题。

2.5、分代(generational collecting)

原理:基于对象生命周期分析得出的垃圾回收算法。把对象分为年轻代、年老代、持久代,对不同的生命周期使用不同的算法(2-3方法中的一个即4自适应)进行回收。

J2SE1.2以后使用此算法

JVM分别对新生代和旧生代采用不同的垃圾回收机制

2.5.1、新生代的GC(Minor GC):

指发生在新生代的垃圾收集动作,因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。

新生代通常存活时间较短,因此基于Copying算法来进行回收,所谓Copying算法就是扫描出存活的对象,并复制到一块新的完全未使用的空间中,对应于新生代,就是在Eden和FromSpace或ToSpace之间copy。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从eden到survivor,最后到旧生代,

2.5.2、旧生代的GC(Major GC / Full GC):

指发生在老年代的 GC。旧生代与新生代不同,对象存活的时间比较长,比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减少内存碎片带来的效率损耗。

MajorGC 的速度一般会比 Minor GC 慢 10倍以上。

Thinking in java给java gc取了一个罗嗦的称呼:“自适应、分代的、停止-复制、标记-扫描”式的垃圾回收器。

导致Gc的情况:

1、tenured被写满

2、perm被写满

3、System.gc()的显式调用。

4、上一次GC之后heap的各域分配策略动态变化。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: