详解Java垃圾收集算法

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

\~kR}zJ0k#]01.垃圾收集算法的核心思想

y7u NEagJ._ G8R6A9`051Testing软件测试网0K^[$dS2`!t1x6Mj

    Java语言建立了垃圾收集机制,用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。该机制可以有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引发的内存耗尽,以及不恰当的内存释放所造成的内存非法引用。51Testing软件测试网9K)Y:M9\'eK*O

51Testing软件测试网/LD l8|!D$ls4^*E%u

 51Testing软件测试网\:q n @PPXB

@Sf{(s/r2tf0     垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能,因此需要开发人员做比较深入的了解。51Testing软件测试网(_F m'B:z t1g \L_&]:J B'p

U;a4C.zi,g"fU0 

A5D D(s({t7mf-}4O051Testing软件测试网5~ JsT4WDg9m4o:`

2.触发主GC(Garbage Collector)的条件

%K-{4`o+i_:^o f0

7I.y)oD.E(r4y![0 

D|0]vBn0

?]`I)G \'cp0  JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:51Testing软件测试网,L!A{&E _9q v

,K(t~$DTFNG0 51Testing软件测试网Q^ Y6TGz+E(QcHL.Y

51Testing软件测试网8Xa2L'a"q6r4l&C

      ①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。51Testing软件测试网g@i$U y%A)IO

`i W dpY.z0 51Testing软件测试网 a/y X+Yl7b/U4Od)j/o

YI:Y/R|v*X4[0  ②Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。

$M w;cA)?~ T {'sQR{X1[051Testing软件测试网0Y6c,KT+o`9w `

 

s8g0rPW'TOc-o:C051Testing软件测试网IF;G7S,M

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

D4N ~2xX-g \#e;i2t&B0 51Testing软件测试网&YFtwBAs

51Testing软件测试网k.Nc p0])o*Ic

3.减少GC开销的措施51Testing软件测试网E(HyRXf%c1Y

51Testing软件测试网 sw{N+]7g#Bf

 

8L!p;IfLp051Testing软件测试网*hM.~$v _P

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

2[3z}7~'L0

4`&y!w*X@8A#s!Q-^-O0 

'M%Z|/zu3Fa051Testing软件测试网~n9J,}'b*[-r-k

    (1)不要显式调用System.gc()51Testing软件测试网M|k)]i2foAZ

Uz"gJuK']3a2`0此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。51Testing软件测试网 BN(k_5|s3k9A

hN-i%[:?MC5t8Y3[0 

Xv5~n#^ |;u u&Ks5K051Testing软件测试网.q.A8b{(O

     (2)尽量减少临时对象的使用51Testing软件测试网8~w*LXh)J:J Uz

51Testing软件测试网_L s BFJ p)g0{'ub

  临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。51Testing软件测试网R%_%[ c#lc

51Testing软件测试网B)R'HI2P

 

:u9G#bT^oBK051Testing软件测试网4ABg1eh*_

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

.p7cp$b'pDK&{SZ5V7_0

j+{L9Y B0  一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。51Testing软件测试网j.b~:Y0qOsQ!vC

(U;Vic7u&o0 

Kz&^,G(I4Ah051Testing软件测试网jJ-Z;Bs"}|[6mj

     (4)尽量使用StringBuffer,而不用String来累加字符串(详见blog另一篇文章JAVA中String与StringBuffer)

'EPE'L%_Mu051Testing软件测试网(a+n;V*Ry!bO6S

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

i^L7Y$Mo,MB0 

TD:iz j2sa051Testing软件测试网x'Uc9E?G%y,E

     (5)能用基本类型如Int,Long,就不用Integer,Long对象

p)v2C{4@y B)^B'N0

(^3I HP:r6Fj D0      基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。

]U2K7]vO051Testing软件测试网4yQ |*I.D

 

.uz)J.p`O051Testing软件测试网*V%`Su$f?$Y1Y.YXEu6R

     (6)尽量少用静态对象变量

3B%K){$](b051Testing软件测试网$C s pe$MY

  静态变量属于全局变量,不会被GC回收,它们会一直占用内存。51Testing软件测试网)H~2lJ5w*Y*o$Nz N9r

51Testing软件测试网x|&})vV5}9xp*y

 51Testing软件测试网fL P-r9Bf

x7{F9p@4v0   (7)分散对象创建或删除的时间

%Xr z,T;WY V`0

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

ois\\q0Y9Z0 

w-V s%J Hu051Testing软件测试网2`?"o}j

    4.gc与finalize方法

jm"I_G_051Testing软件测试网a$Ol1uD,l+Q

     ⑴gc方法请求垃圾回收51Testing软件测试网?O$SN"Au v Gs

D@Z"G6Mt0      使用System.gc()可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java的垃圾回收。需要注意的是,调用System.gc()也仅仅是一个请求。JVM接受这个消息后,并不是立即做垃圾回收,而只是对几个垃圾回收算法做了加权,使垃圾回收操作容易发生,或提早发生,或回收较多而已。

;kPxZf SPm*U0

N0I0i4XjRp2P0 51Testing软件测试网if/J"GMTF

51Testing软件测试网N)N^&s g2`HL_ n

  ⑵finalize方法透视垃圾收集器的运行

0x9E m0U4B0

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

?B*\ [`+n| a0  protected void finalize() throws Throwable

o6}&}'N4H/d Lv051Testing软件测试网)?-XJ!k6}/I

 51Testing软件测试网+P'E+Q1zX;E1\B)c

