当前位置:编程学习 > JAVA >>

java 基本类型 与常量池和栈

    java基本类型 例如 int a=1  是在栈吧;

    JVM加载类的时候,会给每个类维护一个常量池,按理说,上面的值 1应该是存在于该类的常量池中。(常量池运行中在方法区吧)
他又是为什么存在于栈中呢?
    数据是如何才可以进入栈中? 栈 常量池 --------------------编程问答-------------------- 这个问题挺值得学习的,int是值数据类型,与引用数据数据类型不一样,会有相应的地址来供引用,关注了,我也想知道答案。 --------------------编程问答-------------------- int是基本类型,而基本类型数据是存放于栈中的。对于常量池,你一定是觉得int a = 1 ;
1是常量对吗?生活中可以这么理解,但java中就不是了,1 只是代表一个值,所谓的常量在
java中是指static final修饰的变量,它们是不可改变的,也就是初始化后就再也无法改变,
这样才叫做常量,而a = 2 它就变了吧,所以是变量。java中有六大存储位置,其中我们应该
要很清楚明白四个(栈、堆、静态域、常量池) --------------------编程问答-------------------- a=1 这个1是个常量,但是并不代表它会在常连池中,1本身这个量是在常量池中,但是a=1的这个1不是,它被jvm优化,被直接放在指令指针上,然后a=1的数值被直接存放在方法栈当前栈的索引空间中,因为索引空间是可以直接存放基本数据类型和句柄数据的。
这么说你大概会很糊涂,但是事实是如此,jdk与jvm会将简单量优化。 --------------------编程问答-------------------- 只有String类型的常量和static final修饰的“变量”才算常量。
另外,数值常量没有必要写进常量池,因为它本身就是一个跟指针一样大小的东西,装入常量池,再用一个指针读出,再写入目标变量,与直接把值写入目标变量相比,消耗大,结果一样,所以没有意义。
关于static final,有一个小知识:
class Test1 {
  public static final String TEST1 = "hahaha".toString();
}

class Test2 {
  public static final String TEST2 = "hahaha".toString();
}

class TestClass {
  private String a = Test1.TEST1;
  private String b = Test2.TEST2;
  ...
}

如果查看一下字节码,你会发现在TestClass中,a的值是从常量池获取,而b的值则是引用了Test2的TEST2。
如果你查看Test2的字节码,你会发现"hahaha"的确是在常量池中,而TEST2的值是在运行时被赋予。 --------------------编程问答-------------------- 天煞的不让改回帖论坛。Test1写错了,这里更正一下
class Test1 {
  public static final String TEST1 = "hahaha";
}
 
class Test2 {
  public static final String TEST2 = "hahaha".toString();
}
 
class TestClass {
  private String a = Test1.TEST1;
  private String b = Test2.TEST2;
  ...
}
--------------------编程问答-------------------- 我的理解:
基本类型是放在值和指针都放在栈中的;引用的对象被final static的值才会放到常量池中,指针是被放在栈中。 --------------------编程问答-------------------- 你的想法是对的,但是1不用放到常量池,因为jvm刚好有个指令把1放到操作数栈中。如果100000就得放到常量池了。你可以看一下jvm规范。
至于你说的的a,是在栈中,在进入方法调用时会把局部变量分配好(编译时已确实大小),其实就是移动一下栈指针。你可以看一下函数执行的汇编代码,会更好理解一点。 --------------------编程问答--------------------
引用 4 楼 lcf 的回复:
只有String类型的常量和static final修饰的“变量”才算常量。
另外,数值常量没有必要写进常量池,因为它本身就是一个跟指针一样大小的东西,装入常量池,再用一个指针读出,再写入目标变量,与直接把值写入目标变量相比,消耗大,结果一样,所以没有意义。
关于static final,有一个小知识:
class Test1 {
  public static final String TEST1 = "hahaha".toString();
}

class Test2 {
  public static final String TEST2 = "hahaha".toString();
}

class TestClass {
  private String a = Test1.TEST1;
  private String b = Test2.TEST2;
  ...
}

如果查看一下字节码,你会发现在TestClass中,a的值是从常量池获取,而b的值则是引用了Test2的TEST2。
如果你查看Test2的字节码,你会发现"hahaha"的确是在常量池中,而TEST2的值是在运行时被赋予。

