论坛首页 Java企业应用论坛

关于性能优化

浏览 40005 次
精华帖 (2) :: 良好帖 (10) :: 新手帖 (0) :: 隐藏帖 (1)
作者 正文
   发表时间:2010-11-17  
ouchxp 写道
dsjt 写道
ouchxp 写道

JDK自己已经有优化了. 不合理的优化反而会降低效率
比如字符串常量拼接
String str ="aaa" + "bbb" +"ccc" + "ddd";

StringBuilder sb = new StringBuilder();
sb.append("aaa");
sb.append("bbb");
sb.append("ccc");
sb.append("ddd");
String str = sb.toString();

哪种效率高? 答案是第一种. 对于新手来说想优化很容易适得其反.
反编译看一下就知道为什么了.


我们项目经理不让用第一种

如果你们的JDK版本在1.4或以下的话还说得过去.
如果是1.5或以上.
你完全可以和他提出来.你写一段测试代码给他看.执行效率有很明显的对比
反编译Class给他看.JDK已经做好优化了.自己的优化不仅多余,反而降低了执行效率


你试试我这段,再想想清楚。。
testString cost =453 mils
testStringBuilder cost =0 mils

public static void main(String[] args) {
long t1 = System.currentTimeMillis();
String str1 = "";
for (int i = 0; i < 20000; i++) {
str1 = str1 + "a";
}
long t2 = System.currentTimeMillis();
System.out.println("testString cost =" + (t2 - t1) + " mils");

long t3 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < 20000; i++) {
sb.append("a");
}
long t4 = System.currentTimeMillis();
System.out.println("testStringBuilder cost =" + (t4 - t3) + " mils");

}
0 请登录后投票
   发表时间:2010-11-17   最后修改:2010-11-17
0 请登录后投票
   发表时间:2010-11-17  
蔡华江 写道
ironsabre 写道
蔡华江 写道
ironsabre 写道
flysnowxf 写道
ironsabre 写道
flysnowxf 写道
汗,这是很基础的概念了。
百度百科:http://baike.baidu.com/view/714962.htm

引用
当以前分配的一片内存不再需要使用或无法访问时,但是却并没有释放它,那么对于该进程来说,会因此导致总可用内存的减少,这时就出现了内存泄漏。


再说一次,向集合里一直加东西不删除,这个不叫内存泄漏。


那是你的理解了。我的理解是,分配了内存,但这些内存不再被使用,比如过期了、没用了,但又没有去删除,造成内存占用越来越多。HashMap的使用就容易出现这个问题。


哎。你这个没过期,也有用。你可以通过HashMap取出来值的,这怎么能叫泄漏呢?
我现在总是你,如果你向cache里放了100个对象,永远keep住,如果我应用程序经常来访问它,叫不叫泄漏?
如果我应用程序再也不来访问它,叫不叫泄漏?
按你的理解,第一种不叫,第二种叫。可这两种没有区别。我不可能用由第三方来判断我自己是不是泄漏,明白吗?

下面这个Stack,在被别人用的时候,可能会引起泄漏。你先找找看哪行会出总是。然后再理解一下跟你这个有什么区别。
import java.util.Arrays;
import java.util.EmptyStackException;

public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;

public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}

public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}

public Object pop() {
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}

/**
* Ensure space for at least one more element, roughly doubling the capacity
* each time the array needs to grow.
*/
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}


你这个封装Object[]的Stack跟封装HashMap的Cache有什么区别?
内存泄漏是指存在无法释放的无用引用,从这点来说,HashMap里面的内容也是内存泄漏。


HashMap里的内容你仍然可以自由的存取的,这不叫泄漏,你觉得内存不够用了,你可以去删除掉一些。这完全可以自己控制的,这叫什么泄漏??那按你这个说法,只要向容器里放对象就是泄漏了??

我们现在再看这个Stack类,我把这个Stack类打成一个jar包,然后我提供给你用,你用上面这个Stack作为你的容器,你push一万次,再pop一万次,你的Stack的size已经是0了,但是这一万个对象仍然无法放掉(前提当然是Stack本身要被keep住)。你会有一些内存你已经永远无法使用了,而且你已经没有任何能力去放掉它了(不包括回收到Stack本身)。
看得懂区别了吗??