51Testing软件测试网j(J0hMK{:nD

  在finalize()方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。51Testing软件测试网3|!f/?:\s?

j Dk#U6s2H$` Z1sA0   51Testing软件测试网u]6yv_:[3N ~-h

51Testing软件测试网Rbe5_)Xm5E

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

5]5B?njMU \0 51Testing软件测试网Kz~8L;n*L KA

p {J)yLT&Z@kh+@0      以下是引用片段:

5xV(k wz-En-\051Testing软件测试网E{.A/J;Nn{R v

 

5IwLPs S5S7n051Testing软件测试网:h^ e? z;d7c&?:S

  protected void finalize(){

x5zx_U+u_[6c N0

g6X)X_ SrnR0  // finalization code here51Testing软件测试网`"_Z%K)g0t"Q-d`0^6q
      }51Testing软件测试网(f jHZGB/z

51Testing软件测试网u`7W P?6Q5d

 51Testing软件测试网p/R;c0J3^ ~5Gxof

51Testing软件测试网x0T5n KPv(p

    ⑶代码示例51Testing软件测试网8RXO!w!I#U K

51Testing软件测试网YN` J)S|6M0n@m0w

  以下是引用片段:51Testing软件测试网"p}Z\#|ex%h3[wUN

51Testing软件测试网o {#\ks

  class Garbage {
6o gSR.M+BeM0      int index;51Testing软件测试网t2L Z {&jjb

51Testing软件测试网"Q}!@;S6Uh/X? K

  static int count;

ed-EGi~4d OP{!N051Testing软件测试网?z9J*JY*Pb oD9}

  Garbage() {

}6]5] Ql"S\ q aT0

uP#d#b ~.O1Q0  count++;
S7C1hft&x*D5W0      System.out.println("object "+count+" construct");

.IC,r,{3Z0Sl3U051Testing软件测试网)vJ:j&f wl

  setID(count);51Testing软件测试网 LEb.^q$t1th qk
    }
6V'X(^5y!h%eq x0     void setID(int id) {51Testing软件测试网PFV:T6S@a
  index=id;51Testing软件测试网/uO,{.r0U%i,^)f
      }
~2HjY9CH+a_0     protected void finalize() //重写finalize方法51Testing软件测试网F.m@B"o1d{
     {51Testing软件测试网U4Xqa#~%R3m
    System.out.println("object "+index+" is reclaimed");51Testing软件测试网Ejlxg1u

51Testing软件测试网(j?|A-B

   }51Testing软件测试网0~z)J6cxr|f
    public static void main(String[] args)  {
d0d{?Q;FA4i,^0      new Garbage();

DW|7X3v\ihD051Testing软件测试网G2Z^"id

  new Garbage();51Testing软件测试网0e&o3z _|Te,g
  new Garbage();

m5w?]-a$O7Ii0

*lcaU;an0  new Garbage();
(`1t5UEB`j-]jq}0      System.gc(); //请求运行垃圾收集器
h'mp g*h6\hO#p0     }

g%P+u.`-OI0

oV5tw&e u G0     }51Testing软件测试网JY Cw TOx-{y

oeSi+AmVF S1v#X0 51Testing软件测试网yF|{t3c%s

%p$~+gN(r/_7["c.ZhR05.Java 内存泄漏51Testing软件测试网rPvS;I

f R,`Yr\0 

,A(c}#NR/q0Ed&s0\051Testing软件测试网/dO'f2]'}:| ys

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

51Testing软件测试网7Ol8h+ts%U6ax9n4_

 51Testing软件测试网.f'DFuQF,f_"[

51Testing软件测试网$G$m]e]

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