请忽略我。。让我再研究一下 --------------------编程问答-------------------- 可以看一下java数据存储的介绍,百度文库的,希望对楼主有帮助
http://wenku.baidu.com/view/8c66da7e27284b73f2425056.html --------------------编程问答--------------------
引用 3 楼 spiniper 的回复:
a=1 这个1是个常量,但是并不代表它会在常连池中,1本身这个量是在常量池中,但是a=1的这个1不是,它被jvm优化,被直接放在指令指针上,然后a=1的数值被直接存放在方法栈当前栈的索引空间中,因为索引空间是可以直接存放基本数据类型和句柄数据的。
这么说你大概会很糊涂,但是事实是如此,jdk与jvm会将简单量优化。



我又测试了下   只有我把int 声明为final时才能够在常量池中看到,  而对于long ,double ,float string 
即使不是final 也会在常量池中出现, short char  和int的情况相同;
   查看字节码后 那些在常量池中的都会用ldc #2.。。。  入栈, 而对于int那样的  就是sipush 转化为int后入栈   
public class wy {
    String itemS ="我们 ";
   static final  int as=6;
  Integer i1=129;
  double  a=1.23;
  long    bb=111111111;
   float  cc=1.26f;
   short  ee=55;
   char   ff='z';
   //Boolean bool1=true;
}
 
aload_0
invokespecial java/lang/Object/<init>()V
aload_0
ldc "我们 "
putfield wy/itemS Ljava/lang/String;
aload_0
sipush 129
invokestatic java/lang/Integer/valueOf(I)Ljava/lang/Integer;
putfield wy/i1 Ljava/lang/Integer;
aload_0
ldc2_w 1.23
putfield wy/a D
aload_0
 
    这样说对不对?
  还有就是声明为int a=122  和 Integer a=122  有什么不一样?
              int i4=444;
   Integer i3=new Integer(444);
       i3==i4 为true?? --------------------编程问答--------------------
引用 8 楼 lcf 的回复:
Quote: 引用 4 楼 lcf 的回复:

只有String类型的常量和static final修饰的“变量”才算常量。
另外,数值常量没有必要写进常量池,因为它本身就是一个跟指针一样大小的东西,装入常量池,再用一个指针读出,再写入目标变量,与直接把值写入目标变量相比,消耗大,结果一样,所以没有意义。
关于static final,有一个小知识:
class Test1 {
  public static final String TEST1 = "hahaha".toString();
}

class Test2 {
  public static final String TEST2 = "hahaha".toString();
}

class TestClass {
  private String a = Test1.TEST1;
  private String b = Test2.TEST2;
  ...
}

如果查看一下字节码,你会发现在TestClass中,a的值是从常量池获取,而b的值则是引用了Test2的TEST2。
如果你查看Test2的字节码,你会发现"hahaha"的确是在常量池中,而TEST2的值是在运行时被赋予。

请忽略我。。让我再研究一下


看下这个帖子  http://bbs.csdn.net/topics/290004554 


看一下实际的处理情况:
       int a=3;
       int b=3;
       int c=65535;
       int d=65535;
       int e=32330;
       int f=32330;
看对应的虚拟机指令,可以知道变量里实际存储的是什么:
Code:
   0:        iconst_3   //3
   1:        istore_1
   2:        iconst_3   //3
   3:        istore_2
   4:        ldc        #2; //int 65535
   6:        istore_3
   7:        ldc        #2; //int 65535
   9:        istore        4
   11:        sipush        32330
   14:        istore        5
   16:        sipush        32330
   19:        istore        6
   21:        return
可以看出每个变量保存自己的值.(具体指令的意义参考java虚拟机规范)
这里要注意的是对于int值,如果它大于short能表示的范围,则放到常量池中去.
11:        sipush        32330
   14:        istore        5
这句,11-13,正好是3个字节的指令大小,一个字节是sipush指令,2个字节用来存储32330这个数.两次使用到这个数,都是把它直接存给变量的,所以原贴中一直强调的"栈中共享" 的说法明显不对.
对于65535,它是大于两个字节的,编译的时候把它放入常量池部分,而把取这个数的指令写为ldc#2,我感觉这样一个直观的好处是减少了指令代码的长度.尤其是多次使用到一个相同的数时.




  但是e==f 为true     这个是为什么?   而且两次使用 sipush   32330(是入栈吧?)




--------------------编程问答--------------------
引用 11 楼 goforit_ 的回复:
Quote: 引用 8 楼 lcf 的回复:

Quote: 引用 4 楼 lcf 的回复:

只有String类型的常量和static final修饰的“变量”才算常量。
另外,数值常量没有必要写进常量池,因为它本身就是一个跟指针一样大小的东西,装入常量池,再用一个指针读出,再写入目标变量,与直接把值写入目标变量相比,消耗大,结果一样,所以没有意义。
关于static final,有一个小知识:
class Test1 {
  public static final String TEST1 = "hahaha".toString();
}

