大家如果有什么不明白,可以去看看http://java.sun.com/docs/books/vmspec/index.html

解决方案 »

  1.   

    大家如果有熟悉ASM或编译原理,但是不是很熟悉Java VM的,可以看看这个,很容易的
    http://java.sun.com/docs/books/vmspec/index.html
      

  2.   

    两段程序的执行效率一样,都是将变量的定义放在循环中处理,基本同意楼主的看法。Sun JDK的Java编译器做了足够的优化,甚至自动将以上两段程序中的i变量转换成了byte型,以优化执行。一点拙见,不敢要分。
      

  3.   

    不是拙见,不是拙见,还好,有人来看看
    不过i变量转换成了byte型吗?
      

  4.   

    呵呵,我觉得不一样,至少GC认为不是一样的,你多new几个看看就知道了,new一万个看看,那个运行的时间多就可以来
      

  5.   

    测试以下:public class Sam1 {
    public static void main(String[] args) {
    long startTime = new java.util.Date().getTime(); for(long i = 0; i < 10000000; i++) {
    int j = 9;
    }
    long endTime = new java.util.Date().getTime();
    System.out.println(endTime - startTime);
    }
    }public class Sam2 {
    public static void main(String[] args) {
    long startTime = new java.util.Date().getTime();
                      int j;
    for(long i = 0; i < 10000000; i++) {
        j = 9;
    }
    long endTime = new java.util.Date().getTime();
    System.out.println(endTime - startTime);
    }
    }
      

  6.   

    我想应该是一样的,因为{}里面的变量有作用域的问题,
    不知道cxj_2000(多多呢?多多呢?啊!!!¥%#%……※) 兄的测试结果是什么,
    测试结果如下,令人大跌眼睛:
    多数情况下,前者与后者是一样的,结果为90,但是1/5的情况是,前者为90,后者为80,why??????????????
    不要说,因为,前者在前的原因,我换了位置,结果也一样,呵呵。        long startTime = new java.util.Date().getTime();
            int j;
    for(long i = 0; i < 10000000; i++) {
        j = 9;
    }
    long endTime = new java.util.Date().getTime();
    System.out.println(endTime - startTime); startTime = new java.util.Date().getTime();
    for(long i = 0; i < 10000000; i++) {
        int k = 9;
    }
    endTime = new java.util.Date().getTime();
    System.out.println(endTime - startTime);
      

  7.   

    java 因为语言提供了gc,所以分配内存都是在heap里面,按理说差别还是比较明显的。
      

  8.   

    测试结果确实有些偏差的,后者有的时候会时间短一些,虽然这种情况很少.why?
      

  9.   

    拜托大家不要总看源代码,看看反编译出来的伪字节码,JVM解释的是可执行字节码,不是源代码!
    Tmsheng(小唐蔡)有一点我我不同意,编译器在token analyze的过程中就对所有的变量,不管是这个变量在什么作用域,而且学过编译的人都应该明白,作用域在可执行文件里是没有体现的,可执行文件里只有address, instructions(operators), operrand.这是不会“新开辟内存”的,因为i=9,9是constants, 所以9应该出现在类似汇编语言的数据段(data segment), 在Java中被称作常量池(constant pool).以下是Java VM specification 的原文4.4 The Constant Pool
    Java virtual machine instructions do not rely on the runtime layout of classes, interfaces, class instances, or arrays. Instead, instructions refer to symbolic information in the constant_pool table. 
    All constant_pool table entries have the following general format:
        cp_info {
         u1 tag;
         u1 info[];
        }Each item in the constant_pool table must begin with a 1-byte tag indicating the kind of cp_info entry. The contents of the info array vary with the value of tag. The valid tags and their values are listed in Table 4.3. Each tag byte must be followed by two or more bytes giving information about the specific constant. The format of the additional information varies with the tag value.
    Constant Type  Value  
    CONSTANT_Class  7  
    CONSTANT_Fieldref  9  
    CONSTANT_Methodref  10  
    CONSTANT_InterfaceMethodref  11  
    CONSTANT_String  8  
    CONSTANT_Integer  3  
    CONSTANT_Float  4  
    CONSTANT_Long  5  
    CONSTANT_Double  6  
    CONSTANT_NameAndType  12  
    CONSTANT_Utf8  1  这和Wirh教授的PL/0的处理方法本质上一样的,就算Java再先进,Java Compiler 还是符合传统编译原理的范畴的。
    所以我还是认为,两个的效率一样。不知各位高手如何认为。
      

  10.   

    我只从语法的角度说:
    第一种:
    class Def1
    {
        public static void main(String s[])
        {
            int i;这时并没有给i 分配任何内存.
            for(int k=1; k<5; k++){
                i=9;.......i被正确的初始化了在内存中有了一个确定的位置,在循环中反复的对i进行操作。
            }
        }
    }class Def2
    {
        public static void main(String s[])
        {
            for(int k=1; k<5; k++){
                int i=9;--------初始化了变量i,在循环中反复的对i进行操作。
            }
        }
    }在两个程序中i 都是局部变量。都对应着内存中得一个地址。都是在循环中反复对那个地址进行的操作而已。在java中对于变量,如果没有显示的初始化,就不会给其分配内存。
      

  11.   

    cxj_2000也不对
    GC不会对constants动手的!“你多new几个看看就知道了,new一万个看看,那个运行的时间多就可以来”,这里哪有new,都是直接把constant pool里的常量地址赋给i.和garbage collector没有关系
      

  12.   

    我个人认为效率一样,
    大家如果要测,应该吧k 放大 (int k=0;k<1000000;k++)来测效果才会明显。
      

  13.   

    哎,又是这种幼稚问题。告诉你这两个有差别的那个人绝对是个菜鸟!
    这个i运行时很有可能是个寄存器,有个屁区别!
    除非编译器比着看谁更傻得有创意。:)不过,从代码风格上看,第二个好。基本原则是:
    尽量缩小你变量的生存期,一旦不用了,就让它go-out-of-scope.
      

  14.   

    看看这个吧:
    http://www.csdn.net/expert/topic/876/876923.xml?temp=.6928675
      

  15.   

    to:DanielYWoo(小丹尼) java中一切都是对象,对吧,i是不是对象?是吧,是的话他是怎么出来的?不是new出来的吗?虽然你没有看到new,呵呵
      

  16.   

    对于原始类型这两种差别不大,效率差不多
    但是换引用类型试试,差别很明显
    你们看看这三种定义方式的运行结果:
    public class Sam1 {
    public static void main(String[] args) {
    long startTime = new java.util.Date().getTime(); for(long i = 0; i < 10000000; i++) {
    String j = new String();
    j = "";
    }
    long endTime = new java.util.Date().getTime();
    System.out.println(endTime - startTime);
    }
    }
    public class Sam2 {
    public static void main(String[] args) {
    long startTime = new java.util.Date().getTime();
                      String j = new String();
    for(long i = 0; i < 10000000; i++) {
        j = "";
    }
    long endTime = new java.util.Date().getTime();
    System.out.println(endTime - startTime);
    }
    }
    public class Sam3 {
    public static void main(String[] args) {
    long startTime = new java.util.Date().getTime();
                      
    for(long i = 0; i < 10000000; i++) {
        String j  = "";
    }
    long endTime = new java.util.Date().getTime();
    System.out.println(endTime - startTime);
    }
    }
      

  17.   

    fandiy(继文),要么是你看错了,要么是你不懂编译原理,我是指这样两种  String j;
      for(long i = 0; i < 10000000; i++) {
          j = new String();
          j = "";
      }     for(long i = 0; i < 10000000; i++) {
          String j = new String();
          j = "";
      } 争论的关键在于String j放在里面和外面时,j的内存存储空间(指针本身的分配)是在编译时决定且被分配一次,还是运行时决定且被分配多次。我不是要问j所指的对象地址的内存分配次数,你先看看我上面的帖子和回复反正,我认为应该很明显j当然当然在编译时确定类型,确定地址分配的
      

  18.   

    to: cxj_2000
    原文:java中一切都是对象,对吧,i是不是对象?是吧,是的话他是怎么出来的?不是new出来的吗?虽然你没有看到new,呵呵我们现在在考虑编译原理,不是OOP,在中间码上还能看出作用域等问题,在机器码上哪里有对象呀首先int j 是 primitive,而非Object子类,编译器底层根本不能调用int的构造函数,你见过invokespecial #2 (int) 这样的伪代码调用int的构造函数么?其次,再次再次再次再次再次再次再次再次再次再次再次请求大家看反编译伪码,你们看源代码能看出来JVM指令么?
      

  19.   

    不知道你有没有试过
    Sam2和Sam3的效率等同,却与Sam1有很大的效率差异。
    但有一点你得明白,变量显示初始化能更好的提高效率
      

  20.   

    to:DanielYWoo(小丹尼) 
    你先看看结果,再考虑编译原理
    不要死搬教条。
    你的那两种new String()方法,效率没有太大的区别
    你能比较出什么结果来?
      

  21.   

    你所分析的只是你的Code,
    你所运行的是编译过的机器Code,
    你只能了解怎样写的代码效率更高,
    没有结果的东西再怎么分析也一样,
    除非你去分析机器Code的执行效率。
      

  22.   

    简单变量和object是不一样的。个人倾向于第一种写法,另外,System.gc()不要轻易调用。
      

  23.   

    to fandiy(继文):
    我分析的不是code,是executable byte code, understand?
    You should see this http://java.sun.com/docs/books/vmspec/index.html
    You should understand the underlying mechanism and known how JVM executable the executable byte-code, and then express your opinion.
    Pls, don't express your opinion when you absolutely do NOT know what we are talking about.争论的关键在于String j放在里面和外面时,j的内存存储空间(指针本身的分配)是在编译时决定且被分配一次,还是运行时决定且被分配多次。我不是要问j所指的对象地址的内存分配次数.你看明白我要讨论什么了么?  String j;
      for(long i = 0; i < 10000000; i++) {
          j = new String();
          j = "";
      }     for(long i = 0; i < 10000000; i++) {
          String j = new String();
          j = "";
      }
      

  24.   


     自己试一下,看一下结果,想想再说。
      String j;
      for(long i = 0; i < 10000000; i++) {
          j = new String();
          j = "";
      }     for(long i = 0; i < 10000000; i++) {
          String j = new String();
          j = "";
      }
      你这两个没什么区别
      你再看看这个
      String j = new String();
      for(long i = 0; i < 10000000; i++) {
          j = "";
      }
      你选择用哪个?
      

  25.   

    看来只有 ajoo(聪明的一猪) 最明白了,等到揭帖了,我至少给你300,嗳,知音难寻呀
    谢谢 yayv(yayv),sunni(死猪),Mars_Mao(战神),ilovehuhu(木头) , signboy(横)的支持to signboy(横):
    顺便提一下,signboy(横)测试出来的结果有误差是因为
    1。目前JavaVM在普通机器上只能精确到10毫秒,JavaVM在某些计算机上可以精确到纳秒,JDK中提供了这种方法,但是在实际中很少有机器能够精确到纳秒,所以没太大用,你的结果如果有10毫秒的误差,那是可以忽略的。
    2。JavaVM的garbage collector运行于最低优先等级的线程,就算你调用了Runtime.gc(),系统也不知道什么时候真正去回收内存。而且目前JDK1.4不提供改变gc优先级的方法,而且将来可能也不会提供。gc具体什么时候回收是不可预知的,你永远不可能用Runtiem.freeMemory获得精确的内存情况,这个返回值只是一个approximate, 如果返回值误差不是非常明显的话,是不能用来证明谁更省内存的。cxj_2000和piggybank(吞硬币的小猪)先看清楚了再说
    且他人也不要发表没有论据的观点,先看看反编译出来什么再说
    如果不懂,不要装懂,不要从表面的源代码级别上衡量JVM底层的工作。
    先明白汇编语言再来看这个问题但我仍然欢迎各位懂汇编语言,懂编译原理,对这个问题有兴趣的朋友来贴
    Thanks
      

  26.   

    大家可以参考参考ajoo(聪明的一猪)提供的http://www.csdn.net/expert/topic/876/876923.xml?temp=.6928675
    很不错
      

  27.   

    fandiy(继文) 
    你没看懂我们在讨论什么,所以非要把new String拿出来,我们比的是
      String j;
      for(long i = 0; i < 10000000; i++) {
          j = new String("Fool");
      }

        for(long i = 0; i < 10000000; i++) {
          String j = new String("Fool");
      } 你太可笑了,我懒得理你了。
    不懂编译的别瞎跟贴
      

  28.   

    to DanielYWoo(小丹尼):
    我觉得你说话太狂了点,你懂多少编译原理???
    你看看你的题目:“代码效率问题”,可不就是代码效率问题么?何必又说人家Fool,又说人家可笑呢?我觉得你自己真是浪费时间,有这个功夫自己比较一下两种方法的效率不就得了!!!搞笑!!!
      

  29.   

    to lfgoal(浪子)
    我接受你的批评
    因为这个贴字跟出来很多跑题的问题,40回复中只有1/4每跑题,结果后来已经在谈别的问题了,所以我有点激动,请大家不要跑题,看看前面一些的跟帖在这里向 fandiy(继文) 表示歉意不过也请多提出你对上面两个程序编译结果及VM解释过程的看法。
    Thanks
      

  30.   

    同意楼上,
    请教问题要谦虚点,你编译原理得几分呀?
    你要是真懂编译原理,那么把JVM的编译过程研究一下不就可以了,
    本来大家想发表点意见,看你这种态度,还是算了吧,懒得理你!
      

  31.   

    to Joeblackyang(做IT做得身心疲惫)
    首先,我接受你的批评
    其次,我编译得了92分JVM的编译过程和PL/0等语言差不多
    反编译代码已经摆得很清楚了,除了变量i,k以为词法扫描时
    出现的次序而造成的机内表示不同之外,没有差别。如果谁能解释下面两段有什么执行差别的,请回贴
    但我认为,没人能找得出来,已经很明显了,是一样的,
    我只是想来这里验证一下,和志同道合者讨论一下   0 iconst_1        // Push int constant 1 into stack
       1 istore_2        // Store into local variable 2 (k=1)
       2 goto 11        // First time through don't increment
       5 bipush 9       // Push int constant 9 into stack
       7 istore_1       // Store into local variable 1 (i=9)
       8 iinc 2 1        // Increment local variable 2 by 1 (k++)
      11 iload_2        // Push local variable 2 (k) into stack 
      12 iconst_5      // Push int constant 5 into stack for comparing
      13 if_icmplt 5   // Compare and loop if less than (k < 5)
      16 return         // Return void when done

    0 iconst_1         // Push int constant 1 into stack
       1 istore_1         // Store into local variable 1 (k=1)
       2 goto 11         // First time through don't increment
       5 bipush 9        // Push int constant 9 into stack
       7 istore_2         // Store into local variable 2 (i=9)
       8 iinc 1 1         // Increment local variable 1 by 1 (k++)
      11 iload_1         // Push local variable 1 (k) into stack
      12 iconst_5       // Push int constant 5 into stack
      13 if_icmplt 5    // Compare and loop if less than (k < 5)
      16 return
      

  32.   

    first:晕倒了,javac编译的是字节码,又不是机器码,对吧,你这样编译来反编译来能看出什么?效率要看机器码才对啊。second:你的题目是考虑效率的问题,而不是什么编译不编译,从你给的代码可以看出是两种不同的方法做同一种事,是看看那种方法做的效率高,其实你看代码就可以看出来的,何必要去编译反编译呢?firth:你找台机器运行下你写的程序就知道那个效率高,那个效率低了。再问问小丹尼:你现在讨论的是效率还是编译呢?假如是效率的话,你的方法(看反编译出来的程序)就是错误了。假如是编译的话,就当我说废话。
    new 5次跟new一次没有差别吗?
      

  33.   

    都是new5次  String j;
      for(long i = 0; i < 10000000; i++) {
          j = new String("Fool");
      }

        for(long i = 0; i < 10000000; i++) {
          String j = new String("Fool");
      }
      

  34.   

    对不起,忘了改Fool成其他词了
      

  35.   

    看看聪明的猪的观点,我赞同这个i运行时很有可能是个寄存器,有个屁区别!
    除非编译器比着看谁更傻得有创意。:)不过,从代码风格上看,第二个好。基本原则是:
    尽量缩小你变量的生存期,一旦不用了,就让它go-out-of-scope.