武内崇saber厨:关于java栈与堆深入了解 对一些错误认识的纠正

来源:百度文库 编辑:偶看新闻 时间:2024/04/28 15:12:45
1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
//栈都是由运行环境来处理的,这点C++和java没有什么不同.对于堆,不过java多了个GC.
2.这里的堆和栈首先要明确是虚拟机栈,和寄存器根本不是一个级别的东西,就别比较了.
3.栈数据共享好像是作者自己创造的概念.而且给基本类型也引入了"引用"的概念,不知道出于何种打算.
java虚拟机规范中说:Primitive values do not share state with other primitive values. A variable whose type is a primitive type always holds a primitive value of that type.
看一下实际的处理情况:
  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,我感觉这样一个直观的好处是减少了指令代码的长度.尤其是多次使用到一个相同的数时.

  其实,java 对变量的处理很简单,基本类型变量存放值,引用类型存放一个"引用" (实际就是一个"指针" ,以前曾经和别人讨论过,很多人认为是个"句柄",并举了很多证据,但是我后来看到了sun的java hotspot白皮书,里面直接说明了,引用实际就是一个c的"指针" ,使用句柄需要多次间接查找,会带来效率的瓶颈,当然这个指针并不是直接指向实际的对象,实际指向的是一个两个机器字大小的对象头,对于数组是3个机器字大小的对象头,因为还要保存数组的长度) .
java设计时保留基本类型而不把一切都设计为对象,就是出于效率考虑,如果对于基本类型再通过"引用"去查找值,何苦呢?
4.String也是包装类? 这应该也是作者自己定义了包装类的概念,去java语言规范里看看什么是wrapper class.
5.Integer i = 3;编译器如何处理?
sun的编译器是这样处理的:
Integer i=Integer.valueOf(3);
而不是通过new来创建了,因为Integer类中静态的创建了-128~+127之间的对象,需要的数在这个范围之内时,直接返回,此范围之外的数才通过new来创建.
简单测试.
Integer i=3;
Integer j=3;
我们测试i==j会发现它是true.
String str = new String("abc");去看String类的构造方法会发现,这里用的是String(String original)来创建的,也就是说用一个String来创建一个String,"abc"编译的时候编译器会把它加入常量池部分.不知道原贴所谓的包装类是如何得来的.
csdn 的java版有个“推荐”的专门讲String常量池的帖子,很不错。
5."JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。 "又没有分清编译器和虚拟机的职责分配.编译器会把"abc"放入常量池,并记住它在常量池中的位置,别的地方用到的时候编译器直接生成ldc指令来制定了,不会让jvm去找,去开辟地址等等.
7.原贴明显不知道常量池的存在,好像是把常量池的数据都认为是"栈"的了.
  java新增了StringBuilder来处理可变字符串,如果不需要多线程环境,应该首先选择这个,而不是StringBuffer