java声明一个变量的开销大吗?

幻悟 2015-07-28 11:04:25
一直很纠结这个问题
Object o=null;
for(){
o=Collection.get(i);
......
}
还是这样
for(){
Object o=Collection.get(i);
......
}
求告知
...全文
553 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
老李家的小二 2015-07-29
  • 打赏
  • 举报
回复
其实就是个人习惯问题,偶尔也会有喜新厌旧的赶脚
落落叶叶无声 2015-07-28
  • 打赏
  • 举报
回复
引用 1 楼 skgary 的回复:
对于绝大多数程序而言,纠结这些意义不大。 当然,习惯性用上面那种会好一些。
我觉得作为一个程序员纠结这个有必要 性能上,上面那种效率高些 如果循环次数很大,上万百万的话,无疑会加重虚拟机的压力 因为每次循环虚拟机都要开辟空间,还要用GC回收o对象,消耗了性能。
skgary 2015-07-28
  • 打赏
  • 举报
回复
对于绝大多数程序而言,纠结这些意义不大。 当然,习惯性用上面那种会好一些。
sxiaobei 2015-07-28
  • 打赏
  • 举报
回复
您好,其实有时候编译器也不能很细化的看问题,这和java中对局部变量的存储方式有关的,在java当中,局部变量不管是引用还是基本数据类型,都是存储在栈帧当中的局部变量表中,在局部变量表中,用变量槽(slot)来存储局部变量,一个slot可以存储一个32位数数据类型,像是你上面所说的情况,一个引用就可以放在一个solt中,slot的组织方式像一个列表一样,当我们建立新的局部变量后,会被放入一个slot当中,如果像你上面所说,每一个循环中新建一个变量,那么变量槽应该被放入了很多局部变量的引用,但是上面有人有javap看了,其实不会存在那么多引用,主要原因就是在jvm中会优化,也就是slot是会被复用的,就是一个局部变量出了它的作用域之后,那么该slot就可以被复用了,所以你每一次循环,相当于那个局部变量就出了作用域,下一次再声明一个新的变量,就会把上一个出了作用域的slot覆盖掉,所以两种方式其实在,局部变量表中,始终也是占用的一个slot,也就是在空间占用上,两个是一样的,但是就像你看的那样,第二种方式会不断的覆盖,就算是覆盖也是需要时间的,所以总的来说第一种还是更好一点,并且第一种,面临的问题,就是引用的地址会不断变化,但是第二种方式也是,需要每一个都需要引用指向实例啊,所以说这一点应该是相同的,两种方法其实主要区别,就是在存放局部变量的时候会有区别。
日知己所无 2015-07-28
  • 打赏
  • 举报
回复
如果有异常处理的话,一般用第一种比较多一些 没有异常处理的话,建议使用第二种方式
skgary 2015-07-28
  • 打赏
  • 举报
回复
引用 12 楼 luohaoxh 的回复:
[quote=引用 7 楼 skgary 的回复:] [quote=引用 2 楼 u012557814 的回复:] [quote=引用 1 楼 skgary 的回复:] 对于绝大多数程序而言,纠结这些意义不大。 当然,习惯性用上面那种会好一些。
我觉得作为一个程序员纠结这个有必要 性能上,上面那种效率高些 如果循环次数很大,上万百万的话,无疑会加重虚拟机的压力 因为每次循环虚拟机都要开辟空间,还要用GC回收o对象,消耗了性能。[/quote] 你这么说,我不得不说,你如果真的纠结这事,得搞清楚这变量申明后,是不是在堆里,到底 需要不需要gc。 [/quote]这也是我发帖子的用意,我想知道的人告诉我一下,到底声明一个变量是怎样的过程,开销是怎样?有人知道的吗?[/quote] 楼上已经有大神出来说了结果。 我其实想说的是变量申明占的内存并不是在堆里,并不由gc回收。 但叫大家别纠结这事的原因在于,其实大部分的性能并不在于你这点点代码写的好不好,而在于整体上对于IO,数据库读写之类的操作好不好。
幻悟 2015-07-28
  • 打赏
  • 举报
