详解Java垃圾收集算法

上一篇 / 下一篇  2009-09-03 16:16:49 / 个人分类:JAVA

:o#m9q AKJF!d2t01.垃圾收集算法的核心思想51Testing软件测试网G.w~;h]VG

dB;a d1x!G0    Java语言建立了垃圾收集机制,用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。该机制可以有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引发的内存耗尽,以及不恰当的内存释放所造成的内存非法引用。

e,S.VVV D-n5Pz051Testing软件测试网+nc+bK#x

 

1_8?V5Af-f2S0

,Hur+J)S+`0     垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能,因此需要开发人员做比较深入的了解。

zJ"T:ctj$v051Testing软件测试网Og8\9Nupi5J{

 

4h&z(X m"B!E0

-wy9BK]k,@4NC02.触发主GC(Garbage Collector)的条件51Testing软件测试网-itA*Qik

e1f#^'{ncv0oO!?0 

F k3Z` w&_|_051Testing软件测试网a W~1o;Tg/v

  JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:

}TU.S&T ?Mj0

MD;w AP5\/L[*c0 

pv ^\h$A051Testing软件测试网)J`5r`*A'X

      ①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。

*h)ekSBO0

.^1b(k/z C4\0 51Testing软件测试网g)_)o(_} h

1u!oQ4f+ljwC0  ②Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。51Testing软件测试网 T,EyuD | F2[

[%B.@6yz hD V0 51Testing软件测试网|nYV&y*D7oF

51Testing软件测试网/J)]'GF P N-]

       由于是否进行主GC由JVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的。51Testing软件测试网?u/zSf;SJ:V3M

I~+yD#pN8P'W0a0 

Di!MiM |4Y%aK051Testing软件测试网p1iv!Q'GbY*D

3.减少GC开销的措施

PM|;U,wB051Testing软件测试网:|G{$]DJ

 

dx.X6Xf0

Q^ [J'B^0  根据上述GC的机制,程序的运行会直接影响系统环境的变化,从而影响GC的触发。若不针对GC的特点进行设计和编码,就会出现内存驻留等一系列负面影响。为了避免这些影响,基本的原则就是尽可能地减少垃圾和减少GC过程中的开销。具体措施包括以下几个方面:

XI a%xu6J4YbrC051Testing软件测试网t9nU y_ V} l

 51Testing软件测试网kE;G$d/P4_ D k

51Testing软件测试网 `1^\ tx6rG{:{

    (1)不要显式调用System.gc()51Testing软件测试网;~y F5kQ|'??R

51Testing软件测试网)C(rSI|S@&I d

此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。51Testing软件测试网*NZUR7g^0v]

k"o P c'mofH9oR_0 

3itA kTW051Testing软件测试网+gH~n R,d

     (2)尽量减少临时对象的使用

$M5e!Q;|*G ~0BIj051Testing软件测试网.b~B\gK2S.ZKz

  临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。51Testing软件测试网)|.zM jl-r:{

51Testing软件测试网+TsxU{Z(?*]B

 

L cp `$w hmX:pv051Testing软件测试网 B#CT_R z"P

   (3)对象不用时最好显式置为Null

