理解GC-GC对spark的影响

什么是GC

  

垃圾收集 Garbage Collection 通常被称为“GC”,回收没用的对象以释放空间。

       GC 主要回收的是虚拟机堆内存的空间,因为new 的对象主要是在堆内存。

垃圾收集的算法

1)标记 -清除算法

标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。

    它的主要缺点有两个:一个是效率问题,标记和清除过程的效率都不高;另外一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

理解GC-GC对spark的影响

2)复制算法

 “复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

     这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,缺点:这种算法持续复制长生存期的对象则导致效率降低。

理解GC-GC对spark的影响

 3)标记-整理算法

复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。

    根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存;

4)分代收集算法

GC分代的基本假设:绝大部分对象的生命周期都非常短暂,存活时间短。

    “分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

理解GC-GC对spark的影响

JVM的minor gc与full gc

理解GC-GC对spark的影响

其中:

        年轻代:存放岁数比较年轻的对象。分为 Eden区 和 Survivor区。

                Eden区:开始对象分配的地方;

                Survivor区:是经过minor gc 后存活对象的存储区域,一般这个区域要比Eden区小。分为两个区:from 和 to。

        老年代:存放存活时间长的对象和年轻代存不下的对象,这个区域要比年轻代大的多。

1)对象分配

        一般对象分配:当有新对象产生时,JVM会把对象分配到Eden区,Survivor区作为备用;

        大对象分配:大对象是指需要连续空间的java对象,如很长的数组、字符串。这样的大对象不会分配在年轻代,直接进入老年代。

2)minor gc(年轻代 gc)

        minor gc 是指 年轻代的垃圾收集动作。因为年轻代中的对象基本都是朝生夕死的(80%以上),所以Minor gc 会非常频繁,回收速度也比较快。

        触发条件:Eden区满了。

        操作方法:

            1)第一次minor gc,通过复制算法,将Eden区存活的对象复制到 Survivor from 区,对象年龄设为1;

            2)以后的minor gc,通过复制算法,将Eden区 和 Survivor from 区 存活的对象 复制到 Survivor to 区,然后 再将 to 区 变成新的 from 区,同时对象的年龄+1,一旦某个对象达到了指定的次数,就会把该对象移到老年代。

3)full gc(老年代 gc)

        full gc 是指 老年代的垃圾收集动作。因为老年代中的对象大多是存活时间长的对象,老年代回收要用 标记整理或标记清除算法,回收速度很慢。

       触发条件:老年代满了。

        操作方法:

            通过标记整理或标记清除算法,扫描老年代的每个对象,并回收不可达的对象。

频繁GC的影响及优化方法

   1)频繁GC的影响

       task运行期间动态创建的对象使用的Jvm堆内存的情况

         理解GC-GC对spark的影响

 当给spark任务分配的内存少了,会频繁发生minor gc,如果存活时间长的对象特别多,就会发生full gc。

        当频繁的new对象时,导致很快进入老年代,这样也可能发生full gc。

        频繁gc 会影响 工作任务线程的正常执行,从而降低spark 应用程序的性能。

2)优化方案

    a)优化代码,避免频繁new 同一个对象,导致的频繁gc。

    b)调节可用存储内存和执行内存的比例,以减少gc 发生的频率。

    c)对应存储内存,可以考虑存储序列化后的对象,调节序列化级别为MEMORY_DISK_SER或MEMORY_ONLY_SER,这样占用内存空间小。

    d)还可以使用Kryo序列化类库,进行序列化,因为kryo序列化方法可以进一步的降低RDD的parition的内存占用量。