是你没看懂我的意思。我指的是“封装了HashMap"的Cache。
你如果能拿到Stack中Object[]的引用,你一样可以对数组进行操作。
如果你拿不到HashMap的引用,你一样无法对数组进行操作。
所以问题并不是hashMap本身。
其次,就算你能拿到HashMap本身,我也认为这是内存泄漏。请注意,"内存泄漏是指存在无法释放的无用引用"。
Java最大特色在于内存的自动释放。
Object a = new Object();
。。。
a = null;
显然,a是释放了的,至少程序员的本意是要释放a的,但是由于集合中保存了另一个a的引用(这个总是很隐蔽的),导致a的内存并没有释放,这不是溢出,这是泄漏。


你是无法拿到Stack中Object[]的引用的。
但请你告诉我,你无法存取删除Cache里的值呢。如果你能存取Cache里的东西,你也能去删除掉它,这叫什么泄漏?
或者你告诉我们,我们要怎么理解一个在Cache里的东西是属于正常的需要被Cache的 还是属于泄漏的呢?
0 请登录后投票
   发表时间:2010-11-17  
ironsabre 写道
蔡华江 写道
ironsabre 写道
蔡华江 写道
ironsabre 写道
flysnowxf 写道
ironsabre 写道
flysnowxf 写道
汗,这是很基础的概念了。
百度百科:http://baike.baidu.com/view/714962.htm

引用
当以前分配的一片内存不再需要使用或无法访问时,但是却并没有释放它,那么对于该进程来说,会因此导致总可用内存的减少,这时就出现了内存泄漏。


再说一次,向集合里一直加东西不删除,这个不叫内存泄漏。


那是你的理解了。我的理解是,分配了内存,但这些内存不再被使用,比如过期了、没用了,但又没有去删除,造成内存占用越来越多。HashMap的使用就容易出现这个问题。


哎。你这个没过期,也有用。你可以通过HashMap取出来值的,这怎么能叫泄漏呢?
我现在总是你,如果你向cache里放了100个对象,永远keep住,如果我应用程序经常来访问它,叫不叫泄漏?
如果我应用程序再也不来访问它,叫不叫泄漏?
按你的理解,第一种不叫,第二种叫。可这两种没有区别。我不可能用由第三方来判断我自己是不是泄漏,明白吗?

下面这个Stack,在被别人用的时候,可能会引起泄漏。你先找找看哪行会出总是。然后再理解一下跟你这个有什么区别。
import java.util.Arrays;
import java.util.EmptyStackException;

public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;

public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}

public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}

public Object pop() {
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}

/**
* Ensure space for at least one more element, roughly doubling the capacity
* each time the array needs to grow.
*/
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}


你这个封装Object[]的Stack跟封装HashMap的Cache有什么区别?
内存泄漏是指存在无法释放的无用引用,从这点来说,HashMap里面的内容也是内存泄漏。


HashMap里的内容你仍然可以自由的存取的,这不叫泄漏,你觉得内存不够用了,你可以去删除掉一些。这完全可以自己控制的,这叫什么泄漏??那按你这个说法,只要向容器里放对象就是泄漏了??

我们现在再看这个Stack类,我把这个Stack类打成一个jar包,然后我提供给你用,你用上面这个Stack作为你的容器,你push一万次,再pop一万次,你的Stack的size已经是0了,但是这一万个对象仍然无法放掉(前提当然是Stack本身要被keep住)。你会有一些内存你已经永远无法使用了,而且你已经没有任何能力去放掉它了(不包括回收到Stack本身)。
看得懂区别了吗??

是你没看懂我的意思。我指的是“封装了HashMap"的Cache。
你如果能拿到Stack中Object[]的引用,你一样可以对数组进行操作。
如果你拿不到HashMap的引用,你一样无法对数组进行操作。
所以问题并不是hashMap本身。
其次,就算你能拿到HashMap本身,我也认为这是内存泄漏。请注意,"内存泄漏是指存在无法释放的无用引用"。
Java最大特色在于内存的自动释放。
Object a = new Object();
。。。
a = null;
显然,a是释放了的,至少程序员的本意是要释放a的,但是由于集合中保存了另一个a的引用(这个总是很隐蔽的),导致a的内存并没有释放,这不是溢出,这是泄漏。