回复
引用 11 楼 bao110908 的回复:
哎,实践出真知! 写代码看编译指令给你验证吧: 第一种方式代码:
import java.util.ArrayList;
import java.util.List;

public class Test {

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        for ( int i = 0 ; i < 100 ; i++ ) {
            list.add( "num-" + i );
        }

        String element = null;
        for ( int i = 0 , k = list.size() ; i < k ; i++ ) {
            element = list.get( i );
            System.out.println( System.identityHashCode( element ) );
        }
    }
}
第二种方式代码:
import java.util.ArrayList;
import java.util.List;

public class Test {

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        for ( int i = 0 ; i < 100 ; i++ ) {
            list.add( "num-" + i );
        }

        for ( int i = 0 , k = list.size() ; i < k ; i++ ) {
            String element = list.get( i );
            System.out.println( System.identityHashCode( element ) );
        }
    }
}
使用 javap -c Test 查看 JVM 指令(第一种方式):
H:\tmp>javap -c Test
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new     #2; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   iconst_0
   9:   istore_2
   10:  iload_2
   11:  bipush  100
   13:  if_icmpge       48
   16:  aload_1
   17:  new     #4; //class java/lang/StringBuilder
   20:  dup
   21:  invokespecial   #5; //Method java/lang/StringBuilder."<init>":()V
   24:  ldc     #6; //String num-
   26:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava
   29:  iload_2
   30:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuild
   33:  invokevirtual   #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   36:  invokeinterface #10,  2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
   41:  pop
   42:  iinc    2, 1
   45:  goto    10
   48:  aconst_null
   49:  astore_2
   50:  iconst_0
   51:  istore_3
   52:  aload_1
   53:  invokeinterface #11,  1; //InterfaceMethod java/util/List.size:()I
   58:  istore  4
   60:  iload_3
   61:  iload   4
   63:  if_icmpge       93
   66:  aload_1
   67:  iload_3
   68:  invokeinterface #12,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   73:  checkcast       #13; //class java/lang/String
   76:  astore_2
   77:  getstatic       #14; //Field java/lang/System.out:Ljava/io/PrintStream;
   80:  aload_2
   81:  invokestatic    #15; //Method java/lang/System.identityHashCode:(Ljava/lang/Object;)I
   84:  invokevirtual   #16; //Method java/io/PrintStream.println:(I)V
   87:  iinc    3, 1
   90:  goto    60
   93:  return
}
48:获取 null 值 49:将 null 压入 astore_2 区域 这两个指令相当于:String line = null; 68:从 List 中获取 i 的值 73:类型检查 76:将值的地址压入 astore_2 区域 第二种方式的 JVM 指令:
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new     #2; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   iconst_0
   9:   istore_2
   10:  iload_2
   11:  bipush  100
   13:  if_icmpge       48
   16:  aload_1
   17:  new     #4; //class java/lang/StringBuilder
   20:  dup
   21:  invokespecial   #5; //Method java/lang/StringBuilder."<init>":()V
   24:  ldc     #6; //String num-
   26:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava
   29:  iload_2
   30:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuild
   33:  invokevirtual   #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   36:  invokeinterface #10,  2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
   41:  pop
   42:  iinc    2, 1
   45:  goto    10
   48:  iconst_0
   49:  istore_2
   50:  aload_1
   51:  invokeinterface #11,  1; //InterfaceMethod java/util/List.size:()I
   56:  istore_3
   57:  iload_2
   58:  iload_3
   59:  if_icmpge       91
   62:  aload_1
   63:  iload_2
   64:  invokeinterface #12,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   69:  checkcast       #13; //class java/lang/String
   72:  astore  4
   74:  getstatic       #14; //Field java/lang/System.out:Ljava/io/PrintStream;
   77:  aload   4
   79:  invokestatic    #15; //Method java/lang/System.identityHashCode:(Ljava/lang/Object;)I
   82:  invokevirtual   #16; //Method java/io/PrintStream.println:(I)V
   85:  iinc    2, 1
   88:  goto    57
   91:  return
}
64:从 List 中获取 i 的值 69:类型检查 72:将值的地址压入 astore_4 区域 从两种不同风格不同的 JVM 指令来说,几乎没有区别,我不知道楼上的各位何来第一种性能高的说法? 变量的内存分配是在栈空间做的,而栈空间的操作是在编译时确定的。对于 JVM 来说,只是将堆中对象的内存地址压入栈空间中。 对于编译时内联处理,以及 GC 的垃圾处理来说,尽量将局变量的作用域减少到最小的范围之内!
从jvm指令来看,确实几乎没有差别,只能说编译器真智能.这样看来,优雅和性能居然是统一的,多谢bao110908帮我解惑了.
幻悟 2015-07-28
  • 打赏
  • 举报
