Object s;
for(int i=0;i<10;i++)
{
    s = new String("sss");
}
for(int i=0;i<10;i++)
{
    Object s = new String("sss");
}一种是循环外定义变量,一种是循环内定义变量,这两种方式到底有什么概念的性能差异循环外定义变量可以避免在循环内一次次的声明实例引用,但是要等到方法结束才释放最后指向的实例。也就是说在方法结束前总有一个实例是不被回收的。循环内定义变量可以在每一次循环结束通知GC释放实例两种写法的对实例的回收次数都是一样的,关键是回收时机和引用的声明次数。

解决方案 »

  1.   

    倾向于
    for (;;) {
    Object ...
    }
    在乎的不是GC不GC的,而在乎于隔离两次循环之间变量,使之不会冲突或者彼此干涉
      

  2.   

    class Test
    {
    public static void f1()
    {
    Object s;
    for(int i=0;i<10;i++)
    {
    s = new String("sss");
    }
    } public static void f2()
    {
    for(int i=0;i<10;i++)
    {
    Object s = new String("sss");
    }
    }
    }
    以上两个方法编译出来的字节码完全一样,都是public static void f1();
      Code:
       0:   iconst_0
       1:   istore_1
       2:   iload_1
       3:   bipush  10
       5:   if_icmpge       24
       8:   new     #2; //class java/lang/String
       11:  dup
       12:  ldc     #3; //String sss
       14:  invokespecial   #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V
       17:  astore_0
       18:  iinc    1, 1
       21:  goto    2
       24:  returnpublic static void f2();
      Code:
       0:   iconst_0
       1:   istore_0
       2:   iload_0
       3:   bipush  10
       5:   if_icmpge       24
       8:   new     #2; //class java/lang/String
       11:  dup
       12:  ldc     #3; //String sss
       14:  invokespecial   #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V
       17:  astore_1
       18:  iinc    0, 1
       21:  goto    2
       24:  return}
    相反变量定义在内层更能及时的被回收,每次循环结束即可以让GC回收。
      

  3.   

    个人认为:一种是循环外定义变量:当for循环结束时,还有一个没有被gc回收一种是循环内定义变量:当for循环结束时,gc全部被回收这个就是最主要的差异愚见,莫见怪!
      

  4.   

    那么在循环之后显式s=null;会怎样呢?gc不是随时待命的,一般只有内存不够或者函数调用结束时才会去操作吧。好像System.gc();也不能保证gc操作,只是提示gc个人认为还是有一点点影响吧,写个函数测一测不就解决了
      

  5.   

    其实大可不必讨论这么多的,因为上面f1和f2方法编译出来的字节码是完全一致的,既然JVM只认字节码,那么效果上也应该是一样的,只需要注意作用域本该在循环内部,就该写在里面。
      

  6.   

    有时需要break的地方,还是不得不放在外面。不同的javac字节码会不会有所不同阿
      

  7.   

    不要刻意追求GC,那个是保不齐的。
    首先要与己方便,然后才能与人方便。需要在循环外面用到的,那就放在外面,否则一律应放入循环体内。
    有时候,刻意优化,反而效果更差。比如,我经常看到
    String sql = 
    " SELECT A.X X, " +
    "        B.Y Y, " +
    ......
    "   FROM TBL_A A, " +
    "        TBL_B B " +
    "  WHERE ....... "; // 二三十行,近千个静态字符
    很多人只知道拼接String应当用StringBuffer,比如
    StringBuffer buff = new StringBuffer();
    buff.append(" SELECT A.X X, ");
    buff.append(" ...... ");
    ....
    String sql = buff.toString();
    自以为优化了代码,却不知道这样子内存开销更大,效率更差。因为中间有几个append就出现几个中间的String,同时StringBuffer的capacity还要从默认的16,扩容好几次才能上千。而用+,由于中间全是静态的,编译器会自动优化成一个String。同样,变量声明在for内外,对于GC来说,根本没有太大区别。先不论gc时机和具体回收的堆内容的不确定性,就光看栈引用和堆对象的关系而言,两种写法都只会新创建N个堆对象,同时,任意时刻,都只有一个栈引用一个堆对象。除了循环之后,最后一个有些许差别外,其余,根本看不出任何在“性能”上,某种写法压倒另外一种写法的理由。
      

  8.   

    1:
    Object o;
    for(;;){
     o=new Object();
    }2:
    for(;;){
     Object o=new Object();
    }第一种方法变量o只生成一次
    第二种方法,每次都回生成变量o虽然没有创建对象,但生成一个变量,系统仍然要分配内存空间的,所以第一种效率高但是现在的编译器都是带优化的,编译的时候一优化就一样了.无所谓的.
      

  9.   

    Object s;
    for(int i=0;i<10;i++)
    {
        s = new String("sss");
    }
    for(int i=0;i<10;i++)
    {
        Object s = new String("sss");
    }放在外面是N个s,一一对应地引用N个String实例;
    放在里面是1个s,一一引用N个String实例;
    放在外面,创建String实例的过程一样,回收的过程不详(java我不懂)
    放在里面,创建String实例的过程一样,回收的过程不详(java我不懂)
    放在里面外面,只是多个s和一个s的不同,在罗辑上是这样,实际过程,不详,因为我不懂,我真的不懂.
      

  10.   

    “不要在循环体内定义变量”这句本来就有问题
    比如你要用个循环把一个对象添加到list,那么有时候就不得不定义了
      

  11.   

    放在外面是N个s,一一对应地引用N个String实例;
    放在里面是1个s,一一引用N个String实例;
    ??????????????
      

  12.   

    1.代码编译后的结果是一致的,运行时的行为也是一致的,gc不保证什么时候回收。
    2.在循环体外声明一个只有循环体内才使用的变量是一个不良习惯,这是典型老式C语言的编程风格,就像在C语言中滥用全局变量一样,这样会扰乱上下文变量的定义和语义,造成程序本身的不可读性。
    3.非持久性状态变量声明尽量局部化,这是模块化和面向对象思想的一般准则。
      

  13.   

    1、随着硬件水平的提高,大多数场合和评价一般代码的标准不再是效率,而是清晰、易懂。
    2、推荐“不要在循环体内定义变量”是一条广泛和一般的准则。如果是一个完全正确的程序员,可以违反这个规则。正如同有人用汇编写大型系统。但毕竟是少数人员,出错的概率也太大。对于一般人,清晰的变量定义、变量内存申请、变量回收是非常需要考虑的。
    3、循环体内定义、回收是可以的,但是面对复杂的程序,过多的BREAK和CONTINUE, 再加上与变量定义、变量内存申请、变量回收搅在一起无疑会增加程序的复杂度。
    4、从普遍意义上来说,“不要在循环体内定义变量”是一条工程化和软件化的规则,当然高手不受此限制。
      

  14.   

    1:
    Object o;
    for(;;){
     o=new Object();
    }2:
    for(;;){
     Object o=new Object();
    }第一种方法变量o只生成一次
    第二种方法,每次都回生成变量o^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    应该不是吧   记的学C的时候  书上讲的:
    for( ...)
    {
       int i=5;   //只在第一次循环才声明(C专家编程里的,就是书皮有条很丑的鱼那本)
    }
      

  15.   

    同意kaper3的说法for( ...)
    {
       int i=5;   //只在第一次循环才声明
    }试想一下,如果想当然的认为是每次循环都会定义,那么把这个循环展开就会是
    int i=5;
    int i=5;
    ......
    显示是不能为编译器接受的。
      

  16.   

    在VC里调试
    void main()
    {
    for(int i=0;i<5;i++)
    {
    int* num = new int;
    }
    }
    可以发现每次循环时 &num是同一个值,指针本身这个32位整数值是不变的。
      

  17.   

    我觉得这只是一个习惯.心理上感觉很好.以前总感觉声明在循环体内会增加内存,但刚刚试了一下,没有,就只有hashCode不一样.运行时间几乎没什么不同.
      

  18.   

    1如果是只作用在循环里,请在里面定义,循环结束后就被回收了,如果循环结束后别的地方仍要用到这个值,请在外面定义。
    2NEW的用途,我听别人说好象NEW出来的东西需要自己手工回收的,可能系统也可以自动回收,但不管怎么样,每NEW一次就会新建一个对象,然后改变引用转变到新的变量的地址上,然后原来的对象没有被回收,只有在循环结束后才被统一回收,那么那么在循环期间循环次数越多,生成的对象也就越多,这样会占用系统资源,导致速度下降。
      

  19.   

    to 楼上,所说new的用途与变量定义在循环外还是内无关,因为无论是声明在哪里,每次new都是一样的要新建一个对象,都是要把引用转变到新的变量地址上都会在结束单趟循环后标识JVM可以释放刚才的对象
      

  20.   

    如果不是一个String的对象,在循环外定义会不会好一点,这样就不用每一次循环都NEW一个新的对象了。
      

  21.   

    定义生成对象编译器系统都要执行一定的操作,当然是放循环外面好一点,少执行一次也是效率。但是有的后放外面代码实在太难看可以在效率和编码可读性之间抉择一下。
    另外java编译器是有一定的优化功能的,不过一方面并不能叫准他的优化规则,另外一方面过分依赖编译器的优化能力也是一个很危险的事情。
      

  22.   

    同意 bluesmile979(笑着)  的观点.
      

  23.   

    楼主的例子有问题,真正做程序的时候很少有那样的情况。
    一般的情况是:
    Object o = null;// 假设list里面有几个对象;
    for (int i = 0; i < list.size(); i++) {
        o = (Object)list.get(i);
    }
    如果定义到里面每次循环结束的时候都要把o(Object)完全回收;不同的虚拟机回收机制不同,这样有可能影响到代码的运行效率。而定义到外面,每次循环结束不需要调用回收机制从而提高运行效率。一般比较大的程序,比如银行,保险业数据比较庞大,即使是一点点的效率提高也会被非常明显的表现出来。
      

  24.   

    我说的是一般很少再循环内部 new 一个对象,一般是引用已经存在的对象。
      

  25.   

    应该是不要在循环体内定义句柄。new 还是要在循环里面new 的。不然程序就出错了,程序要是都错了,效率再高有什么用啊?
      

  26.   

    用c/c++开发的程序员在开发过程中需要非常注意变量或者对象的作用域和生命周期,因为需要自己来管理内存的分配,而java是由虚拟机来管理的,在定义对象或者变量时,我个人认为也应该从这两方面来考虑!
      

  27.   

    to shanbingch(男人) 
    无论是在循环内还是循环外定义,只要是结束当前循环后,前一个变量就成了游离的对象,可以被GC清理掉的。
      

  28.   

    1、在编写大型程序时,如果存在大量循环体内的变量定义,则阅读和维护起来将会更麻烦一点。2、从直觉上的代码效率(既然很多朋友这么愿意讨论这个问题,干嘛不实际测试一下,纸上谈兵百无一用。我不测试,因为有时间还得做比这个重要的事)看,肯定循环体内反复定义是很差劲的,除非编译器在这方面作了优化,使其和在循环体外定义的结果是一样的,但那样也就没有讨论必要了。我从开始写程序就喜欢把一个函数过程中所有用到的变量声明语句写在执行语句前面(这点在DELPHI中有很好的体现。如同做菜的菜板,生熟应该分开,代码一团乱,不干不净,吃了有病),并加注释。后来看到关于代码规范的资料,也是这样说的。
      

  29.   

    1、在编写大型程序时,如果存在大量循环体内的变量定义,则阅读和维护起来将会更麻烦一点。
    =============
    非也!OOP提倡封装,把局部使用的变量尽可能放到小的范围中是一种美德,而不会给阅读和维护带来麻烦。2、从直觉上的代码效率(既然很多朋友这么愿意讨论这个问题,干嘛不实际测试一下,纸上谈兵百无一用。我不测试,因为有时间还得做比这个重要的事)看,肯定循环体内反复定义是很差劲的,除非编译器在这方面作了优化,使其和在循环体外定义的结果是一样的,但那样也就没有讨论必要了。
    ==========
    我认为就题目上面给出的代码,没有效率上的差别,即使有,也非常非常微弱,不足挂齿。
    我从开始写程序就喜欢把一个函数过程中所有用到的变量声明语句写在执行语句前面(这点在DELPHI中有很好的体现。如同做菜的菜板,生熟应该分开,代码一团乱,不干不净,吃了有病),并加注释。后来看到关于代码规范的资料,也是这样说的。
    ============
    其实规范也是相对而言的,我管理的项目中,都要求变量和注释放在用到的地方,很明显这比你翻屏幕来得方便些。
      

  30.   

    至于 -- 为什么有人推荐“不要在循环体内定义变量” 
    我想一定是某个人搞错了,可能想说的是“尽量不要在循环体内new对象”,这就很好理解了,因为这样会导致效率问题。