U&@9[ \+|9`;z8w.K}s.o051Testing软件测试网B"pC*bx

  一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。51Testing软件测试网,@F sKY J y6[z Z'E9J

51Testing软件测试网h!NI)z1~ vx&T[

 

[2T4D7V^051Testing软件测试网L`!FGB1K| Ix

     (4)尽量使用StringBuffer,而不用String来累加字符串(详见blog另一篇文章JAVA中String与StringBuffer)51Testing软件测试网E*j(?b.{O H

51Testing软件测试网`VV\"x#@8UE%I^

  由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象。51Testing软件测试网 L.w*cY'{.i1T

2j4t+L-Uw"N/VD0 51Testing软件测试网7PwU&~B$o`D

51Testing软件测试网"bL+A/e^!i?

     (5)能用基本类型如Int,Long,就不用Integer,Long对象51Testing软件测试网y%\Wz9e;[ej

} Yz[ p1mto H!F0      基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。

K_qE2f)V$v0

eQHdG:R{F0 

(iX ~ Jx6Cv)Jm$~z0

-dh P J FFG)u0     (6)尽量少用静态对象变量

1ba?0NoR0

+q/lG q*d!j,Do }0  静态变量属于全局变量,不会被GC回收,它们会一直占用内存。

| D:Z-{w#d+|0

FuiU7k,L.e l V0 

/eJ&J f i9\F*f X0

{rGqb_1K0   (7)分散对象创建或删除的时间

If'B;\+c)w0

M6fKB1lo+DX0      集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。

X5VW-GE(d051Testing软件测试网9`)E[E4g5L?Z

 

K {J8xh`051Testing软件测试网7B5fV9P#|0`7u'C

    4.gc与finalize方法51Testing软件测试网Ug aV~3u4AZ

d+[~ S%R0     ⑴gc方法请求垃圾回收

x?3M8U m$D051Testing软件测试网 Fip,dD/Fq dd6R N

      使用System.gc()可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java的垃圾回收。需要注意的是,调用System.gc()也仅仅是一个请求。JVM接受这个消息后,并不是立即做垃圾回收,而只是对几个垃圾回收算法做了加权,使垃圾回收操作容易发生,或提早发生,或回收较多而已。51Testing软件测试网c poE ~0c)OP"n

%a u)XBZd&|0 

)E/zK[+l%i e-|051Testing软件测试网5HB7E:O-@

  ⑵finalize方法透视垃圾收集器的运行51Testing软件测试网&ffL["G(v"R

51Testing软件测试网 VP,w"ko

  在JVM垃圾收集器收集一个对象之前 ,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了缺省机制来终止化该对象释放资源,这个方法就是finalize()。它的原型为:51Testing软件测试网o$A \(muti

51Testing软件测试网$t Y-j{"QjcNw|u'I;N

  protected void finalize() throws Throwable51Testing软件测试网;}v k%{:hh jtC

51Testing软件测试网4O8?/](u\0X#O

 

,om&^ v:a0

3`mw#lx+KO0  在finalize()方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。

!D^ L7J)k051Testing软件测试网)p'gNX ^%Z

   

I"j-r?f&Vf051Testing软件测试网Ocl8V!o5`d6m

    因此,当对象即将被销毁时,有时需要做一些善后工作。可以把这些操作写在finalize()方法里。

J6a,i'k(P?Dnx S0

#K5g5m/fl0 51Testing软件测试网&H%I%O9H"L

51Testing软件测试网aE6L7fp6xnr;Z

      以下是引用片段:51Testing软件测试网#D*p5Hf8p

I]L:i6wn-](]:o9M0 

;y;{\2H5D7j2BY?.u051Testing软件测试网,vK` LV6nTV

  protected void finalize(){

^ b^l n:B ZK%c051Testing软件测试网2b:}XIe4~ U;D

  // finalization code here
s/`H8[wx6O0      }51Testing软件测试网8xl6}?G4\%A;uF

mm3AXy0 

wc3Pt][ NhMvl0

l;m2FNT.U0    ⑶代码示例51Testing软件测试网n)A?*xD)j8G

Vf:D#q'o(n(hRJ~0  以下是引用片段:

sg{5{U,l0

W-U~3p1bd m0  class Garbage {51Testing软件测试网 y.i;E I&?/uK
      int index;51Testing软件测试网'P'd"e9?gx U

4C.kBa;["Nc0  static int count;51Testing软件测试网a'{n!{7R

'R#A4X'Eq0  Garbage() {51Testing软件测试网*\@X-VS5[|1g{1X

*z*wk T]'qi0  count++;
Z:FW&qNr8E%aW%L'o0      System.out.println("object "+count+" construct");51Testing软件测试网Z$H r;y$cp

51Testing软件测试网Huz6GAt1Y

  setID(count);
];K8O(^sfMC%vk7~6RB0    }51Testing软件测试网KhNB.xn&q
     void setID(int id) {
"D I/_S Y3g0  index=id;
E0cL2k@4p0      }51Testing软件测试网n7MfJ'M
     protected void finalize() //重写finalize方法51Testing软件测试网dJF.AFZgz6k:?h
     {
h e5n6Lagtyo@O0    System.out.println("object "+index+" is reclaimed");51Testing软件测试网:v S4TwM4s(L

51Testing软件测试网xzRgp

   }
"xL-S%b7\ ]0    public static void main(String[] args)  {51Testing软件测试网 rR0`}4n7ZTMs
      new Garbage();51Testing软件测试网R;k{s$e i1w

W1`8n&?&\ hy0  new Garbage();51Testing软件测试网/`;o u5n4W'\
  new Garbage();

-D*T{k:OJG h0

'H*t@E)d2[ e0  new Garbage();
$O{S0E9f)Sp ~1?8d0      System.gc(); //请求运行垃圾收集器51Testing软件测试网D^0`{w&K4n(bk9yE
     }51Testing软件测试网1B#J?1fK,U+]4YT

51Testing软件测试网\~Q fb2gfu

     }

p3MBog$p,a0

n LutjT0vz0 51Testing软件测试网]n(x1k/H,?:p,u"h"u

51Testing软件测试网y;OXX*c

5.Java 内存泄漏51Testing软件测试网7x+Qc D7m3Yt,K

51Testing软件测试网:}VRDS-E

 51Testing软件测试网.B d$Z7svX7EKJ

51Testing软件测试网3TQ D(^dY

  由于采用了垃圾回收机制,任何不可达对象(对象不再被引用)都可以由垃圾收集线程回收。因此通常说的Java 内存泄漏其实是指无意识的、非故意的对象引用,或者无意识的对象保持。无意识的对象引用是指代码的开发人员本来已经对对象使用完毕,却因为编码的错误而意外地保存了对该对象的引用(这个引用的存在并不是编码人员的主观意愿),从而使得该对象一直无法被垃圾回收器回收掉,这种本来以为可以释放掉的却最终未能被释放的空间可以认为是被“泄漏了”。

b#w{-q(l0

"O_u}/P z_0 51Testing软件测试网 Z/v%qo!V0S

gh:f[D*z1?$M0  考虑下面的程序,在ObjStack类中,使用push和pop方法来管理堆栈中的对象。两个方法中的索引(index)用于指示堆栈中下一个可用位置。push方法存储对新对象的引用并增加索引值,而pop方法减小索引值并返回堆栈最上面的元素。在main方法中,创建了容量为64的栈,并64次调用push方法向它添加对象,此时index的值为64,随后又32次调用pop方法,则index的值变为32,出栈意味着在堆栈中的空间应该被收集。但事实上,pop方法只是减小了索引值,堆栈仍然保持着对那些对象的引用。故32个无用对象不会被GC回收,造成了内存渗漏。51Testing软件测试网%wy~X.`Y

2AK,K1y5I6@0 51Testing软件测试网^uZX.u6}O

51Testing软件测试网!}8Tj/wh {2e0GK

  以下是引用片段:51Testing软件测试网;k [9IFxKi4r Z

51Testing软件测试网_+]}y:}BV2ENM

public class ObjStack {
U!r4[j}$Ofr3{0
   private Object[] stack;

y:d Q*oA&A0

drK0^r@%M Q0   private int index;51Testing软件测试网/Xb1ML"p%A.~b

2Y#By @m0   ObjStack(int indexcount) {

O s)gXvJ`L051Testing软件测试网)AC_:n2X&QXl

   stack = new Object[indexcount];
o1q'j+|`#x+mk&u y"b0   index = 0;
51Testing软件测试网:b a'Y+hT

51Testing软件测试网;JC"JO b h*ou

   }

1}0{2O|i0

}7a f@Y)R%h0 

&E&LKrZT0y-YA0

{T`/mpg?0   public void push(Object obj) {
,Ig ]#EV*?Wap Q:o0   stack[index] = obj;51Testing软件测试网j3I2hHp+VH!zM
   index++;
51Testing软件测试网Ip)kZT7`X

!Ce b8M |&N/Pc0   }51Testing软件测试网9s5p4gr} G

51Testing软件测试网k[e6Wti5l

 

L2o;y K#[6dZU051Testing软件测试网 \;IcD4[0[

   public Object pop() {51Testing软件测试网e+WN.GhT
    index--;
51Testing软件测试网FEd7[H

)p*u,?Amn0    return stack[index];

rC j!L/d2p1p$@051Testing软件测试网W_4Lpb4_%A}3]

   }51Testing软件测试网q#k?7az:F Ha~

Uam0k9J0   }

N?-@7M#]9N A;{0

$vVh6A5S2H0 51Testing软件测试网 sy+`r,~

%S}"f4G}0    public class Pushpop {

9V'n r f0Y(h'V/wD051Testing软件测试网G*M.UiwH^7q

       public static void main(String[] args) {

:P3[a1qg0mL0

z(qnT'd0         int i = 0;

UW,n!Vx:@#}"K^9t051Testing软件测试网1O^p?{&i!R0k

         Object tempobj;

Z[ O(Bd051Testing软件测试网jT1o4q)K

         ObjStack stack1 = new ObjStack(64);51Testing软件测试网y~Q)GLo9T+[

@,k!z+Y*]/p#a0         /**  new一个ObjStack对象,并调用有参构造函数。分配stack Obj数组的51Testing软件测试网+eo%]S})N5q

l;Bps S]/XH0           *   空间大小为64,可以存64个对象,从0开始存储。
rPc"BN-U9fR0          */

Z+x9iDx+U051Testing软件测试网+_.oxC6{ ]\Y

 51Testing软件测试网 q fs*H$X \4I

(~q7e8l Go/Up0       while (i < 64)51Testing软件测试网Y~"NlJ.\

5iO(s!aE2j0         {   tempobj = new Object();

#](G[4[Q)S0

DS-m$TCl-zO@0              //循环new Obj对象,把每次循环的对象一一存放在stack Obj数组中。
J G)kJF Z8Zu }0             stack1.push(tempobj);

ir'K4g p7Wr051Testing软件测试网P1MC o5q~0XJ3A S\ T

             i++;

)gft#`"f0

.eX? @^&P6r;H#S [0             System.out.println("第" + i + "次进栈" + "t");51Testing软件测试网3M+^3j!\H q*Q Dl(Q

k)X~ ]MJ0          }51Testing软件测试网.bGmaaQDi W1b