回复
引用 7 楼 skgary 的回复:
[quote=引用 2 楼 u012557814 的回复:] [quote=引用 1 楼 skgary 的回复:] 对于绝大多数程序而言,纠结这些意义不大。 当然,习惯性用上面那种会好一些。
我觉得作为一个程序员纠结这个有必要 性能上,上面那种效率高些 如果循环次数很大,上万百万的话,无疑会加重虚拟机的压力 因为每次循环虚拟机都要开辟空间,还要用GC回收o对象,消耗了性能。[/quote] 你这么说,我不得不说,你如果真的纠结这事,得搞清楚这变量申明后,是不是在堆里,到底 需要不需要gc。 [/quote]这也是我发帖子的用意,我想知道的人告诉我一下,到底声明一个变量是怎样的过程,开销是怎样?有人知道的吗?
  • 打赏
  • 举报
回复
哎,实践出真知! 写代码看编译指令给你验证吧: 第一种方式代码:
import java.util.ArrayList;
import java.util.List;

public class Test {

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        for ( int i = 0 ; i < 100 ; i++ ) {
            list.add( "num-" + i );
        }

        String element = null;
        for ( int i = 0 , k = list.size() ; i < k ; i++ ) {
            element = list.get( i );
            System.out.println( System.identityHashCode( element ) );
        }
    }
}
第二种方式代码:
import java.util.ArrayList;
import java.util.List;

public class Test {

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        for ( int i = 0 ; i < 100 ; i++ ) {
            list.add( "num-" + i );
        }