class Test2 {
  public static final String TEST2 = "hahaha".toString();
}

class TestClass {
  private String a = Test1.TEST1;
  private String b = Test2.TEST2;
  ...
}

如果查看一下字节码,你会发现在TestClass中,a的值是从常量池获取,而b的值则是引用了Test2的TEST2。
如果你查看Test2的字节码,你会发现"hahaha"的确是在常量池中,而TEST2的值是在运行时被赋予。

请忽略我。。让我再研究一下


看下这个帖子  http://bbs.csdn.net/topics/290004554 


看一下实际的处理情况:
       int a=3;
       int b=3;
       int c=65535;
       int d=65535;
       int e=32330;
       int f=32330;
看对应的虚拟机指令,可以知道变量里实际存储的是什么:
Code:
   0:        iconst_3   //3
   1:        istore_1
   2:        iconst_3   //3
   3:        istore_2
   4:        ldc        #2; //int 65535
   6:        istore_3
   7:        ldc        #2; //int 65535
   9:        istore        4
   11:        sipush        32330
   14:        istore        5
   16:        sipush        32330
   19:        istore        6
   21:        return
可以看出每个变量保存自己的值.(具体指令的意义参考java虚拟机规范)
这里要注意的是对于int值,如果它大于short能表示的范围,则放到常量池中去.
11:        sipush        32330
   14:        istore        5
这句,11-13,正好是3个字节的指令大小,一个字节是sipush指令,2个字节用来存储32330这个数.两次使用到这个数,都是把它直接存给变量的,所以原贴中一直强调的"栈中共享" 的说法明显不对.
对于65535,它是大于两个字节的,编译的时候把它放入常量池部分,而把取这个数的指令写为ldc#2,我感觉这样一个直观的好处是减少了指令代码的长度.尤其是多次使用到一个相同的数时.




  但是e==f 为true     这个是为什么?   而且两次使用 sipush   32330(是入栈吧?)





谢谢大大发片!

我研究了一下Java ByteCode http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings 发现JVM指令里面只有bipush和sipush。另外对于integer,有-1 ~ 5的7个内置常量指令,也就是上面看到的类似iconst_3(虽然不知道为什么)。

所以对于byte范围内的整数,则用bipush(byte integer push)入栈;对于short范围内的整数,用sipush(short integer push),其余则由指令LDC从常量池加载。

另外一个疑惑的事情是,由64位Java编译出来的指令码,也没有"iipush"或者"ffpush"之类的东西,照理说指令长度不再有4个字节的限制,应该可以才对。。难道是为了兼容性?

至于e==f为true,这不是自然的事情么。。。只是栈头的两个数作比较而已,它们的确相同。至于为什么要调用两次sipush,是因为istore有一个出栈操作,14行之后栈是空的,必须再入栈才能执行下一个istore --------------------编程问答-------------------- java没有32与64的区分,java所有的定义都是规范化的,他不会收到cpu字长的影响,因此class文件不区分32与64,这些由jvm去处理了。
常量池会存储所有的常量,即使是1也不例外,只是jdk在编译的时候,会优化class,将一些没有必要的常量指令化来节省class文件大小和内存加载的空间。ldc只是从常量池将数据检索出来,并无转换的过程,它将常量池的数据检索出来并压入操作数栈,然后其它指令讲操作数栈中此数出栈病存入方法栈局部变量索引中。
其实java的执行引擎,与cpu执行引擎类似,大体上所有执行引擎的设计都是如此,java的常量池相当于内存数据,而java的指令指针相当于cpu的指令指针,它总是指向下一句代码。而局部变量相当于cpu的二级缓存,至于操作数栈,相当于寄存器。为什么说jvm是个虚拟机,因为他就像一个虚拟机一样运行。 --------------------编程问答--------------------
引用 12 楼 lcf 的回复:
Quote: 引用 11 楼 goforit_ 的回复:

Quote: 引用 8 楼 lcf 的回复:

Quote: 引用 4 楼 lcf 的回复:

只有String类型的常量和static final修饰的“变量”才算常量。
另外,数值常量没有必要写进常量池,因为它本身就是一个跟指针一样大小的东西,装入常量池,再用一个指针读出,再写入目标变量,与直接把值写入目标变量相比,消耗大,结果一样,所以没有意义。
关于static final,有一个小知识:
class Test1 {
  public static final String TEST1 = "hahaha".toString();
}