`lQ@Y8`] ^i0 

S;|/cy f/s:Wq0

ObrO3y[0          while (i > 32)

0e9{q3F gW Uy0

:B(X4oX??(_,o S$p H0         {51Testing软件测试网1i-Dy5K*I;u5D

51Testing软件测试网2F8ZT2TRv

             tempobj = stack1.pop();//这里造成了空间的浪费。51Testing软件测试网K3gIv$}%b

4}8Bh"uge0             /**正确的pop方法可改成如下所指示,当引用被返回后,51Testing软件测试网i8T0ObMIP%B
                *堆栈删除对他们的引用,因此垃圾收集器在以后可以回收他们

OL4sO;XpO0

H/q&P ZCI0             */
9H(N[+M+}8p5f0Q j/c0             /**
51Testing软件测试网7d^^6MT

51Testing软件测试网c RL3Gt5}T

               * public Object pop() {

*?4V^-QN051Testing软件测试网 e6\Z&BfE:G"r

               *   index - -;

W pv3dw3tRi K051Testing软件测试网vv6?H"t5k

               *   Object temp = stack [index];
'yVOL#QK4G:XQ[/s*u0               *   stack [index]=null;return temp;
51Testing软件测试网h2LW1kt/Emu

51Testing软件测试网n(Gu%Y%T

               *   }

v&Fd8x3J-sIt3V&?051Testing软件测试网%z BQ|;e.k%h

             */
N2r%er3L&z0               i--;51Testing软件测试网k+j-X&Z9CtAS+[
               System.out.println("第" + (64 - i) + "次出栈" + "t");
51Testing软件测试网h!e+H4Ld:E

51Testing软件测试网&wN8J,~J+b$irh

              }

*uqt F|0

d{*j0rw l?R C0            }51Testing软件测试网M3kV-_$je` I

51Testing软件测试网wj4{KWk pt

          }51Testing软件测试网$XY6F9A]({CGU

`:k.Z xe051Testing软件测试网/k` BY u
6.如何消除内存泄漏

S0KmQ:S@z'D051Testing软件测试网g0w5] G.q

 

u7I4P y.@ p^0

&\(QP w3A4u:c0     虽然Java虚拟机(JVM)及其垃圾收集器(garbage collector,GC)负责管理大多数的内存任务,Java软件程序中还是有可能出现内存泄漏。实际上,这在大型项目中是一个常见的问题。避免内存泄漏的第一步是要弄清楚它是如何发生的。本文介绍了编写Java代码的一些常见的内存泄漏陷阱,以及编写不泄漏代码的一些最佳实践。一旦发生了内存泄漏,要指出造成泄漏的代码是非常困难的。因此本文还介绍了一种新工具,用来诊断泄漏并指出根本原因。该工具的开销非常小,因此可以使用它来寻找处于生产中的系统的内存泄漏。

'IRR!k)t qG\"nL0

.bIy(h;e P1zKs,~0 

Jn.\ ]vx051Testing软件测试网JI%cx G"AJZ.S+g@

      垃圾收集器的作用51Testing软件测试网k$i:Z(`1a;^Z*y9w

no(g?b?0    

"vinc!Y g#\/_lf0

X"u2T2e1?B c/V0     虽然垃圾收集器处理了大多数内存管理问题,从而使编程人员的生活变得更轻松了,但是编程人员还是可能犯错而导致出现内存问题。简单地说,GC循环地跟踪所有来自“根”对象(堆栈对象、静态对象、JNI句柄指向的对象,诸如此类)的引用,并将所有它所能到达的对象标记为活动的。程序只可以操纵这些对象;其他的对象都被删除了。因为GC使程序不可能到达已被删除的对象,这么做就是安全的。51Testing软件测试网$B,RGa8y{2P#CH

GN ?!rR-?4PT"p PS0 51Testing软件测试网#C#S a9c{

51Testing软件测试网J0m)Jg-af-h(V

      虽然内存管理可以说是自动化的,但是这并不能使编程人员免受思考内存管理问题之苦。例如,分配(以及释放)内存总会有开销,虽然这种开销对编程人员来说是不可见的。创建了太多对象的程序将会比完成同样的功能而创建的对象却比较少的程序更慢一些(在其他条件相同的情况下)。

"^/BQS` m3u z051Testing软件测试网E6kO1lr

 51Testing软件测试网;w2X\(J.E4c$a;g!~

F2u"M?!O!e TS0      而且,与本文更为密切相关的是,如果忘记“释放”先前分配的内存,就可能造成内存泄漏。如果程序保留对永远不再使用的对象的引用,这些对象将会占用并耗尽内存,这是因为自动化的垃圾收集器无法证明这些对象将不再使用。正如我们先前所说的,如果存在一个对对象的引用,对象就被定义为活动的,因此不能删除。为了确保能回收对象占用的内存,编程人员必须确保该对象不能到达。这通常是通过将对象字段设置为null或者从集合(collection)中移除对象而完成的。但是,注意,当局部变量不再使用时,没有必要将其显式地设置为null。对这些变量的引用将随着方法的退出而自动清除。51Testing软件测试网*X8]!eH*{&`

51Testing软件测试网WFAY&H-V2C

 

+A&O,X+?%g051Testing软件测试网!] U gllm

      概括地说,这就是内存托管语言中的内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用。51Testing软件测试网G |_V x"k.@u

51Testing软件测试网8i:f4r_+k5_)wb,r

 51Testing软件测试网-UQ KY7yU

T'Zb1qHdT0      典型泄漏

wz u4Q@c|i/gZ051Testing软件测试网 b2~ n F5u}$Z

 51Testing软件测试网\tl%C*[#Gp q R

&o{ls,U0   既然我们知道了在Java中确实有可能发生内存泄漏,就让我们来看一些典型的内存泄漏及其原因。

2qN:{?x W0

[#],UMv{fg)Agg+R7d0  51Testing软件测试网7Q7X"o!h:g5^j

-z-CGO\N$H0      全局集合

9ri,i p;~R2G5y T7b0

Hk,C'q't!Jb9j6K0   51Testing软件测试网 H(n6Ow!Z%a _x

51Testing软件测试网6oTU.|,MQe

      在大的应用程序中有某种全局的数据储存库是很常见的,例如一个JNDI树或一个会话表。在这些情况下,必须注意管理储存库的大小。必须有某种机制从储存库中移除不再需要的数据。51Testing软件测试网a+d(N&O&eTrD~ o7N

V]W6|Z%e6ZG'b0 

D0Cj3asq&T7`(s W0

v5Xa%X o9{!ur0  这可能有多种方法,但是最常见的一种是周期性运行的某种清除任务。该任务将验证储存库中的数据,并移除任何不再需要的数据。51Testing软件测试网 m(?,q:Z,OR

4j9X OJs1H`-N9h \0 

,}\ y.D*H Ro051Testing软件测试网h C0y\8nsc_

      另一种管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零时,该元素就可以从集合中移除了。51Testing软件测试网}'D D k&rMi*?Ib2_ }-X

51Testing软件测试网['@uF U%B0_R1`

 

R7ok5[F8B0w5\@Q x0

H6cS)zG'~P `h0      缓存

:YG(ONm)^n+G:g051Testing软件测试网%G%xO_f7?i

 51Testing软件测试网9n:Mi7?|.Fh

51Testing软件测试网b5YE(|cl

  缓存是一种数据结构,用于快速查找已经执行的操作的结果。因此,如果一个操作执行起来很慢,对于常用的输入数据,就可以将操作的结果缓存,并在下次调用该操作时使用缓存的数据。

wC.[@4a-[\9L"jj?051Testing软件测试网pYKSq9hs,[

 

lfZS)O"G`0

qQ lbD$AtpC#e9a0     缓存通常都是以动态方式实现的,其中新的结果是在执行时添加到缓存中的。典型的算法是:

_@EDy`eQ051Testing软件测试网3W$y o]%b `*t}

 51Testing软件测试网G(Gm0hW/}Bu W3{

'A'B,UA-m~&o0    检查结果是否在缓存中,如果在,就返回结果。

!R7e X!a/CdF051Testing软件测试网k/D2n!?-f\

  51Testing软件测试网pU9E)w4n;`+G

51Testing软件测试网Dxld1q-n

      如果结果不在缓存中,就进行计算。

0z gD;ZOZ-M }051Testing软件测试网B k hZ%y,Q

 51Testing软件测试网Er#r K@az

51Testing软件测试网'A5te,be2?

      将计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。51Testing软件测试网yYC!xU1W

*lP9?W9m5S0MR;ef'b051Testing软件测试网-f)o3HDS

51Testing软件测试网O_MEq5B
  该算法的问题(或者说是潜在的内存泄漏)出在最后一步。如果调用该操作时有相当多的不同输入,就将有相当多的结果存储在缓存中。很明显这不是正确的方法。51Testing软件测试网*O,f2y6c-t,L]

51Testing软件测试网:I;TB0u@ W


%PEb [ V%gBx0  为了预防这种具有潜在破坏性的设计,程序必须确保对于缓存所使用的内存容量有一个上限。因此,更好的算法是:51Testing软件测试网M/FMf)FDY

krP D9ZJ051Testing软件测试网 oZax[z3jd
  检查结果是否在缓存中,如果在,就返回结果。

8SE+XxE}-}5K_E m X051Testing软件测试网+~9]?] H#~


E @*a2@*f^4`E}z,zn&mP0  如果结果不在缓存中,就进行计算。

/\~ b,_ F\051Testing软件测试网)ux0A6c$FY6D'Qx U


Z/C$V8n*G0dYqO0  如果缓存所占的空间过大,就移除缓存最久的结果。51Testing软件测试网![)M7W9r9Pd'y.mV

suS$C!o!mhR%\051Testing软件测试网:`t|j5Ky!y EA9@
  将计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。51Testing软件测试网.pJ*s3jI-VM4{6I!y-J$W

[s l.g/To&g"|9f0
9i%XTY R)W0  通过始终移除缓存最久的结果,我们实际上进行了这样的假设:在将来,比起缓存最久的数据,最近输入的数据更有可能用到。这通常是一个不错的假设。51Testing软件测试网z:Ga3IfT"X

51Testing软件测试网|3V L!Bs| C"K

51Testing软件测试网R9Wk8{ T
  新算法将确保缓存的容量处于预定义的内存范围之内。确切的范围可能很难计算,因为缓存中的对象在不断变化,而且它们的引用包罗万象。为缓存设置正确的大小是一项非常复杂的任务,需要将所使用的内存容量与检索数据的速度加以平衡。51Testing软件测试网/`AoyK

51Testing软件测试网2wy7d7cqeqG3LF,i

51Testing软件测试网L(w4`$r_6j8Z}/Jz
  解决这个问题的另一种方法是使用java.lang.ref.SoftReference类跟踪缓存中的对象。这种方法保证这些引用能够被移除,如果虚拟机的内存用尽而需要更多堆的话。

Z4M-j^#p.d:nc051Testing软件测试网P? g o*V-i


/@U sZ,S)SOn0  ClassLoader

9Wva({"c$n051Testing软件测试网9HuJ;Iy


5bWS&RzJ|0  Java ClassLoader结构的使用为内存泄漏提供了许多可乘之机。正是该结构本身的复杂性使ClassLoader在内存泄漏方面存在如此多的问题。ClassLoader的特别之处在于它不仅涉及“常规”的对象引用,还涉及元对象引用,比如:字段、方法和类。这意味着只要有对字段、方法、类或ClassLoader的对象的引用,ClassLoader就会驻留在JVM中。因为ClassLoader本身可以关联许多类及其静态字段,所以就有许多内存被泄漏了。51Testing软件测试网*A BT C3N2~

51Testing软件测试网 w2} ? Fw#nu:@

51Testing软件测试网m.W6L2d;KE.`pHG
  确定泄漏的位置

:C;B,u({O051Testing软件测试网a \8V{ R7T)l8@2Z

51Testing软件测试网 l]R N#T$JF
  通常发生内存泄漏的第一个迹象是:在应用程序中出现了OutOfMemoryError。这通常发生在您最不愿意它发生的生产环境中,此时几乎不能进行调试。有可能是因为测试环境运行应用程序的方式与生产系统不完全相同,因而导致泄漏只出现在生产中。在这种情况下,需要使用一些开销较低的工具来监控和查找内存泄漏。还需要能够无需重启系统或修改代码就可以将这些工具连接到正在运行的系统上。可能最重要的是,当进行分析时,需要能够断开工具而保持系统不受干扰。51Testing软件测试网q-C$T$a3Ut m#]

51Testing软件测试网4O;I-UoL^/aE4tP


J"Q7cG\#X0  虽然OutOfMemoryError通常都是内存泄漏的信号,但是也有可能应用程序确实正在使用这么多的内存;对于后者,或者必须增加JVM可用的堆的数量,或者对应用程序进行某种更改,使它使用较少的内存。但是,在许多情况下,OutOfMemoryError都是内存泄漏的信号。一种查明方法是不间断地监控GC的活动,确定内存使用量是否随着时间增加。如果确实如此,就可能发生了内存泄漏。

1k \:@#U;HFe,I0

_6v"u"@ ]0

xe0{ JlD0

TAG: 算法 java JAVA 垃圾回收

 

评分:0

我来说两句

Open Toolbar