        for ( int i = 0 , k = list.size() ; i < k ; i++ ) {
            String element = list.get( i );
            System.out.println( System.identityHashCode( element ) );
        }
    }
}
使用 javap -c Test 查看 JVM 指令(第一种方式):
H:\tmp>javap -c Test
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new     #2; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   iconst_0
   9:   istore_2
   10:  iload_2
   11:  bipush  100
   13:  if_icmpge       48
   16:  aload_1
   17:  new     #4; //class java/lang/StringBuilder
   20:  dup
   21:  invokespecial   #5; //Method java/lang/StringBuilder."<init>":()V
   24:  ldc     #6; //String num-
   26:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava
   29:  iload_2
   30:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuild
   33:  invokevirtual   #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   36:  invokeinterface #10,  2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
   41:  pop
   42:  iinc    2, 1
   45:  goto    10
   48:  aconst_null
   49:  astore_2
   50:  iconst_0
   51:  istore_3
   52:  aload_1
   53:  invokeinterface #11,  1; //InterfaceMethod java/util/List.size:()I
   58:  istore  4
   60:  iload_3
   61:  iload   4
   63:  if_icmpge       93
   66:  aload_1
   67:  iload_3
   68:  invokeinterface #12,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   73:  checkcast       #13; //class java/lang/String
   76:  astore_2
   77:  getstatic       #14; //Field java/lang/System.out:Ljava/io/PrintStream;
   80:  aload_2
   81:  invokestatic    #15; //Method java/lang/System.identityHashCode:(Ljava/lang/Object;)I
   84:  invokevirtual   #16; //Method java/io/PrintStream.println:(I)V
   87:  iinc    3, 1
   90:  goto    60
   93:  return
}
48:获取 null 值 49:将 null 压入 astore_2 区域 这两个指令相当于:String line = null; 68:从 List 中获取 i 的值 73:类型检查 76:将值的地址压入 astore_2 区域 第二种方式的 JVM 指令:
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new     #2; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   iconst_0
   9:   istore_2
   10:  iload_2
   11:  bipush  100
   13:  if_icmpge       48
   16:  aload_1
   17:  new     #4; //class java/lang/StringBuilder
   20:  dup
   21:  invokespecial   #5; //Method java/lang/StringBuilder."<init>":()V
   24:  ldc     #6; //String num-
   26:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava
   29:  iload_2
   30:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuild
   33:  invokevirtual   #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   36:  invokeinterface #10,  2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
   41:  pop
   42:  iinc    2, 1
   45:  goto    10
   48:  iconst_0
   49:  istore_2
   50:  aload_1
   51:  invokeinterface #11,  1; //InterfaceMethod java/util/List.size:()I
   56:  istore_3
   57:  iload_2
   58:  iload_3
   59:  if_icmpge       91
   62:  aload_1
   63:  iload_2
   64:  invokeinterface #12,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   69:  checkcast       #13; //class java/lang/String
   72:  astore  4
   74:  getstatic       #14; //Field java/lang/System.out:Ljava/io/PrintStream;
   77:  aload   4
   79:  invokestatic    #15; //Method java/lang/System.identityHashCode:(Ljava/lang/Object;)I
   82:  invokevirtual   #16; //Method java/io/PrintStream.println:(I)V
   85:  iinc    2, 1
   88:  goto    57
   91:  return
}
64:从 List 中获取 i 的值 69:类型检查 72:将值的地址压入 astore_4 区域 从两种不同风格不同的 JVM 指令来说,几乎没有区别,我不知道楼上的各位何来第一种性能高的说法? 变量的内存分配是在栈空间做的,而栈空间的操作是在编译时确定的。对于 JVM 来说,只是将堆中对象的内存地址压入栈空间中。 对于编译时内联处理,以及 GC 的垃圾处理来说,尽量将局变量的作用域减少到最小的范围之内!
幻悟 2015-07-28
  • 打赏
  • 举报
回复
看了大家的回复,我更纠结了.强迫症的人真可怜,老纠结在优雅和性能上.真希望有人能告诉我,大家平时都用的第二种,可是看来大家平时都用的第一种比较多?
-天宇 2015-07-28
  • 打赏
  • 举报
回复
建议第一种 变量的声明也是需要消耗资源的
  • 打赏
  • 举报
回复
一般建议使用第二种方式,尽可能地把局部变量的作用范围减小!
skgary 2015-07-28
  • 打赏
  • 举报
回复
引用 2 楼 u012557814 的回复:
[quote=引用 1 楼 skgary 的回复:] 对于绝大多数程序而言,纠结这些意义不大。 当然,习惯性用上面那种会好一些。
我觉得作为一个程序员纠结这个有必要 性能上,上面那种效率高些 如果循环次数很大,上万百万的话,无疑会加重虚拟机的压力 因为每次循环虚拟机都要开辟空间,还要用GC回收o对象,消耗了性能。[/quote] 你这么说,我不得不说,你如果真的纠结这事,得搞清楚这变量申明后,是不是在堆里,到底 需要不需要gc。
迷林 2015-07-28
  • 打赏
  • 举报
回复
对于这种,我觉得楼主每必要纠结,有时候写代码是看情况的,不一定非得按照某一定的模式去写
qqw6789567 2015-07-28
  • 打赏
  • 举报
回复
第一个吧,格式方便,创建一次
nanfu08 2015-07-28
  • 打赏
  • 举报
回复
建议使用第一种定义
scmod 2015-07-28
  • 打赏
  • 举报
回复
楼主试下循环个上百万次就知道了... 区别貌似不大的... 只是面试笔试题好像喜欢搞这些

62,629

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