JVM 学习笔记 III --- GC

来源:互联网 发布:网络神偷破解安卓版 编辑:程序博客网 时间:2024/05/21 06:50

javapapers
Java garbage collection is an automatic process to manage the runtime memory used by programs. By doing it automatic JVM relieves the programmer of the overhead of assigning and freeing up memory resources in a program.
java 与 C语言相比的一个优势是,可以通过自己的JVM自动分配和回收内存空间。


Java相比于c/c++,程序员不需要考虑内存的分配与回收,系统为我们简化了很多工作。但同时,不当的使用还是可能导致内存泄露,并且由于回收机制的局限性,总可能存在各种各样的漏洞。要了解垃圾回收,先得理解Java的内存分配机制。


无论是普通类型的变量还是引用类型的变量(实例)都可以作为局部变量,保存在栈中。普通变量保存的是它的值,而引用变量保存的是指向堆区的指针,通过指针能找到相应的对象。

普通类型也就是Java基本类型:byte、short、char、int、long、boolean。注意区分它们的包装类不是基本类型,是类。

从语法上看,所有用new关键字初始化的变量都是引用类型,存储在堆区。

数组也是引用类型。

两种类型另一个重要的区别是,在作为参数时,基本类型遵循“值拷贝”,而引用类型则是“引用拷贝”在方法中变动会直接改变变量值。


垃圾

Java中无法引用的对象就是垃圾,之前说了,引用变量指向堆中的一个对象,如果引用无法“找到”该对象,则该对象无法被使用,处于“废弃”状态。Java中的垃圾指的是堆中的废弃对象。


先来看下堆的组成

这里写图片描述

堆被分为新生代、年长代和永恒代。新生代分为Eden(伊甸园,所有对象刚new出来都在这个区,直到第一次GC)和Survivor区,Survivor分为FromSpace和ToSpace。


垃圾的产生

例如: (1)改变对象的引用,如置为null或者指向其他对象。    Object x=new Object();//object1    Object y=new Object();//object2    x=y;//object1 变为垃圾    x=y=null;//object2 变为垃圾 (2)超出作用域    if(i==0){       Object x=new Object();//object1    }//括号结束后object1将无法被引用,变为垃圾 (3)类嵌套导致未完全释放    class A{       A a;    }    A x= new A();//分配一个空间    x.a= new A();//又分配了一个空间    x=null;//将会产生两个垃圾 (4)线程中的垃圾    class A implements Runnable{        void run(){        //....      }    }    //main    A x=new A();//object1    x.start();    x=null;//等线程执行完后object1才被认定为垃圾(5)循环引用    A o1 = new A();    A o2 = new A();    o1.o = o2;    o2.o = o1;    o1 = null;    o2 = null;//两对象都不可达,但仍有互相引用关系class A{    public Object o = null;}

垃圾回收算法

在确定了哪些垃圾可以被回收后,垃圾收集器要做的事情就是开始进行垃圾回收,但是这里面涉及到一个问题是:如何高效地进行垃圾回收。由于Java虚拟机规范并没有对如何实现垃圾收集器做出明确的规定,因此各个厂商的虚拟机可以采用不同的方式来实现垃圾收集器,所以在此只讨论几种常见的垃圾收集算法的核心思想。

1.Mark-Sweep(标记-清除)算法

这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。具体过程如下图所示:

这里写图片描述

从图中可以很容易看出标记-清除算法实现起来比较容易,但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。

2.Copying(复制)算法

为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。具体过程如下图所示:

这里写图片描述

这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。

很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。

3.Mark-Compact(标记-整理)算法

为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。具体过程如下图所示:

这里写图片描述

4.Generational Collection(分代收集)算法

分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。如上图堆的构成,年长代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。

目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。

而由于年长代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。

永恒代,用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。


参考:
Java垃圾回收机制
JVM 的 工作原理,层次结构 以及 GC工作原理

0 0
原创粉丝点击