GC垃圾分代回收机制及算法
在java语言中java内存运行时区的各个部分程序计数器、虚拟机栈、本地方法栈,随线程而生、线程而灭,内存的分配与回收都具有确定性java堆和方法区则不同,一个接口中多个实现类需要的内存不同,一个方法区需要的内存也可能不同,线程只有在运行时才知道创建了那些对象,这部分内存回收创建都是动态的。判断哪些内存需要回收 方法:1、引用计数算法2、可达性分析算法垃圾收集算法:1、标记-清除算法...
在java语言中java内存运行时区的各个部分
程序计数器、虚拟机栈、本地方法栈,随线程而生、线程而灭,内存的分配与回收都具有确定性
java堆和方法区则不同,一个接口中多个实现类需要的内存不同,一个方法区需要的内存也可能不同,线程只有在运行时才知道创建了那些对象,这部分内存回收创建都是动态的。
判断哪些内存需要回收 方法:
1、引用计数算法
给对象加一个计数器,每当一个地方引用时,计数器+1,引用失效时计算器数值-1,计数器为0的对象不能再使用,实现简单效率高
缺点:不能解决对象的循环引用问题,可能造成内存的泄露。 内存泄露及内存溢出
2、可达性分析算法
通过一系列的“GC Roots”对象作为起点,从这些节点向下搜索,搜索所走过的路径为引用链(Reference Chain),当一个对象到GC Roots没有任何链连接,即GCRoots到对象不可达,对象不可用,判定为可回收对象。
在java中可作为GCRoots的对象包括
1>.虚拟机栈中引用的对象
2>.方法区中类静态属性引用的对象
3>,方法区中常量引用的对象
垃圾收集算法:
1、标记-清除算法(Mark-Sweep)
最基础的算法(后续的都基于此进行改进)。标记处所有需要回收的对象,标记完成后统一回收所被标识的对象
不足:1>、效率问题:标记,清除两个效率都不高
2>、空间问题:标记清除后会产生大量的不连续的内存碎片,空间碎片太多会导致以后程序在运行过程中需要分配较大对象时,无法找到足够连续的内存,而不得不提前触发另一次垃圾收集操作。
2、复制算法
为解决效率问题,复制算法出现,将容量按内存划分为两个大小相等的两块,每次只能使用其中一块,当使用的这一块用完后,将存活的对象复制到另一个上面,在把已经使用的内存空间清掉,使得每次都是对半区进行回收。 代价每次只能使用一半的内存空间,未免太高。
现代商业都采用这种方法进行回收新生代,新生代存活几率小,一般会将内存分为较大一块Eden空间和两块较小的survivor空间,每次使用Eden和一块survivor区,回收时将Eden和survivor一次性复制到另一块survivor上,最后清理掉Eden和survivor,默认内存分配为8:1:1。只有10%会被浪费,当survivor内存不够时,需要依赖其他内存(老生代)进行担保。指的是survivor内存不够放新生代收集下来存活的对象,这些对象直接进入老生代,对象进入老生带的另一个触发条件,一个对象在两个survivor中来回移动15次
3、标记整理算法
复制收集算法在对象存活率很高的情况下,进行较多的复制操作,效率会变得很低,若不想浪费50%的内存需要分配内存担保,应对100%存活的条件,所以根据老年代,提出了标记整理算法。过程同标记-清除类似,但后续不是直接对可回收对象进行清理,而是将存活对象都向一端移动,清理掉边界以外的内存。
4、分代收集算法
根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。
更多推荐
所有评论(0)