java的内存回收机制GC

Java的内存管理实际上就是对象的管理,其中包括对象的分配和释放。

对于程序员来说,分配对象使用new关键字;
释放对象时,只要将对象所有引用赋值为null,让程序不能够再访问到这个对象,我们 称该对象为"不可达的".GC将负责回收所有"不可达"对象的内存空间。

jvm区域总体分两类:

(1)heap区和非heap区。heap区又分:Eden Space(伊甸园)、Survivor Space(幸存者区)、Tenured Gen(老年代-养老区)。

(2) 非heap区又分:Code Cache(代码缓存区)、Perm Gen(永久代)、Jvm Stack(java虚拟机栈)、Local Method Statck(本地方法栈)。

HotSpot虚拟机GC算法采用分代收集算法

     1)年轻代(Young Gen):年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个Eden Space和2个Suvivor Space(命名为A和B)。当对象在堆创建时,将进入年轻代的Eden Space。垃圾回收器进行垃圾回收时,扫描Eden Space和A Suvivor Space,如果对象仍然存活,则复制到B Suvivor Space,如果B Suvivor Space已经满,则复制到Old Gen。同时,在扫描Suvivor Space时,如果对象已经经过了几次的扫描仍然存活,JVM认为其为一个持久化对象,则将其移到Old Gen。扫描完毕后,JVM将Eden Space和A Suvivor Space清空,然后交换A和B的角色(即下次垃圾回收时会扫描Eden Space和BSuvivor Space。这么做主要是为了减少内存碎片的产生。

      我们可以看到:Young Gen垃圾回收时,采用将存活对象复制到到空的Suvivor Space的方式来确保尽量不存在内存碎片,采用空间换时间的方式来加速内存中不再被持有的对象尽快能够得到回收。 


      2)年老代(Tenured Gen):年老代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁(譬如可能几个小时一次)。年老代主要采用压缩的方式来避免内存碎片(将存活对象移动到内存片的一边,也就是内存整理)。当然,有些垃圾回收器(譬如CMS垃圾回收器)出于效率的原因,可能会不进行压缩。 


      3)持久代(Perm Gen):持久代主要存放类定义、字节码和常量等很少会变更的信息。


分区的目的:新生区由于对象产生的比较多并且大都是朝生夕灭的,所以直接采用标记-清理算法。而养老区生命力很强,则采用复制算法,针对不同情况使用不同算法。

非heap区域中Perm Gen中放着类、方法的定义,jvm Stack区域放着方法参数、局域变量等的引用,方法执行顺序按照栈的先入后出方式。

程序如何与GC进行交互

  Java2增强了 内存管理功能,增加了一个java.lang.ref包,其中定义了三种引用类。这三种引用类分别为SoftReference、 WeakReference和PhantomReference.通过使用这些引用类,程序员可以在一定程度与GC进行交互,以便改善GC的工作效率。这 些引用类的引用强度介于可达对象和不可达对象之间。

  创建一个引用对象也非常容易,例如如果你需要创建一个SoftReference对 象,那么首先创建一个对象,并采用普通引用方式(可达对象);然后再创建一个SoftReference引用该对象;最后将普通引用设置为null.通过 这种方式,这个对象就只有一个SoftReference引用。同时,我们称这个对象为SoftReference对象。

   SoftReference的主要特点是据有较强的引用功能。只有当内存不够的时候,才进行回收这类内存,因此在内存足够的时候,它们通常不被回收。另 外,这些引用对象还能保证在Java抛出OutOfMemory异常之前,被设置为null.它可以用于实现一些常用图片的缓存,实现Cache的功能, 保证最大限度的使用内存而不引起OutOfMemory.以下给出这种引用类型的使用伪代码;

//申请一个图像对象 
Image image=newImage();//创建Image对象 
… 
//使用image 
… 
//使用完了image,将它设置为soft引用类型,并且释放强引用; 
SoftReference sr=new SoftReference(image); 
image=null; 
… 
//下次使用时 
if(sr!=null)  image=sr.get(); 
else{ 
//由于GC由于低内存,已释放image,因此需要重新装载; 
image=newImage(); 
sr=newSoftReference(image); 
}


一些 Java编码时利用GC的建议


  根据GC的工作原理,我们可以通过一些技巧和方式,让GC运行更加有效率,更加符合应用程序的要求。以下就是一些程 序设计的几点建议。

  1. 最基本的建议就是尽早释放无用对象的引用。 大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域 (scope)后,自动设置为null.我们在使用这种方式时候, 必须特别注意一些复杂的对象图,例如数组,队列,树,图等 ,这些对象之间有相互引用关系 较为复杂。对于这类对象,GC回收它们一般效率较低。如果程序允许,尽早将不用的引用对象赋为null.这样可以加速GC的工作。

  2.  尽量少用finalize函数。 finalize函数是Java提供给程序员一个释放对象或资源的机会。但是,它会加大GC的工作量,因此尽量少采用 finalize方式回收资源。
原因有三:

其一,GC为了能够支持finalize函数,要对覆盖这个函数的对象作很多附加的工作。

其二,在finalize运行完成之后,该对象可能变成可达的,GC还要再检查一次该对象是否是可达的。因此,使用 finalize会降低GC的运行性能。

其三,由于GC调用finalize的时间是不确定的,因此通过这种方式释放资源也是不确定的。


  3.如果需要使用经常使用的图片,可以使用soft应用类型。它可以尽可能将图片保存在内存中,供程序 调用,而不引起OutOfMemory.

 
  4.注意集合数据类型,包括数组,树,图,链表等数据结构,这些数据结构对 GC来说,回收更为复杂。另外,注意一些全局的变量,以及一些静态变量。这些变量往往容易引起悬挂对象(danglingreference),造成内存 浪费。

  5.当程序有一定的等待时间,程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定 会执行。使用增量式GC可以缩短Java程序的暂停时间。

你可能感兴趣的:(java)