class Test2 {
  public static final String TEST2 = "hahaha".toString();
}

class TestClass {
  private String a = Test1.TEST1;
  private String b = Test2.TEST2;
  ...
}

如果查看一下字节码,你会发现在TestClass中,a的值是从常量池获取,而b的值则是引用了Test2的TEST2。
如果你查看Test2的字节码,你会发现"hahaha"的确是在常量池中,而TEST2的值是在运行时被赋予。

请忽略我。。让我再研究一下


看下这个帖子  http://bbs.csdn.net/topics/290004554 


看一下实际的处理情况:
       int a=3;
       int b=3;
       int c=65535;
       int d=65535;
       int e=32330;
       int f=32330;
看对应的虚拟机指令,可以知道变量里实际存储的是什么:
Code:
   0:        iconst_3   //3
   1:        istore_1
   2:        iconst_3   //3
   3:        istore_2
   4:        ldc        #2; //int 65535
   6:        istore_3
   7:        ldc        #2; //int 65535
   9:        istore        4
   11:        sipush        32330
   14:        istore        5
   16:        sipush        32330
   19:        istore        6
   21:        return
可以看出每个变量保存自己的值.(具体指令的意义参考java虚拟机规范)
这里要注意的是对于int值,如果它大于short能表示的范围,则放到常量池中去.
11:        sipush        32330
   14:        istore        5
这句,11-13,正好是3个字节的指令大小,一个字节是sipush指令,2个字节用来存储32330这个数.两次使用到这个数,都是把它直接存给变量的,所以原贴中一直强调的"栈中共享" 的说法明显不对.
对于65535,它是大于两个字节的,编译的时候把它放入常量池部分,而把取这个数的指令写为ldc#2,我感觉这样一个直观的好处是减少了指令代码的长度.尤其是多次使用到一个相同的数时.




  但是e==f 为true     这个是为什么?   而且两次使用 sipush   32330(是入栈吧?)





谢谢大大发片!

我研究了一下Java ByteCode http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings 发现JVM指令里面只有bipush和sipush。另外对于integer,有-1 ~ 5的7个内置常量指令,也就是上面看到的类似iconst_3(虽然不知道为什么)。

所以对于byte范围内的整数,则用bipush(byte integer push)入栈;对于short范围内的整数,用sipush(short integer push),其余则由指令LDC从常量池加载。

另外一个疑惑的事情是,由64位Java编译出来的指令码,也没有"iipush"或者"ffpush"之类的东西,照理说指令长度不再有4个字节的限制,应该可以才对。。难道是为了兼容性?

至于e==f为true,这不是自然的事情么。。。只是栈头的两个数作比较而已,它们的确相同。至于为什么要调用两次sipush,是因为istore有一个出栈操作,14行之后栈是空的,必须再入栈才能执行下一个istore




 e和f是局部变量吧, 那个   istore    5  是出栈把32300 赋值给 e是吧? (那e是存放在哪?)
  给f赋值的时候又入栈32300,     那么e和f 到底是不是共享32300?  是不是栈中共享?  --------------------编程问答-------------------- e存放在局部变量索引中,方法栈内存结构中,有一段是专门存放局部变量的。 --------------------编程问答-------------------- 常量池(constant pool)是每个类或者每个接口的java class文件中的constant_pool表的运行期表示。常量池的功能类似传统的程序设计语言的符号表。每个常量池都是从 JVM 的方法区分配的。类或接口的常量池在该类或接口的   .class文件被JVM成功装载时创建。
--------------------编程问答-------------------- 关于e和f是否共享32300,这个既是又不是。
e和f是两个不同变量,但是有同样的值。比方说在内存的位置0x0001和0x0005,各存放了一个32300。因为32300只是一个数字,它们永远相同。这就好比质子,它们是全等的,你从一个铁原子和一个碳原子中各拿一个质子来比,它们没有任何不同。同样,你把内存中0x0001和0x0005的32300拿来比,它们也没有不同。就是说,32300跟质子一样,虽然来自不同的地方,但它们却又是相同的 --------------------编程问答--------------------
引用 14 楼 goforit_ 的回复:
 e和f是局部变量吧, 那个   istore    5  是出栈把32300 赋值给 e是吧? (那e是存放在哪?)
  给f赋值的时候又入栈32300,     那么e和f 到底是不是共享32300?  是不是栈中共享? 

e和f是栈中两个变量,当然不会共享。
http://bbs.csdn.net/topics/390469447#这里列出了一些扯淡的帖子,可以看看,防止上当受骗。
补充:Java ,  Java SE
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,