最近一段時間,在公司里對java內存泄露的問題進行了調查。
問題的發現:
系統中在連續不停地、反復進行一個操作(先打開A,然后切替到畫面B,點擊畫面履歷再回到A,如此反復)。經過長時間的測試,經常會20小時,JVM的內存使用量增長30M以上。
問題的分析:
首先根據操作,找到會執行的代碼,對代碼進行分析。
Java會產生內存泄露的原因,經過本次調查,
1.對于打開的socket等資源,沒有做及時的回收處理。2.生存周期較長的對象,持有了生存周期較短的對象的引用,以至于那些生存周期短的對象,在無用的情況下,沒有得到回收。
3.對于類的成員變量為集合的情況,對集合的使用應該謹慎。比如,一個專門保存用戶操作履歷的對象,有全局變量List來保存用戶所有點擊過的鏈接。但實際項目中,不可能保存住用戶的每一次鏈接操作,然后顯示給用戶,有時候可能只是顯示最新的20條。所以這時候就要對這個全局變量進行處理,不能讓它無限的膨脹下去。
4.在類的成員變量為集合的情況,集合中的元素又是比較復雜的對象,(這個對象中可能還包含著是集合的成員變量)在不需要此類的對象的時候,應該自己來實現對類的成員的銷毀。如:
1
Iterator itor = myMap.keySet().iterator();
2
while (itor.hasNext()) {
3
MyObject selectedInfo = (MyObject) itor.next();
4
selectedInfo.destroy();// 假設MyObject里有destroy方法,對MyObjec中的成員進行銷毀
5
selectedInfo = null;
6
}
7
myMap.clear();

2

3

4

5

6

7

5.對單態模式應該慎用,單例對象在被初始化后將在JVM的整個生命周期中存在,如果單態對象持有外部對象的引用,那么這個外部對象將不能被回收,如故這個外部對象很龐大,那么對內存的消耗是很大的。
6.雖然寫java程序,有GC幫助我們管理內存,但好的編程習慣還是需要的,可以避免不必要的麻煩。
雖然寫java程序,有GC幫助我們管理內存,但好的編程習慣還是需要的,可以避免不必要的麻煩。
1.復雜的對象,在不需要的情況下,最好能實現對它的成員的銷毀,然后再將其賦為null。2.對于打開的流,一定要做及時的處理。另外對于HttpURLConnection對象,連接后,要調用它的disconnect(),不要對資源進行不必要的浪費。
3.盡量少用全局變量。
4.在哪里生成對象,就在哪里銷毀它。
5.盡量避免對象之間的相互引用。
最后,記述一下我記錄內存的方法。
由于對代碼做好修改之后,要確認一下內存是否有明顯增長。
于是寫一段代碼,每個5分鐘對對內存進行一次記錄,在連續運行20小時候,做成曲線圖,以便分析。
(以下是為了方便,重新寫的,原來項目中用到的,有一整套完備的定時器生成和起動的管理類,這里沒有寫出來。)
1
import java.io.DataOutputStream;
2
import java.io.FileOutputStream;
3
import java.io.IOException;
4
public class MemoryCollect {
5
public static void main(String args[]) {
6
MemoryCollector mc = new MemoryCollector(300000);
7
mc.start();
8
}
9
}
10
class MemoryCollector extends java.util.TimerTask {
11
private static final java.text.NumberFormat nf = java.text.NumberFormat
12
.getPercentInstance();
13
private static final java.text.DateFormat df = new java.text.SimpleDateFormat(
14
"yyyy-MM-dd HH:mm:ss");
15
private DataOutputStream dos = null;
16
private long period = 0;
17
private java.util.Timer timer = null;
18
MemoryCollector(long p) {
19
period = p;
20
timer = new java.util.Timer();
21
}
22
public void start() {
23
timer.schedule(this, 0, period);
24
}
25
public void run() {
26
System.gc();
27
Runtime imp = Runtime.getRuntime();
28
imp.totalMemory();
29
long totol = imp.totalMemory() / 1024;
30
long free = imp.freeMemory() / 1024;
31
try {
32
dos = new DataOutputStream(new FileOutputStream("D:\\memory.txt",
33
true));
34
String date = df.format(new java.util.Date());
35
String info = date + "\t" + totol + "\t" + free + "\t"
36
+ nf.format((double) free / (double) totol);
37
System.out.println(info);
38
dos.writeUTF(info + "\r");
39
dos.flush();
40
dos.close();
41
dos = null;
42
} catch (Exception e) {
43
System.out.println(e);
44
} finally {
45
try {
46
if (dos != null) {
47
dos.flush();
48
dos.close();
49
dos = null;
50
}
51
} catch (IOException e) {
52
e.printStackTrace();
53
}
54
}
55
}
56
public void stop() {
57
super.cancel();
58
if (timer != null) {
59
timer.cancel();
60
timer = null;
61
}
62
}
63
}

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日