你是无法拿到Stack中Object[]的引用的。但请你告诉我,你为什么无法拿到Cache里的值呢。


这个。。。。
0 请登录后投票
   发表时间:2010-11-17  
性能这个问题 从设计到开发到测试都是需要考虑和验证的,不是说开发完了 通过测试来找问题,LZ的观点偏了
0 请登录后投票
   发表时间:2010-11-17  
现在设我是被Cache住的一个小对象,我就是这个对象,那么我的身份怎么定义?

A) 我是一个Cache住的有用的对象,我能提高访问效率。
B) 我是一个被泄漏掉的垃圾,我是垃圾。

我是A还是B,谁说了算,按什么标准执行?
0 请登录后投票
   发表时间:2010-11-17  
ouchxp 写道
dsjt 写道
ouchxp 写道

JDK自己已经有优化了. 不合理的优化反而会降低效率
比如字符串常量拼接
String str ="aaa" + "bbb" +"ccc" + "ddd";

StringBuilder sb = new StringBuilder();
sb.append("aaa");
sb.append("bbb");
sb.append("ccc");
sb.append("ddd");
String str = sb.toString();

哪种效率高? 答案是第一种. 对于新手来说想优化很容易适得其反.
反编译看一下就知道为什么了.


我们项目经理不让用第一种

如果你们的JDK版本在1.4或以下的话还说得过去.
如果是1.5或以上.
你完全可以和他提出来.你写一段测试代码给他看.执行效率有很明显的对比
反编译Class给他看.JDK已经做好优化了.自己的优化不仅多余,反而降低了执行效率


你这段代码确实String+和StringBuilder看来效果差不多,或才说String+还快一点点。
但是你要清楚为什么。
这是因为你这两个方法生成的对象的数量是一样的,所以你这个例子很有欺骗性。
你要明白,之所以性能差别很大,最主要的就是String+在大多数情况下会多生成很多临时对象来。

下面这个例子,String+的性能会差上几千倍。

public static void main(String[] args) {
long t1 = System.currentTimeMillis();
String str1 = "";
for (int i = 0; i < 20000; i++) {
str1 = str1 + "a";
}
long t2 = System.currentTimeMillis();
System.out.println("testString cost =" + (t2 - t1) + " mils");

long t3 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < 20000; i++) {
sb.append("a");
}
long t4 = System.currentTimeMillis();
System.out.println("testStringBuilder cost =" + (t4 - t3) + " mils");

}
0 请登录后投票
   发表时间:2010-11-17  
ironsabre 写道
ouchxp 写道
dsjt 写道
ouchxp 写道

JDK自己已经有优化了. 不合理的优化反而会降低效率
比如字符串常量拼接
String str ="aaa" + "bbb" +"ccc" + "ddd";

StringBuilder sb = new StringBuilder();
sb.append("aaa");
sb.append("bbb");
sb.append("ccc");
sb.append("ddd");
String str = sb.toString();

哪种效率高? 答案是第一种. 对于新手来说想优化很容易适得其反.
反编译看一下就知道为什么了.


我们项目经理不让用第一种

如果你们的JDK版本在1.4或以下的话还说得过去.
如果是1.5或以上.
你完全可以和他提出来.你写一段测试代码给他看.执行效率有很明显的对比
反编译Class给他看.JDK已经做好优化了.自己的优化不仅多余,反而降低了执行效率


你试试我这段,再想想清楚。。
testString cost =453 mils
testStringBuilder cost =0 mils

public static void main(String[] args) {
long t1 = System.currentTimeMillis();
String str1 = "";
for (int i = 0; i < 20000; i++) {
str1 = str1 + "a";
}
long t2 = System.currentTimeMillis();
System.out.println("testString cost =" + (t2 - t1) + " mils");

long t3 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < 20000; i++) {
sb.append("a");
}
long t4 = System.currentTimeMillis();
System.out.println("testStringBuilder cost =" + (t4 - t3) + " mils");

}

