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 |
|