作者weii (迷惑失道)
看板SFFamily
标题[转录]关於 memory leak 的几个 tip
时间Fri Dec 28 19:39:18 2007
※ [本文转录自 java 看板]
作者: willieliao (Willie Liao) 看板: java
标题: Re: [问题] 请教在linux跑的java错误讯息
时间: Thu Dec 27 13:53:23 2007
※ 引述《willieliao (Willie Liao)》之铭言:
: 你的程式有memory leak,也就是说程式跑越久memory就越用越多,
: 而且garbage collection之後也不会下降,最後就是出现outofmemory exception
: 这种情况短期治标是加大memory(-Xmx多少m),长期治本当然是要看哪里有memory
: leak,也就是物件越涨越大,reference的物件越来越多,而没有从memory中releas
: e掉。虽然java 有garbage collection,但是有reference到的物件是不会被collect的
....
: 推 dwi2:推一下这篇!我之前也有遇过这种问题 11/24 20:18
: 推 slalala:不知道可否请您分享 让新手注意如何建少记体用量的技巧呢? 11/24 22:53
: → slalala:因为我写的批次处理的程式 都还是会使记忆体用量偏大 11/24 22:55
: → slalala:除非缩视窗才会释放些空间应该有些地方观念上写的不够严谨 11/24 22:56
不好意思回一下有点久的文章,因为我现在才看到有人推文问问题。一般没有写过
c++直接写java的新手因为没有destroy object的观念,特别容易造成memory leak
首先我要强力推荐使用profiler,我们公司是用要付费的borland,不过免费的
jconsole(附在jdk1.5以上,到jdk_home/bin找jconsole.exe)更好用,而且有
强大的find thread deadlock功能(大感谢!让我少死很多脑细胞)。
如果你找不出来哪里有memory leak的话,让程式run一段时间用profiler去监视
哪种class的instance数量一直上升不下降,那就有可能是来源。
回到程式本身,一些小技巧如下:
第一,绝对不要写出无限回圈!即使是有限回圈,也要尽量减少跑的次数。
public int getThis() {
return this.
getThis();
}
以上的程式编译会过,但是一跑保证不到一秒就会出现stackoverflowerror。
第二,尽量不要保留intermediate(过渡)的物件
最耗记忆体的写法:
public void writeXML(FileWriter writer) {
Vector tempV = new Vector();
tempV.add("<root>");
for (int i = 0; i < 10000; i++) {
tempV.add("<value> + String.valueOf(i) + "</value>");
}
tempV.add("</root>");
for (int j = 0; j < tempV.size(), j++) {
writer.write(tempV.get(j)); //这里不需要cast
}
writer.close();
}
这个call会暂存10002个string objects..
有点sense的写法:
public void writeXML(FileWriter writer) {
StringBuffer sb = new StringBuffer();
sb.append("<root>");
for (int i = 0; i < 10000; i++) {
sb.append("<value>" + String.valueOf(i) + "</value>");
}
sb.append("</root>");
writer.write(sb.toString());
writer.close();
}
这个写法只会有一个stringbuffer的instance,但是这个instance会很大。
最省记忆体的写法
public void writeXML(FileWriter writer) {
writer.write("<root>");
for (int i = 0; i < 10000; i++) {
writer.write("<value>" + String.valueOf(i) + "</value>");
}
writer.write("</root>");
writer.close();
}
一边生成一边写出最省记忆体..
第三,不需要的物件要清掉,也就是把本来指向这个物件的variable
指向null
private HashMap tempHashMap = new HashMap();
public void doSomething() {
for (int i = 0; i < 100000; i++) {
tempHashMap.put(new Object(), new Object());
}
...
}
这个doSomething()假设你call他1000次,程式还是会执行无误。但是,你的
tempHashMap就会长大成1000倍,不但变慢还会memory leak。
解决之道要吗把变数搬进method里,要吗像这样:
public void doSomething() {
for (int i = 0; i < 100000; i++) {
tempHashMap.put(new Object(), new Object());
}
...
tempHashMap.clear();
}
或是
private HashMap tempHashMap;
public void doSomething() {
tempHashMap = new HashMap();
for (int i = 0; i < 100000; i++) {
tempHashMap.put(new Object(), new Object());
}
...
tempHashMap = null;
}
这两种方法的的效果其实差不多,不过後者更省记忆体一点。坏处是有的时候不小心
null pointer就会跑出来。
最後,尽量避免一些java中memory intensive的api calls。
这个的话真的要靠经验。比方说,java的regular expression和 JAXP(java API
for xml)里面都有很多吃memory的怪兽(美国这边我们叫它memory hog)。碰到的
话也只好用profiler找出来在想办法避开了。
大概就想到这麽多吧,板上的神人大大门不要客气尽量鞭...
Willie
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 76.111.69.123
1F:→ willieliao:好像错字很多,各位大大多包含,中文越来越不行了 12/27 14:07
2F:推 PsMonkey:第二点看不太懂.... 12/27 15:01
3F:→ willieliao:改一下好了,其实2跟三差不多 12/27 15:20
4F:→ willieliao:我改一下例子,请大家暂时不要推文 12/27 15:43
※ 编辑: willieliao 来自: 76.111.69.123 (12/27 16:07)
5F:→ willieliao:改好了,版大看看有没有比较通顺点 12/27 16:08
6F:推 kojilin:第一个应该是喷StackOverflowError之类的? 12/27 16:17
7F:→ willieliao:nice catch, 我改一下。 12/27 16:43
※ 编辑: willieliao 来自: 76.111.69.123 (12/27 16:46)
8F:→ willieliao:第一点其实应该要解释一下outofMemorryError (HEAP) 12/27 17:01
9F:→ willieliao:跟stackoverflowerror(STACK)的区别,不过太晚了明天 12/27 17:02
10F:→ willieliao:在来改好了 12/27 17:03
11F:推 PsMonkey:ㄜ... 还有,第三点的例子... 个人觉得不是很恰当 XD 12/27 18:47
12F:推 qrtt1:第三个例子是很多短命鬼 12/27 21:04
13F:推 linshihpong:有教学总比看到教学再抓错好...推一个~ 12/27 21:10
14F:→ willieliao:恩 举第三点的例子是因为我下面的junior programmer 12/27 23:13
15F:→ willieliao:常常cache用完不清掉...我再来想想有没有更好的例子 12/27 23:14
--
三月的柳絮不飞 你的心如小小的寂寞的城
我达达的马蹄是美丽的错误 我不是归人 我是马~
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 220.132.117.169