JVM之-------GC

来源:互联网 发布:手机淘宝闲鱼在哪里找 编辑:程序博客网 时间:2024/05/29 19:06

1,在java的五个内存区域中,程序计数器、虚拟机栈、本地方法栈三个区域随着线程的创建而产生,随着线程的死亡而消失,即内存的回收自动完成;

但是堆和方法区就不一样了,这部分的内存分配和回收都是动态的;GC要重点负责的就是这部分内存的回收;

 

2,垃圾收集器需要完成的三件事情:

(1)哪些内存需要回收?

(2)什么时候回收?

(3)如何回收?

 

哪些内存需要回收?

也就是判断堆中哪些对象已死?哪些对象还活着?

对于判断对象的生死,常用的算法有:

1)引用计数算法:----没解决对象之间的循环引用问题

   即,为每一个对象添加一个引用计数,当有一个地方引用它时计数器值就加1,当引用失效的时计数器值就减1;所以当任何时刻计数器都是0的对象就是没用的对象,即已经死了的对象。很明显没解决对象之间的循环引用问题,当A对象引用了B对象,B对象又引用了A对象,而实际上AB都是没用的对象的时候,AB因为计数器值不为0而得不到回收。

2)根搜索算法:----java语言使用的算法

基本思想:

通过一系列名为“GC Roots”的对象作为起始点,开始向下搜索(搜索所走过的路径称为引用链),当一个对象到任何“GC Roots”都没有引用链相连的时候(用图论的话来说就是从“GC Roots”到这个对象不可达),则证明这个对象是死了的,将被判定为可回收的对象。

在Java语言里,可作为“GC Roots”的对象包括四种:

(1)虚拟机栈中引用的对象;

(2)方法区中的类静态属性引用的对象;

(3)方法区中的常量引用的对象;

(4)本地方法栈中JNI(即本地方法)引用的对象。


问题:在根搜索算法中不可达的对象,一定会被清除吗?答案是不一定。

      要真正的清除一个对象,至少要经历两次标记过程:

     (1)进行根搜索算法后不可达GC Roots,将进行第一次标记并进行一次筛选:是否有必要执行finalize()方法;

     (2)如果有必要执行,这个对象将会放在一个名为F-Queue的队列中,并在稍后邮虚拟机自动建立的、低优先级的Finalizer线程去执行;

      注意:这里的“执行”只是说虚拟机会触发这个方法,但并发承诺会等待他结束,原因很简单:finalize()方法的不可预知性,如果finalize()方法执行缓慢或者是发生了死循环等情况,F-Queue队列中的其他对象将永远处于等待状态,甚至导致整个内存回收系统崩溃;

       稍后GC将对F-Queue中的对象进行第二次标记,如果对象在finalize()中成功解救了自己(加上一个对自己的引用),那在这次标记时它将被移出“即将回收”的集合中;如果对象在这次标记中还没解救自己,那么将会被清除;

 

注意:任何一个对象的finalize()方法只会被系统执行一次,而且finalize()方法能做的事情,使用try-finally或其他方式都可以实现,不建议使用finalize()方法;

 

 

如何回收?

常用的的垃圾收集算法有:

1)标记-清除算法

   缺点:(1)效率低,标记过程和清除过程的效率都很低;

        (2)空间问题,标记-清除后会产生大量的不连续的内存碎片,可能会导致以后需要分配较大对象时无法找到足够的连续内存而不得不触发另一次垃圾收集动作;

2)复制算法

优点:避免了标记-清除算法的缺点

缺点:(1)将内存缩小为原来的一半(未免太高了点)

     (2)可能需要额外的空间进行分配担保:即当在新生代中没有足够的空间来存放新对象的时候,把新生代中的对象都转移到老年代,把新产生的对象放在新生代;

新生代的回收使用了这个算法,但是做了一些改进:因为新生代的对象一般都是朝生夕死,不需要按11来划分内存,而是把内存划分为一块较大的Eden空间和两块较小的Survivor空间;新产生的对象优先放在Eden区,当Eden区满了,便Minor GC一次,把存活的对象放到一个Survivor区,这个Survivor也满了就再Minor GC一次,把这个Survivor存活的对象,放到另一个Survivor区。

Eden区和Survivor区默认是8:1,所以只浪费10%的内存空间。

 

3)标记-整理算法

复制算法在对象存活率较高的时候要执行较多的复制操作,效率将会很低;更重要的是如果不想浪费50%的内存空间,就需要额外的空间进行分配担保;

标记-整理算法解决了这个问题:它的标记过程和标记-清除算法相同,但是后续的动作不一样:把所有活着的对象移动到一端,然后直接清除掉端为边界以外的内存;

老年代的回收使用了这个算法。

 

4)分代收集算法:当前商业虚拟机采用的算法

根据对象存活期的不同,将内存划分为新生代(存活时间短)和老年代(存活时间长),然后根据各个年代的特点采用最合适的收集算法;

新生代:复制算法,只需要复制少量存活的对象便可完成收集;

老年代:标记—清除 或是 标记—整理算法;

 

 

3,内存分配策略

(1)对象优先在Eden区分配

(2)大对象直接进入老年代:可以通过参数-XXPretensureSizeThreshold指定大于多少的对象直接进入老年代

(3)长期存活的对象将进入老年代

原创粉丝点击