何时GC的2种判定与3种GC算法

来源:互联网 发布:简单的编程心形 编辑:程序博客网 时间:2024/06/02 03:57
1.在JDK1.2之前,使用的是引用计数器算法,即当这个类被加载到内存之后,就会产生方法区,堆栈、程序计数
器等一系列信息,当创建对象的时候,为这个对象在堆栈空间中分配对象,同时会产生一个引用计数器,同时引
用计数器+1,当有新的引用时,引用计数器继续+1,而当其中一个引用销毁时,引用计数器-1,当引用计数器减
为0的时候,标志着这个对象已经没有引用了,可以回收了!但是这样会有一个问题:
当我们的代码出现这样的情况时:
a)ObjA.obj=ObjB
b)ObjB.obj=ObjA
这样的代码会产生如下引用情形objA指向objB,而ObjB又指向objA,这样当其他所有的引用都消失了之后,objA
和objB还有一个相互的引用,也就是说两个对象的引用计数器各为1,而实际上这两个对象都已经没有额外的引用,已经是垃圾了。


2.根搜索算法:
根搜索算法是从离散数学中的图论引入的,程序把所有的引用关系看做一张图,从一个节点GC Root开始,寻找对
应的引用节点,找到这个节点之后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节
点则被认为是没有被饮用到的节点,即无用的节点。
目前Java中可作为GC Root的对象有:
1.虚拟机栈中引用的对象(本地变量表)
2.方法区中静态属性引用的对象
3.方法区中常量引用的对象
4.本地方法栈中引用的对象(Native对象)。
java中存在的四种引用
(1)强引用:
只要引用存在,垃圾回收器永远不会回收。
(2)软引用
非必须引用,内存溢出之前进行回收,可以通过以下代码实现
Object obj=new Object();
SoftReference<Object> sf=new SoftRerence<Object>(obj);
obj=null;
sf.get();//有时会返回null

这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然这个对象被标记为需要回收的对象时,

则返回null;

软引用主要用于用户实现类似缓存的功能,在内存不足的情况下直接通过软引用取值,无需从繁忙的真实来源查
询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真实的来源查询这些数据。
(3)弱引用
第二次垃圾回收时回收,可以通过如下代码实现
Object obj=new Object();
WeakReference<Object> wf=new WeakReference<Object>(obj);
obj=null;
wf.get();//有时会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾

弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,

将返回null。弱引用主要用于监控对象是否已经被标记为即将回收的垃圾,可以通过弱引用的isEnQueues方法

返回对象是否被垃圾回收器标记。
(4)虚引用
垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现
Object obj=new Object();
PhantomReference<Object> pf=new PhantomReference<Object>(obj);
obj=null;
pf.get();//永远是返回null 
pf.isEnQueued();//返回从内从中已经删除。
虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null。

对象在内存中会被划分为5块区域,而每块数据的回收比例是不同的(根据统计有):



方法区中主要存放类与类之间关系的数据,而这部分数据加载到内存之后,基本上是不会发生变更的,Java堆中的数据基本上是朝生夕死的,我们用完之后要马上回收的,而Java栈和本地方法栈中的数据,因为有后进先出的原则,当我取下面的数据之前,必须要把栈顶的元素出栈,因此回收率可认为是100%,而程序计数器主要记录程序执行的行号要跳转的地址等一些信息,这块区域也是被认为是唯一一块不会内存溢出的区域。方法区中的数据回收率比较低,而成本又比较高,一般认为“性价比”比较差的,所以SUN自己的虚拟机HotSpot中是不可回收的!但是现在高性能分布式J2EE的系统中,我们大量用到了反射、动态代理等这些类频繁的调用自定义类加载器,都需要动态的加载和卸载了,以保证永久不会溢出,他们通过自定义的类加载器进行了各种操作,因此在实际的应用开发中,类也是经常被加载和卸载的,方法区也是会被回收的!但是方法区中的回收条件非常苛刻,只有同时满足以下三个条件才会被回收:
1.所有实例被回收
2.加载该类的ClassLoader被回收
3.Class对象无法通过任何途径访问(包括 反射)

垃圾回收算法
标记-清除算法(Mark-Sweep)

从根节点开始标记所有可达对象,其余没有标记的即为垃圾对象,执行清除。但回收后的空间是不连续的。


标记-清除算法采用从根集合进行扫描,对存活的对象标记,标记完毕后,在扫描整个空间中未被标记的对象,进
行回收。
标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,
但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片。

复制算法


复制算法采用从根集合扫描,并将存活对象复制到一块新的,没有使用过的空间中,这种算法当控件存活的对象
比较少时,极为高效,但是带来的成本是需要一块内存交换空间进行对象的移动。也就是s0,s1等空间。

标记-整理法


标记-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时,在回收不存活的对象占用的空间后,

会将所有的存活对象网左端空闲空间移动,并更新相应的指针。标记-整理算法是在标记-清除算法的基础上,

又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。


两个最基本的Java垃圾回收算法:复制算法和标记清理算法

常用的算法

         复制算法:两个区域A和B,初始对象在A,继续存活的对象被转到B。此为新生代最为常用的算法

         标记清理:一块区域,标记要回收的对象,然后回收,一定会出现碎片,那么一定会出现碎片,那么引导出标记-整理算法

多了碎片整理,整理出更大的内存方更大的对象。

新生代和年老代

新生代:初始对象,生命周期短的         永久代:长时间存在的对象 

整个Java的垃圾回收是新生代和年老代的协作,这种叫做分代回收

PS:Serial New收集器是针对新生代的收集器,采用的是标记整理

         Parallel New新生代采用复制算法,老年代采用标记整理

         Parallel Scavenge收集器,针对新生代,采用复制收集算法

         Serial Old,新生代采用复制,老年代采用标记清理

Parallel Old,针对老年代,标记整理

CMS收集器,基于标记整理

G1收集器:整体上是基于标记清理,局部采用复制

综上:新生代基本采用复制算法,老年代采用标记整理算法。cms采用标记清理。





0 0
原创粉丝点击