你试试我这段, 再想想清楚.

我可没说循环也不用StringBuilder
	public static void main(String[] args) {
		long t1 = System.currentTimeMillis();
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < 1000000; i++) {
			//15个a
			String str = "a" + "a" + "a" + "a" + "a" + "a" + "a" + "a" + "a"
					+ "a" + "a" + "a" + "a" + "a" + "a";
			sb.append(str);
		}
		long t2 = System.currentTimeMillis();
		System.out.println("testString cost =" + (t2 - t1) + " mils");

		long t3 = System.currentTimeMillis();
		StringBuilder sb1 = new StringBuilder("");
		for (int i = 0; i < 1000000; i++) {
			sb1.append("a");//15个a
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
		}
		long t4 = System.currentTimeMillis();
		System.out.println("testStringBuilder cost =" + (t4 - t3) + " mils");
	}
0 请登录后投票
   发表时间:2010-11-17  
ouchxp 写道
ironsabre 写道
ouchxp 写道
dsjt 写道
ouchxp 写道

JDK自己已经有优化了. 不合理的优化反而会降低效率
比如字符串常量拼接
String str ="aaa" + "bbb" +"ccc" + "ddd";

StringBuilder sb = new StringBuilder();
sb.append("aaa");
sb.append("bbb");
sb.append("ccc");
sb.append("ddd");
String str = sb.toString();

哪种效率高? 答案是第一种. 对于新手来说想优化很容易适得其反.
反编译看一下就知道为什么了.


我们项目经理不让用第一种

如果你们的JDK版本在1.4或以下的话还说得过去.
如果是1.5或以上.
你完全可以和他提出来.你写一段测试代码给他看.执行效率有很明显的对比
反编译Class给他看.JDK已经做好优化了.自己的优化不仅多余,反而降低了执行效率


你试试我这段,再想想清楚。。
testString cost =453 mils
testStringBuilder cost =0 mils

public static void main(String[] args) {
long t1 = System.currentTimeMillis();
String str1 = "";
for (int i = 0; i < 20000; i++) {
str1 = str1 + "a";
}
long t2 = System.currentTimeMillis();
System.out.println("testString cost =" + (t2 - t1) + " mils");

long t3 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < 20000; i++) {
sb.append("a");
}
long t4 = System.currentTimeMillis();
System.out.println("testStringBuilder cost =" + (t4 - t3) + " mils");

}

你试试我这段, 再想想清楚.

我可没说循环也不用StringBuilder
	public static void main(String[] args) {
		long t1 = System.currentTimeMillis();
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < 1000000; i++) {
			//15个a
			String str = "a" + "a" + "a" + "a" + "a" + "a" + "a" + "a" + "a"
					+ "a" + "a" + "a" + "a" + "a" + "a";
			sb.append(str);
		}
		long t2 = System.currentTimeMillis();
		System.out.println("testString cost =" + (t2 - t1) + " mils");

		long t3 = System.currentTimeMillis();
		StringBuilder sb1 = new StringBuilder("");
		for (int i = 0; i < 1000000; i++) {
			sb1.append("a");//15个a
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
			sb1.append("a");
		}
		long t4 = System.currentTimeMillis();
		System.out.println("testStringBuilder cost =" + (t4 - t3) + " mils");
	}

这个测试用例是错误的,因为String a = "a"+"a"就是String a = "aa";
但是String a = b + c 就不一样了。
0 请登录后投票
   发表时间:2010-11-17  
string +的问题我想大家都很清楚了 就是生成了N多的string 对象,这个影响性能是必然的 ,千万别用 "AAAA"+"BBBB"这样的东西来验证它的性能,那是最不能看出结果的。


还有 楼上有位兄弟说了 JDK6 String+使用了sb,还既然他用了SB ,那么我直接用SB 实现,他怎么会比我的直接的快?他的算法比我的SB 算法高级?,有些东西只要用指头想想就明白了。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics