JVM—垃圾回收

来源:互联网 发布:bloodhound js 编辑:程序博客网 时间:2024/05/17 04:40

在网上找到一个讲JVM的ppt,觉得讲的非常好,现将其中讲垃圾回收整理下来。详细的ppt在我的下载空间中

http://download.csdn.net/detail/u011936381/6959809


在开始讲垃圾回收之前,我们先复习下JVM结构中的堆(Heap);


一、堆(Heap)
       Heap是大家最为熟悉的区域,它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。

JVM将Heap分为New Generation和Old Generation(或Tenured Generation)两块来进行管理:


(1)New Generation
        又称为新生代,程序中新建的对象都将分配到新生代中,新生代又由Eden Space和两块Survivor Space构成,可通过-Xmn参数来指定其大小
(2) Old Generation
       又称为旧生代,用于存放程序中经过几次垃圾回收还存活的对象,例如缓存的对象等,旧生代所占用的内存大小即为-Xmx指定的大小减去-Xmn指定的大小。

对堆的解释:
(1)堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的
(2)鉴于上面的原因,Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间,这块空间又称为TLAB(Thread Local Allocation Buffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配
(3)TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效,但这种方法同时也带来了两个问题,一是空间的浪费,二是对象内存的回收上仍然没法做到像Stack那么高效,同时也会增加回收时的资源的消耗,可通过在启动参数上增加-XX:+PrintTLAB来查看TLAB这块的使用情况。


二、垃圾回收(内存回收)(Garbage Collection)

1、概念
JVM中自动的对象内存回收机制称为:GC(Garbage Collection)
GC的基本原理:
为将内存中不再被使用的对象进行回收,GC中用于回收内存中不被使用的对象的方法称为收集器,由于GC需要消耗一些资源和时间的,Java在对对象的生命周期特征进行分析后,在V 1.2以上的版本采用了分代的方式来进行对象的收集,即按照新生代、旧生代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停
(1)对新生代的对象的收集称为minor GC,
(2)对旧生代的对象的收集称为Full GC,
(3)程序中主动调用System.gc()强制执行的GC为Full GC

2、JVM中自动内存回收机制
(1)引用计数收集器

原理:
      引用计数是标识Heap中对象状态最明显的一种方法,引用计数的方法简单来说就是对每一个对象都提供一个关联的引用计数,以此来标识该对象是否被使用,当这个计数为零时,说明这个对象已经不再被使用了。
优点:
      引用计数的好处是可以不用暂停应用,当计数变为零时,即可将此对象的内存空间回收,但它需要给每个对象附加一个关联引用计数
缺点:
      并且引用计数无法解决循环引用的问题,因此JVM并没有采用引用计数。         
(2)跟踪收集器
原理:
      跟踪收集器的方法为停止应用的工作,然后开始跟踪对象,跟踪时从对象根开始沿着引用跟踪,直到检查完所有的对象。
          根对象的来源主要有三种:
                1.被加载的类的常量池中的对象引用
                2.传到本地方法中,没有被本地方法“释放”的对象引用
                3.虚拟机运行时数据区中从垃圾收集器的堆中分配的部分

存在问题:
     跟踪收集器采用的均为扫描的方法,但JVM将Heap分为了新生代和旧生代,在进行minor GC时需要扫描是否有旧生代引用了新生代中的对象,但又不可能每次minor GC都扫描整个旧生代中的对象,因此JVM采用了一种称为卡片标记(Card Marking)的算法来避免这种现象。
(3)卡片标记算法
卡片标记的算法为将旧生代以某个大小(例如512字节)进行划分,划分出来的每个区域称为卡片,JVM采用卡表维护卡的状态,每张卡片在卡表中占用一个字节的标识(有些JVM实现可能会不同),当Java代码执行过程中发现旧生代的对象引用或释放了对于新生代对象的引用时,就相应的修改卡表中卡的状态,每次Minor GC只需扫描卡表中标识为脏状态的卡中的对象即可,图示如下:





 1、跟踪收集器在扫描时最重要的是要根据这些对象是否被引用来标识其状态

 2、JVM中将对象的引用分为了四种类型,不同的对象引用类型会造成GC采用不同的方法进行回收:
   (1)强引用:默认情况下,对象采用的均为强引用
           (这个对象的实例没有其他对象引用,GC时才会被回收)
   (2)软引用:软引用是Java中提供的一种比较适合于缓存场景的应用
           (只有在内存不够用的情况下才会被GC)
   (3)弱引用:在GC时一定会被GC回收
   (4)虚引用:由于虚引用只是用来得知对象是否被GC



垃圾回收的时机:

1、Java技术提供了一个系统级的线程(Thread),即垃圾收集器线程(Garbage Collection Thread),来跟踪每一块分配出去的内存空间,当Java 虚拟机(Java Virtual Machine)处于空闲循环时,垃圾收集器线程会自动检查每一快分配出去的内存空间,然后自动回收每一快可以回收的无用的内存块。

2、在Jvm内存满时触发一次回收

3、调用System.gc()



1 0