堆和栈的区别
来源:互联网 发布:我赢职场java视频 编辑:程序博客网 时间:2024/06/11 04:49
在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。在单片机应用中,堆栈是个特殊的存储区,主要功能是暂时存放数据和地址,通常用来保护断点和现场。
栈,先进后出(FILO—First-In/Last-Out)(例如:超市排队结账,大一点的超市收银台都是一段狭长的过道,本来下一个是你了,突然这个收银台说不结了,OK,栈形成了,排在前面的要后出去了)。
1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。
2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。
3.Java中的数据类型有两种。
这种类型的定义是通过诸如int a= 3; long b = 255L;的形式来定义的,称为自动变量。
int a=int b=编译器先处理int a= 3;首先它会在栈中创建一个变量为a的内存空间,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b= 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。
特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
另一种是包装类数据,【如Integer,String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于【堆】中】,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。 4.String是一个特殊的包装类数据。即可以用String str = new String(“abc”);的形式来创建,也可以用Stringstr = “abc”;的形式来创建(作为对比,在JDK 5.0之前,你从未见过Integer i = 3;的表达式,因为类与字面值是不能通用的,除了String。而在JDK5.0中,这种表达式是可以的!因为编译器在后台进行Integer i = new Integer(3)的转换)。前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。Java中的有些类,如DateFormat类,可以通过该类的getInstance()方法来返回一个新创建的类,似乎违反了此原则。其实不然。该类运用了单例模式来返回类的实例,只不过这个实例是在该类内部通过new()来创建的,而getInstance()向外部隐藏了此细节。那为什么在String str = “abc”;中,并没有通过new()来创建实例,是不是违反了上述原则?其实没有。
Java内部将此语句转化为以下几个步骤:【String str = “abc”,String str不要连着】
(1)先定义一个名为str的对String类的对象引用变量:String str;
(2)【在【栈】中查找有没有存放值为”abc”的地址,如果没有,则开辟一个存放字面值为”abc”的地址,接着创建一个新的String类的对象o,并将o的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为”abc”的地址,则查找对象o,并返回o的地址。】【上文说数据时存放在堆中,此文说数据存放在栈中】[因为此处不是通过new()创建的啊]
(3)将str指向对象o的地址。
值得注意的是,一般String类中字符串值都是直接存值的。但像String str = “abc”;这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!
为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。"abc"; "abc";System.out.println(str1==str2);注意,我们这里并不用str1.equals(str2);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。
结果说明,JVM创建了两个引用str1和str2,但只创建了一个对象,而且两个引用都指向了这个对象。
我们再来更进一步,将以上代码改成:String str1="abc";str1=out.println(str1+//bcd,abc System.//false
String str1=String str2="bcd"; //bcd "bcd";System.out.println(str1==str4);我们再接着看以下的代码。
new "abc"); "abc"; System.out.println(str1==str2); String str1 = String str2 = String(//false
5. 数据类型包装类的值不可修改。
结果说明,JVM创建了两个引用str1和str2,但只创建了一个对象,而且两个引用都指向了这个对象。
我们再来更进一步,将以上代码改成:
String str1="abc";str1=out.println(str1+//bcd,abc System.//false
String str1=String str2="bcd"; //bcd "bcd";System.out.println(str1==str4);我们再接着看以下的代码。
new "abc"); "abc"; System.out.println(str1==str2); String str1 = String str2 = String(//false
5. 数据类型包装类的值不可修改。
6. 结论与建议:
二、堆和栈的理论知识
stack:
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = new char[10];
但是注意p1、p2本身是在栈中的。
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。
另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是
直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活。
栈: 在函数调用时,首先进栈的是函数的各个参数,然后是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址;在大多数的C编译器中,参数是由右往左入栈的(为什么是由右往左入栈的?),然后是函数中的局部变量。注意静态变量是不入栈的(存放在静态区)。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。
char s1[] = “aaaaaaaaaaaaaaa”;
char *s2 = “bbbbbbbbbbbbbbbbb”;
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#include
void main()
{
char a = 1;
char c[] = “1234567890”;
char *p =”1234567890”;
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,再根据edx读取字符,显然慢了。
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- 堆和栈的区别
- Android精选完整源码之控件指示器视频压缩日历源码!
- 详细解释CNN卷积神经网络各层的参数和链接个数的计算
- php 有哪些数值是false
- ExtJs6学习(五)【ExtJs布局】
- 图片压缩技术
- 堆和栈的区别
- Python学习---18
- Excel实训日志总汇
- 一篇文章搞定面试中的变量提升
- log4cpp 使用笔记
- python识别网站所用技术
- 关于 未处理System.StackOverflowException的一种处理方法
- 总线地址
- oracle创建表空间