欢迎来到天天文库
浏览记录
ID:8977891
大小:48.00 KB
页数:12页
时间:2018-04-13
《从“关于java堆与栈的思考”一帖看错误信息的传播》由会员上传分享,免费在线阅读,更多相关内容在应用文档-天天文库。
1、我对转贴的信息一直有敌意,原因如下:首先,除了制造更多的信息垃圾,转贴不会带来新的价值,想收藏的话一个链接足矣;其次,将错误信息以讹传讹,混淆视听。不妨选一个典型的例子说明一二。相信《关于Java堆与栈的思考》这个帖子大家并不陌生,转载量极大。但内容如何呢?我就试着分析一下。说明:以下内容,黑色字体为引用别人的帖子,红色字体是我的分析,蓝色字体是引用相关文献。1.栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。与C++不同,对于堆来说是有道理的。但C++的栈也是由编译器负责安排和布局的,这
2、和java没什么区别。其实,java里提到的堆和栈应该是逻辑层面上的,是jvm划分的,并不同于C++的实际运行时内存,因此根本不是一个层面上的东西,最好不要进行比较。2.栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。在逻辑上看,可以认为栈比堆快,但是请不要和寄存器比。上面已经提到,java所谓的堆和栈是逻辑上
3、的,《TheJavaVirtualMachineSpecification》中,将运行时数据区分为pc寄存器、java虚拟机栈、堆、方法区、运行时常量池、本地方法栈共6个部分,而CPU中的寄存器是物理实体。比如java执行一个加法操作,逻辑上看是读取栈顶的两个数据,执行加法,将结果写回栈顶,但实际的代码经过jvmjit编译之后很可能就是在寄存器中执行加法。因此逻辑意义上的比较根本没有什么价值。"栈数据可以共享",这个说法是该帖子"创造性"的提出来的。翻遍所有的java资料,根本找不到这样的说法(现在看来,也不全是,国内已经有书籍将这个帖子完全照搬了,抄袭可耻!貌似有本《java程
4、序员面试宝典》,看过的都知道,呵呵)。3.Java中的数据类型有两种。一种是基本类型(primitivetypes),共有8种,即int,short,long,byte,float,double,boolean,char(注意,并没有string的基本类型)。这种类型的定义是通过诸如inta=3;longb=255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如inta=3;这里的a是一个指向int类型的引用,指向3这个字面值。这里混淆了一个问题。首先要知道什么是字面值,所谓的字面值是形如1,3,2.0,2
5、.0f,'c',"Hello"这样的形式出现的常量。基本类型变量里存的是什么?值。存到变量中就不存在"字面值"一说了!"如inta=3;这里的a是一个指向int类型的引用",这个例子就太不恰当了,我们要注意的是,inta=3;我们得到的是一个空间,里面存的是3这个数。这里又"int类型的引用"云云,岂不缪哉?这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。基本类型的变量存在栈里或者堆里不是由"大小可知,生存期可知"就能确定了。关键是上下文。比如voidfunc(){inta=3;}这
6、自然是存在栈里的。而classTest{inta=3;}这就肯定是随对象放到堆里的。因此,不要孤立的看到基本类型就说放到栈里,看到引用类型就说放到堆里。区分引用变量和对象本身特别重要,这个下面再说。另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义inta=3;intb=3;编译器先处理inta=3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理intb=3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均
7、指向3的情况。特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向
此文档下载收益归作者所有