开发出高质量的系统
posted on 2007-02-11 03:26 dreamstone 阅读(2062) 评论(27) 编辑 收藏 所属分类: jdk相关
最开始的测试是在Eclipse中测试的,怕是eclipse的问题,在控制台下也做了测试,虽然得到的数字有微小偏差,但依然是方法一比方法二快1秒左右。问题继续。。 回复 更多评论
据说是因为局部变量放在堆栈中的原因。 回复 更多评论
在我的机器上测试,两者可以看作是一样快的。对每一个方法运行多次,结果也会稍有偏差,第一个方法:3375(次数比较多)、3390。 第二个方法:3375(次数比较多)、3390、3391、3406。 对于下面2段代码来说: for (int i=0;i<n;i++){ String str = // } String str = null; for(int i=0;i<n;i++){ str = // } 区别只是str变量的作用域不同---意味着:代码1的str变量的偏移位置在出了循环的作用域以后,可以被分配给下一个出现的局部变量,而代码2str的位置会一直占有,直到方法结束。 之所以,有人感觉代码2快,我想是一种错觉吧,就是以为代码1会在循环中重复声明变量str--实际上不是这样。 另外,JLS是什么东东? 回复 更多评论
to:JonneyQuest 详细说说? 回复 更多评论
to:daydream 你怎么运行的,不是两个一块调用的吧,如果是用循环多次调用求平均值,活着两个一块调用是不准的。如果你是循环调用,活着同时调用,你可以把两个函数的调用顺序换一下,会有较大的差距。 JLS=The Java Language Specification 讲述的是Java语言的特性,很多东西C++和Java是不同的,JLS中有描述。例如lazyloading的单态在Java中是不可实现的,这个就因为Java的优化造成的,通过JLS可以查到。 回复 更多评论
没有,我是分开运行的,直接运行你的代码。 回复 更多评论
这里问题大了。创建对象第一忌:不要在循环体中创建对象。这种做法会在内存中保存N份这个对象的引用会浪费大量的内存空间(虽说内存便宜,可以进行硬件升级),同时JVM的GC机制会因为这些无谓的对象做大量的回收工作,系统不慢都不行呀 ......................................................................................... 很简单的一个道理,你用String对象根本看不出效果。如果你换成个自定义对象或者图形对象呢?你的第一种做法会在堆内存中生成大量的垃圾对象,这些对象首先占用内存,二则在对于速度的影响上它不会马上体现出来(毕竟在内存够用的情况下无法体现),一旦堆内存中的eden area满了,GC机制开始起作用了那么你就会觉得你的程序速度狂降。。。。 所以说,第二种做法才是王道! 呵呵,刚刚写了一篇关于java优化编程的文字,希望可以提供帮助 http://www.blogjava.net/sinoly/archive/2007/02/11/99205.html 回复 更多评论
相对而言,性能的影响并不只是这段代码的执行速度。需要考虑在JVM种它的处理方式,以及这种方式对资源占用的情况。很多性能问题都是在日积月累中体现的。只是丂一条语句所谓的执行速度来判断效率,个人感觉很不合理 回复 更多评论
to sinoly : “创建对象第一忌:不要在循环体中创建对象。这种做法会在内存中保存N份这个对象的引用会浪费大量的内存空间” 这是误解。完全没有在内存中保存N份对象的引用,循环体内声明的对象也只是在java栈中占据一个位置。 反而在循环体内声明的对象因为其作用域只是在循环体内,更节约内存(虽然微乎其微)。 回复 更多评论
to:sinoly 我在平时写代码的时候也不是在循环体内创建,但是记忆中差别不大。 对于你的说法, 1,String看不出效果,自定义对象和图形对象能有效果? 在印象中无论是什么,这里保留的都是一个引用,应该是一样大的。所以应该没有对象和图形的差别。而且第一种做法不会产生垃圾对象,只会出现大量的引用。一个引用占用的内存是很小的,不会是大量的。但是如果循环次数很多,也是可观的,所以我平时也是写在循环体之外。 2,如果写在外边,其实并不一定就快,因为在里边写的话过了循环体就过了它的有效范围,可以被回收了,虽然并不一定立即回收,但如果第二种写法对象则不能回收。恰恰相反,如果对象很大,例如图形控件,活着保存大量数据的Bean,这个时候这个对象要到函数结束才会被回收,如果函数体很长,活着函数的执行时间很长,那么这个才是更消耗内存的。所以说哪种写法要看情况而定。 3,如何判定一个程序的好坏?这个是个综合问题,要考虑很多因素,但是在印象种无论如何方法二应该是比方法一快的,结果刚好相反,开启这个帖子主要是为了这个问题。就是为什么会这样?而不是讨论哪个方法更好。 回复 更多评论
java字节码中,对于每一个方法都有一个max_locals属性,指出方法的局部变量所需要的存储空间(以字为单位)。 对于一楼的例子,如果把 String str = // 移到循环体外,则max_locals会比在循环体内更大。 回复 更多评论
to:sinoly 看了你写的关于对象创建的问题,我们说的不是一个问题啊。 你的问题是:在循环体种创建相同的对象,就是作用一样的对象,这个当然是浪费内存了。这种问题不需要再讨论。 我的问题是这样的,并不再创建对象上,例如,如下问题,从List种取出对象,可以有两种写法, List<Object> list= //...一个已经存在的List 方法一 for(int i=0;i<list.size();i++){ Object obj = list.get(i); } 方法二 Object obj= null; for(int i=0;i<list.size();i++){ obj= list.get(i); } 这个里边根本没有创建对象的问题,有的问题是方法一会多很多引用,方法二会让一个引用保存期很长,同时对象有效期也变的很长。(其实你的性能优化的文章种应该指出这个问题的。) 另外提示一下,对于我第一个例子中写的: for (int i=0;i<n;i++){ String str = ""; } 在这个函数中只会创建一个对象,因为String是非可变对象,虚拟机会自动重用,这个你可以参照一下JLS中的解释。只有这种情况才是浪费 for (int i=0;i<n;i++){ String str = new String(""); } 最后感谢你参与,另外提一点建议: 1,回文或者写文章前应该先确认一下自己的观点是否是对的,最好给出证明,虽然确认了也不能保证一定是对的,但是至少做过了,这是一种态度。我也是一直这么要求自己,无论多么简单的问题,都给出一个思考的过程,因为这样对看文章的人有帮助。例如你上边说到的,如果是自定义对象活着图形对象的观点,刚好是错误的证明,你可以这样试试。虚拟机内存设置64M,如果你在List中取出一个60M的对象,然后在循环之后再new一个10M的对象,方法一是可以运行的,虽然说慢,但方法二就OutOfMemory了。除非你在循环之后设置 变量= null,但这种做法是否更好只得商榷. 最后说明一点,应用开发和底层框架开发其实有很多东西是不同的。如果实际负责过项目就了解的。 回复 更多评论
@daydream 谢谢再次回复,想问一下关于max_locals这个属性, 1,为什么放到循环体外反而会更大呢?能给简单讲一下为什么吗?活着给一个能查到原因的方向。 2,另外这个max_locals变大后为什么会影响性能呢?在什么时候会使用到max_locals这个属性呢? 谢谢 回复 更多评论
对下面2个方法,foo1需要的max_locals是4,foo2是5, foo1需要的4大概是:this变量占1个字、方法参数x占1个字、 第一段循环的时候,变量i占一个字、s1占一个字,第二个循环的时候,i、s1已经超出作用域,所以,变量j、s2占用了和i、s1重叠的空间,所以最多需要4个字就够了。 foo2方法中s1的作用域直到方法结束,所以需要5个字长度。 max_locals变大后应该不会影响到性能,但是我这儿的意思是说,将局部变量放在循环体内声明并不会导致性能下降。 void foo1(int x) { for (int i = 0; i < 1000; i++) { String s1 = "..."; } for (int j = 0; j < 1000; j++) { String s2 = "...."; } } void foo2(int x) { String s1 = "..."; for (int i = 0; i < 1000; i++) { // other code..... } for (int j = 0; j < 1000; j++) { String s2 = "...."; } } 回复 更多评论
另外,将局部变量放在循环体内声明,也不会导致多出来很多引用。 因为,局部变量对应于java栈的偏移是在编译时就确定的,并不是在运行期动态分配的。循环体内的局部变量对应的是同一个偏移位置。 回复 更多评论
另外,max_locals只是编译器在编译时确定,存放在字节码中,供JVM在运行期调用方法时分配java栈帧大小用的,对于程序员应该没什么用,因为程序也没办法访问java栈。 回复 更多评论
现在大概明白了max_locals的作用,但是我这测试出的问题还在。不知道为什么第一个方法要比第二个方法快。 回复 更多评论
另外我看你上边写的用我的代码,测试结果是接近的,可我怎么测都是差距一秒啊,你的运行环境 ? 我是xp下,试过eclipse运行,试过直接控制台用java命令运行。 jdk1.5 回复 更多评论
运行你的第二段测试代码: 先运行test1共5次,结果:3406、3453、3391、3453、3391 然后把代码改成test2,运行5次,结果: 3406、3406、3406、3437、3391 运行环境:XP、512M内存、Eclipse3.2下,JDK6.0,没有加启动参数,默认最大内存好像是64M。 回复 更多评论
郁闷了,我的两台电脑,测试的结果都是稳定的差不到1秒 xp sp2 eclipse3.2.1 jdk1.5 启动参数也没加,最大内存开始是512,后来改成256和64都试了。 结果test1:2938 2953 2954 2954 2969 test2:3797 3797 3781 4000 3797 3797 我今天再找别人试一下,看看什么情况。 回复 更多评论
又找了几个同事帮忙测试了一下,果然,有的差距是100毫秒,有的200,有的基本没差距。我公司的电脑差距是200,也就是说我家里的测试不准确,真实不可思议,我在家测了很多次,都稳定在差距1000毫秒,呵呵。不过问题总算解决了。 另外经过测试发现在这个问题上,amd的cpu比intel的快,单核的比双核的快。。有意思。 回复 更多评论
注意jdk版本,一些基本的东西随着JDK不断的更新,都会有改变。对JAVA没什么好印象! 回复 更多评论
有些是跟版本有关的,这个跟版本没关系,呵呵。 为什么对java印象不好呢,每个语言都有它的好处和坏处。 回复 更多评论
刚才又做了个测试,一个很有意思的结果:使用ibm的ibm_sdk50测试结果刚好相反,执行10亿次,方法二比方法一快了200毫秒。挺有意思。 不过结论是一样的,就是两种方法性能差别很小,但方法二让对象的有效期变长了,如果是大对象(例如图形对象,数据bean对象)则不好,所以一般情况下应选择方法一的写法。或者方法二的写法加上手动清空释放对象。 回复 更多评论
牛啊! 俺可很少关注到这一层。俺通常来说会用第一种的。可以省下俺4300ms写代码的时间。 回复 更多评论
我在你的代码中分别添加了如下代码: public static void test1(long n){ for (int i=0;i<n;i++){ String str = ""; } System.gc(); } public static void test2(long n){ String str = null; for (int i=0;i<n;i++){ str = ""; } System.gc(); } 测试结果分别为: test1: 3787, 3710, 3756, 3616, 3523 test2: 3538, 3678, 3507, 3756, 3460 我的观点与sinoly一致! 回复 更多评论
@coffey 你一定是没看完评论,可以看看我回复sinoly的评论,你就知道为什么了。 很多东西不要想当然,要测试、分析。只有理论上和实践上都通过才是正确地。否则一定是有错误的地方。 回复 更多评论
Powered by: BlogJava Copyright © dreamstone