锁定老帖子 主题:关于性能优化
精华帖 (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"); } |
|
返回顶楼 | |
发表时间:2010-11-17
最后修改:2010-11-17
|
|
返回顶楼 | |
发表时间: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的 还是属于泄漏的呢? |
|
返回顶楼 | |
发表时间: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里的值呢。 这个。。。。 |
|
返回顶楼 | |
发表时间:2010-11-17
性能这个问题 从设计到开发到测试都是需要考虑和验证的,不是说开发完了 通过测试来找问题,LZ的观点偏了
|
|
返回顶楼 | |
发表时间:2010-11-17
现在设我是被Cache住的一个小对象,我就是这个对象,那么我的身份怎么定义?
A) 我是一个Cache住的有用的对象,我能提高访问效率。 B) 我是一个被泄漏掉的垃圾,我是垃圾。 我是A还是B,谁说了算,按什么标准执行? |
|
返回顶楼 | |
发表时间: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"); } |
|
返回顶楼 | |
发表时间: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"); } |
|
返回顶楼 | |
发表时间: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 就不一样了。 |
|
返回顶楼 | |
发表时间:2010-11-17
string +的问题我想大家都很清楚了 就是生成了N多的string 对象,这个影响性能是必然的 ,千万别用 "AAAA"+"BBBB"这样的东西来验证它的性能,那是最不能看出结果的。
还有 楼上有位兄弟说了 JDK6 String+使用了sb,还既然他用了SB ,那么我直接用SB 实现,他怎么会比我的直接的快?他的算法比我的SB 算法高级?,有些东西只要用指头想想就明白了。 |
|
返回顶楼 | |