java垃圾收集器(GC)浅析

2014-11-24 01:19:43 · 作者: · 浏览: 0
了解java的人必然对GC不陌生。GC即垃圾回收器,我们都知道,java语言有自动内存管理的功能,程序员不必手动释放内存。本文将简单介绍垃圾回收器。 GC的任务: 哪些内存需要回收?什么时候回收?如何回收? GC回收哪些内存? 之前我们介绍过了java运行时的数据区的各个部分,包括程序计数器,java 虚拟机栈,本地方法栈,堆,方法区。 其中程序计数器,虚拟机栈,本地方法栈三个区域随线程而生,随线程而灭。栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来就已知的,因此这几个区域的内存分配与回收都具备确定性,在这几个区域中不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟着回收了。 而java堆和方法区则不一样,我们只有在程序处于运行期间才能知道会创建哪些对象,这部分内存的分配与回收都是动态的,GC关注的就是这部分内存。 如何判断对象是否存活(如何标记垃圾)?
GC如果想回收垃圾,必然得先知道哪些对象可以回收。那么GC是如何标记垃圾对象的呢? 在java中,采用的是根搜索算法判定对象是否存活。算法的基本思路就是通过一系列的名为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为“引用链”,当一个对象到GC roots没有任何引用链相连时(GC roots到这个对象不可达),则证明这个对象是不可用的。 可以作为GC roots的对象: 1.虚拟机栈(栈帧中的本地变量表)中引用的对象。 2.方法区中的类的静态属性引用的对象。 3.方法区中常量引用的对象。 4.本地方法中JNI引用的对象。 \
注:引用的类型 引用分为强引用,软引用,弱引用,虚引用< http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPgoKPHN0cm9uZz7Hv9L908M8L3N0cm9uZz6jusDgJiMyMDI4NDuhsE9iamVjdCBvYmogPSBuZXcgT2JqZWN0KCmhsdXi0fm1xNL908O8tMe/0v3Tw6Os1rvT0Me/0v3Tw7Tm1NqjrEdD08DUtrK7u+G72MrVtfSxu9L908O1xLbUz/OhowoKPHN0cm9uZz7I7dL908M8L3N0cm9uZz6jutPD09rD6Mr20rvQqbu509DTw7WrsqK3x7HY0Oi1xLbUz/Oho7bU09rI7dL908O52MGq18W1xLbUz/OjrNTaz7XNs72r0qq3osn6xNq05tLns/bS7LOj1q7HsKOsvau74bDR1eLQqbbUz/PB0Mjru9jK1be2zqfWrtbQsqK9+NDQu9jK1aGjamF2YczhuanBy1NvZnRSZWZlcmVuY2XAtLT6se3I7dL908OhowoKPHN0cm9uZz7I9dL908M8L3N0cm9uZz6jutKy08PT2sPoyva3x7HY0Oi1xLbUz/OjrLWrscjI7dL908O1xMe/tsi4/Mj10rvQqaGjsbvI9dL908O52MGqtcS21M/z1rvE3Mn6tOa1vc/CtM7ArLv4ytW8r7eiyfrWrsewoaO1sc/CtM5HQ9TL0NDKsaOszt7C27Wxx7DE2rTmyse38dfjubujrLa8u+G72MrVtfSxu8j10v3Tw7nYwaq1xLbUz/Oho2phdmHKudPDV2Vha1JlZmVyZW5jZcC0tPqx7cjt0v3Tw6GjCgo8c3Ryb25nPtDp0v3Twzwvc3Ryb25nPqO61+7I9bXE0v3Tw7nYz7Who9K7uPa21M/zyse38dPQ0OnS/dPDtcS05tTao6zN6sirsru74bbUxuTJ+rTmyrG85Lm5s8nTsM/soaPSss7et6jNqLn90OjS/dPDwLTIobXD0ru49rbUz/O1xMq1wP2howoKPGJyPgoKCjxzdHJvbmc+wKy7+MrVvK/L47eoo7o8L3N0cm9uZz4KCrOjvPvArLv4ytW8r8vjt6jT0KO6seq8xy3H5bP9y+O3qKOsseq8xy3V+8Dty+O3qKOsuLTWxsvjt6ijrLfWtPrK1byvy+O3qAoKPHN0cm9uZz6x6rzHLcfls/3L47eoPC9zdHJvbmc+o7rK18/Iseq8x7Smy/nT0NDo0qq72MrVtcS21M/zo6zU2rHqvMfN6rPJuvPNs9K7u9jK1bX0sbux6rzHtcS21M/zoaMKCsixtePKx6O6MT7Qp8LKzsrM4qGjseq8x7rNx+Wz/bn9s8zQp8LKtryyu7jfo7syPrHqvMfH5bP9uvO74bL6yfq088G/tcSyu8Gs0Pi1xMTatObL6caso6y/1bzky+nGrLn9tuC/ycTctbzWwrWxs8zQ8tTa1MvQ0Ln9s8zW0NDo0qq31sXkvc+087bUz/PKsc7et6jV0rW91+O5u7XEwazQ+MTatOa/1bzktviyu7XDsrvM4cewtKW3osHt0ru0zsCsu/jK1byvtq/X96GjCjxpbWcgc3JjPQ=="https://www.cppentry.com/upload_files/article/76/1_nedy2__.jpg" alt="\">
复制算法:将可用空间按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存使用完了,就将还存活这的对象复制到另一块上面,然后再把已使用过的内存空间一次清理干净。这样使得每次都是对其中一块进行内存回收,不会造成很多内存碎片。当然,实际情况中可能并不是1:1等分。但是原来不变。复制算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。
\
标记-整理算法:跟标记-清除的第一步操作一样,也是先标记处垃圾对象,但后续操作不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

分代收集算法:根据对象的存活周期的不同将内存划分为好几块。一般是把java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适合的收集算法。新生代中,每次垃圾手机时都发现大批对象死去,只有少量存活,那就选用复制算法。老年代因为对象存活率高,没有额外控件对它进行分配担保,那就必须使用“标记-清理”或者“标记-整理”算法来进行回收。