最近一段時(shí)間,在公司里對(duì)java內(nèi)存泄露的問(wèn)題進(jìn)行了調(diào)查。
問(wèn)題的發(fā)現(xiàn):
系統(tǒng)中在連續(xù)不停地、反復(fù)進(jìn)行一個(gè)操作(先打開(kāi)A,然后切替到畫(huà)面B,點(diǎn)擊畫(huà)面履歷再回到A,如此反復(fù))。經(jīng)過(guò)長(zhǎng)時(shí)間的測(cè)試,經(jīng)常會(huì)20小時(shí),JVM的內(nèi)存使用量增長(zhǎng)30M以上。
問(wèn)題的分析:
首先根據(jù)操作,找到會(huì)執(zhí)行的代碼,對(duì)代碼進(jìn)行分析。
Java會(huì)產(chǎn)生內(nèi)存泄露的原因,經(jīng)過(guò)本次調(diào)查,
1.對(duì)于打開(kāi)的socket等資源,沒(méi)有做及時(shí)的回收處理。2.生存周期較長(zhǎng)的對(duì)象,持有了生存周期較短的對(duì)象的引用,以至于那些生存周期短的對(duì)象,在無(wú)用的情況下,沒(méi)有得到回收。
3.對(duì)于類(lèi)的成員變量為集合的情況,對(duì)集合的使用應(yīng)該謹(jǐn)慎。比如,一個(gè)專(zhuān)門(mén)保存用戶操作履歷的對(duì)象,有全局變量List來(lái)保存用戶所有點(diǎn)擊過(guò)的鏈接。但實(shí)際項(xiàng)目中,不可能保存住用戶的每一次鏈接操作,然后顯示給用戶,有時(shí)候可能只是顯示最新的20條。所以這時(shí)候就要對(duì)這個(gè)全局變量進(jìn)行處理,不能讓它無(wú)限的膨脹下去。
4.在類(lèi)的成員變量為集合的情況,集合中的元素又是比較復(fù)雜的對(duì)象,(這個(gè)對(duì)象中可能還包含著是集合的成員變量)在不需要此類(lèi)的對(duì)象的時(shí)候,應(yīng)該自己來(lái)實(shí)現(xiàn)對(duì)類(lèi)的成員的銷(xiāo)毀。如:

2

3

4

5

6

7

5.對(duì)單態(tài)模式應(yīng)該慎用,單例對(duì)象在被初始化后將在JVM的整個(gè)生命周期中存在,如果單態(tài)對(duì)象持有外部對(duì)象的引用,那么這個(gè)外部對(duì)象將不能被回收,如故這個(gè)外部對(duì)象很龐大,那么對(duì)內(nèi)存的消耗是很大的。
6.雖然寫(xiě)java程序,有GC幫助我們管理內(nèi)存,但好的編程習(xí)慣還是需要的,可以避免不必要的麻煩。
雖然寫(xiě)java程序,有GC幫助我們管理內(nèi)存,但好的編程習(xí)慣還是需要的,可以避免不必要的麻煩。
1.復(fù)雜的對(duì)象,在不需要的情況下,最好能實(shí)現(xiàn)對(duì)它的成員的銷(xiāo)毀,然后再將其賦為null。2.對(duì)于打開(kāi)的流,一定要做及時(shí)的處理。另外對(duì)于HttpURLConnection對(duì)象,連接后,要調(diào)用它的disconnect(),不要對(duì)資源進(jìn)行不必要的浪費(fèi)。
3.盡量少用全局變量。
4.在哪里生成對(duì)象,就在哪里銷(xiāo)毀它。
5.盡量避免對(duì)象之間的相互引用。
最后,記述一下我記錄內(nèi)存的方法。
由于對(duì)代碼做好修改之后,要確認(rèn)一下內(nèi)存是否有明顯增長(zhǎng)。
于是寫(xiě)一段代碼,每個(gè)5分鐘對(duì)對(duì)內(nèi)存進(jìn)行一次記錄,在連續(xù)運(yùn)行20小時(shí)候,做成曲線圖,以便分析。
(以下是為了方便,重新寫(xiě)的,原來(lái)項(xiàng)目中用到的,有一整套完備的定時(shí)器生成和起動(dòng)的管理類(lèi),這里沒(méi)有寫(xiě)出來(lái)。)

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

----2009年02月02日