y2Ty4xazX0hEq|c051Testing软件测试网!Kj&{1fd_b4Sk+]

 

Q K5H].sX0

&g(M `y^}GI0  以下是引用片段:51Testing软件测试网v0y)ar"S

51Testing软件测试网$V7RP5Y5s

public class ObjStack {51Testing软件测试网,GO mY;wx
   private Object[] stack;51Testing软件测试网.J!`xA D!Uf ^c

51Testing软件测试网q-x/a\ T]"[ z

   private int index;

W5w%M(`uc051Testing软件测试网Vmg K1B%g8B

   ObjStack(int indexcount) {51Testing软件测试网O8y$@"cM

51Testing软件测试网J,k'n(m5\5W

   stack = new Object[indexcount];51Testing软件测试网"Z;e ap`&sY
   index = 0;
51Testing软件测试网,S|$Z$j5G K

51Testing软件测试网+x [m+Y T7B%?

   }

?mP1\]!L0

A,u&^{T"Fl0 51Testing软件测试网%L.s R+y!QLDm |

wl h[|M8w0   public void push(Object obj) {
k&|$E g@9i7z'h0   stack[index] = obj;51Testing软件测试网7? h0s"?A N
   index++;

5J e _(KxG$@\!A051Testing软件测试网ef!rh ^O _ Q7U)Q1Gi

   }51Testing软件测试网 |{-Y7Mz%[z"fX

i7D*NgpT*W9{0 

+j wO:\#wj}n\4a051Testing软件测试网AK{;} qF1y

   public Object pop() {
w6N5zZcVw6P0    index--;
51Testing软件测试网,VOOZ1dA:\#X6PR

B#VP'C6fl1HK#f.a7n0    return stack[index];51Testing软件测试网x7}4wwTD

B/W d$Ij@4[6{ ]W0   }51Testing软件测试网,mN.R{4q)G!i jd"_@

51Testing软件测试网)y x"N(q6np[ e(mX9v

   }

o ~3IG*S7iN0

m+W#^pA-\0 

#v.UiYru4nHOd|z0

Q%D}C t o%M L`0{0    public class Pushpop {

Gs3cb~9I1d1G}051Testing软件测试网ur6V4Et NW

       public static void main(String[] args) {51Testing软件测试网8R;yn0smjEO P/fP4C

8w(LS0xo-i;v4r d2k0         int i = 0;51Testing软件测试网b1K^+a-x?,]/G+K!oW

K&s!UUb n!B0         Object tempobj;

;~$O SRsf0]0

d:N1Ok G2~0         ObjStack stack1 = new ObjStack(64);

R Z1w5Y zN `?ib e051Testing软件测试网 Kafv2?`W\(Xn

         /**  new一个ObjStack对象,并调用有参构造函数。分配stack Obj数组的

:p+v f!P-fM051Testing软件测试网 j7b9w:{3M-K'H.D

           *   空间大小为64,可以存64个对象,从0开始存储。
p%L&[M3VA0          */
51Testing软件测试网OQV4IW-o{

51Testing软件测试网MAV+p/Un[

 

m5{$s s |Y A0

6AC[2\#cp I1m0       while (i < 64)

H%KtpZ(k qp {d7Mr051Testing软件测试网g}N4E8{ ]Ivr

         {   tempobj = new Object();51Testing软件测试网9N~ ]*z y8J

51Testing软件测试网Q)[8l2cTRg's

              //循环new Obj对象,把每次循环的对象一一存放在stack Obj数组中。
6H7?;J%_;eQ)A&KDG0u0             stack1.push(tempobj);

#@"kC[g8^C0

P{7F8S;nfE0u/Pz0             i++;

E.o4S4b-W6|1r051Testing软件测试网6\O[ ~/h:mU rJ4g

             System.out.println("第" + i + "次进栈" + "t");

6M [ LdH~(h)Q JrM051Testing软件测试网%J T$`*n!}k

          }51Testing软件测试网v4ufJ1OI

51Testing软件测试网!V6`je;`1cM

 

uE;@ cBJ3wy0

6r1RZf8Pg0          while (i > 32)

"b%X6J n ~ov8Z s6S0

4@*Eten%J$H.|0         {51Testing软件测试网%wvm,yr8t4k3Z'\C

51Testing软件测试网)d/x'u0\ I

             tempobj = stack1.pop();//这里造成了空间的浪费。51Testing软件测试网K\/W&n9XFc

-B|bjxG9f){ V[0             /**正确的pop方法可改成如下所指示,当引用被返回后,51Testing软件测试网 Q[7lr,Z6q
                *堆栈删除对他们的引用,因此垃圾收集器在以后可以回收他们
51Testing软件测试网%Aa4W#C p

:eBG;V `7H0             */
,kk P @6?8q0             /**

9SN(T4{ \`.i0

4A"IPCQ|*P dJ0               * public Object pop() {51Testing软件测试网 Tl R0w;c S$|)W~l

51Testing软件测试网"k9R5HKZr }u

               *   index - -;

@d3lND&`@ ]051Testing软件测试网,C'm(OQ%Mr(H

               *   Object temp = stack [index];51Testing软件测试网 Yvf8R!w
               *   stack [index]=null;return temp;
51Testing软件测试网 }:T1@:D:h6c&[+X

y8K| E(J| UW PtC0               *   }

~)Z!zBCl$g P0

d1N7sp{ kS!U0             */
;Q*[obu S5R3u,t0               i--;
(ZzD\ Bn0               System.out.println("第" + (64 - i) + "次出栈" + "t");

ZA.ta:o m3|0

&L8n1y7F$y ?0              }

jg(~2P8[{YI,E6Q0

!~BP3CYY&{ Vs0            }51Testing软件测试网K8YkHV ?i

zdY8^^ e]W#RB&[*Z(A0          }51Testing软件测试网t1^ Git&G\_J

%D&s Kdw-B051Testing软件测试网3K(j-K'i8Db7dB
6.如何消除内存泄漏

3VmRJT9I5T051Testing软件测试网eC(|K_{5k

 51Testing软件测试网4ms5KhJh+eN(O

51Testing软件测试网$eY3?L-G$EdIiI

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

!qaN{c4D#dU gC051Testing软件测试网 ~"^0?5Ula O~

 

"rXewwZn _051Testing软件测试网 n@5U~D9Kw3W

      垃圾收集器的作用

y:D&N'v a0

~W6^8tw0    51Testing软件测试网"Z5b`S3\8x&L

51Testing软件测试网 S(F-Kts$c l9g l

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

c3qi?e {2x!]l051Testing软件测试网?}x#g4J

 51Testing软件测试网n5C^;\ uz7T~"R?|

51Testing软件测试网 @ t:H mDPPm.R

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

51Testing软件测试网2hi ST ?pX;a

 

O9S't)`P8el;w0

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

h#\ T Wn Z_0 

`'BIM'Lk+Q0

O |,J'o|5zR0      概括地说,这就是内存托管语言中的内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用。51Testing软件测试网"s&{7[Z D zc

8y.Y#EwOz0 

p F;uQ/AY4t3I7^,|0

3Pe0Sqbx[D)D0      典型泄漏51Testing软件测试网]jk2rI Y@

H'Q\U6G0 

~n9f"DF;p5f0

+UGFl FfRV-i[`#ll0   既然我们知道了在Java中确实有可能发生内存泄漏,就让我们来看一些典型的内存泄漏及其原因。51Testing软件测试网4Z Q9}2H q D,n2mwz

C7rV0y-G#X0  51Testing软件测试网 u[#d j+i5Z9k

51Testing软件测试网 @g/MEQ2QEQ8h

      全局集合51Testing软件测试网#},rwP*_ [g

4h?f]9d ^2}0   51Testing软件测试网N"m^ e:Fa

51Testing软件测试网w*Vv1Z1^y:{6ov

      在大的应用程序中有某种全局的数据储存库是很常见的,例如一个JNDI树或一个会话表。在这些情况下,必须注意管理储存库的大小。必须有某种机制从储存库中移除不再需要的数据。

K;hc%w;y7gi0

*z8B \f)B:_0 51Testing软件测试网(GM7r;B![^

C.E0Awa&C6R0  这可能有多种方法,但是最常见的一种是周期性运行的某种清除任务。该任务将验证储存库中的数据,并移除任何不再需要的数据。

:j-QF4G#s0

8w$Xl)?7_Z|eR$`0 

]%c0ZDh]%V8g9H,H051Testing软件测试网] ]`,fj$Wt:N

      另一种管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零时,该元素就可以从集合中移除了。

nr,O/jg HY"M7KC#k0

0p1O7Lo%TJo0 51Testing软件测试网3EADb vXd1p

51Testing软件测试网&D`T/Xf7R'\x

      缓存51Testing软件测试网l!~wQ6k,D-f%D

#RzQN)ZL0 

[&s+n(H];W0

)`l zP(F7n4T,H0  缓存是一种数据结构,用于快速查找已经执行的操作的结果。因此,如果一个操作执行起来很慢,对于常用的输入数据,就可以将操作的结果缓存,并在下次调用该操作时使用缓存的数据。51Testing软件测试网itv:SF

? pUZ#P"S;uv0 

T0N\"u0\051Testing软件测试网g9l f:]V(eF:tA

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

{q"cn;Qda+~&@F_051Testing软件测试网 @@'I${9Q(K)fl*N

 

Rl|WG0{I0

v,nMR)\l0    检查结果是否在缓存中,如果在,就返回结果。51Testing软件测试网&|fuE7b[5[

7s"cw{ i"jA0  

:J@2z9f VOG j0

3z-kW*D$A%^0      如果结果不在缓存中,就进行计算。51Testing软件测试网)J n*\t6\c

*D6~KA5U_4FG0 51Testing软件测试网W[H(L i {[t3De Q

-sgET|8d A.icU0      将计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。51Testing软件测试网Fwe\5}!Os&oz

t|se w9W*pa0?'P051Testing软件测试网E-^n9P|#U


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

51Testing软件测试网u/B2N#I/j

51Testing软件测试网#F\ D5kL0V:qe-O3f
  为了预防这种具有潜在破坏性的设计,程序必须确保对于缓存所使用的内存容量有一个上限。因此,更好的算法是:

$z6mV^v'ae5G0t051Testing软件测试网8VXL oRslQ!n

51Testing软件测试网 {mp:CQ9nC;A M"?
  检查结果是否在缓存中,如果在,就返回结果。51Testing软件测试网g5q9k F2w6r,h|

51Testing软件测试网[5uW8`^ h|8XB


?4Os+}1R(DW0  如果结果不在缓存中,就进行计算。51Testing软件测试网J,}%W(Y!I7[{,az

3T1KKf{051Testing软件测试网Kw]!d3z,G
  如果缓存所占的空间过大,就移除缓存最久的结果。51Testing软件测试网tpE/W+j

51Testing软件测试网n OxP,pV

51Testing软件测试网_B{%^"C
  将计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。51Testing软件测试网i(V-qJY'D4UX3r

51Testing软件测试网`.sS%y[!UV


9LB)d+t!};@pd)y0  通过始终移除缓存最久的结果,我们实际上进行了这样的假设:在将来,比起缓存最久的数据,最近输入的数据更有可能用到。这通常是一个不错的假设。51Testing软件测试网{/hZO C/v

2c jg+@4~^051Testing软件测试网T%E6P`Ey;T,d*h:PU
  新算法将确保缓存的容量处于预定义的内存范围之内。确切的范围可能很难计算,因为缓存中的对象在不断变化,而且它们的引用包罗万象。为缓存设置正确的大小是一项非常复杂的任务,需要将所使用的内存容量与检索数据的速度加以平衡。

t4^"_ c5EU A.H051Testing软件测试网LtR!t:|;E&x#v5z1D

51Testing软件测试网j}&I.z C s(S
  解决这个问题的另一种方法是使用java.lang.ref.SoftReference类跟踪缓存中的对象。这种方法保证这些引用能够被移除,如果虚拟机的内存用尽而需要更多堆的话。51Testing软件测试网:p)ei#o/@e*[

51Testing软件测试网n$PNgvO\$I

51Testing软件测试网0sB-Wn(OR
  ClassLoader

:O"Na;CR.r051Testing软件测试网IdnE\ N,C


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

P#Qr5`z{051Testing软件测试网by KLa`7Z
  确定泄漏的位置51Testing软件测试网\ ^8GZQj

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

"X%W.q3h3n5o*[ n:@0

m*y.v+p-Kb051Testing软件测试网&Ml2J:jg-ph
  虽然OutOfMemoryError通常都是内存泄漏的信号,但是也有可能应用程序确实正在使用这么多的内存;对于后者,或者必须增加JVM可用的堆的数量,或者对应用程序进行某种更改,使它使用较少的内存。但是,在许多情况下,OutOfMemoryError都是内存泄漏的信号。一种查明方法是不间断地监控GC的活动,确定内存使用量是否随着时间增加。如果确实如此,就可能发生了内存泄漏。

R HV mYX&}051Testing软件测试网#F }2d0qM

"YN5@E3L)V0

TAG: 算法 java JAVA 垃圾回收

 

评分:0

我来说两句

Open Toolbar