操作系统中 heap 和 stack 的区别

来源:互联网 发布:打的费用计算软件 编辑:程序博客网 时间:2024/05/30 23:00

这个题目其实时很大的,但是关于堆栈,本来就应用广泛。这里主要总结两种角度,一种是Java中关于堆和栈的内容;另外一部分呢,从数据结构角度解释堆栈是个什么东西,多用于算法当中。

导读

参考范围:

Java内存管理;算法导论;计算机操作系统

(一)堆栈的概念

百度百科上,堆栈的解释是这样的:

在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。在单片机应用中,堆栈是个特殊的存储区,主要功能是暂时存放数据和地址,通常用来保护断点和现场。要点:堆,队列优先,先进先出(FIFO—first in first out)。栈,先进后出(FILO—First-In/Last-Out)。

(二)堆栈的区别

如文章开篇说的,从不同的角度看待堆栈是有不同的解释的,后面慢慢解释了,先来看看两者有哪些区别。

2.1 堆栈空间分配区别
1、栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈; 
2、堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

2.2 堆栈缓存方式区别:
1、栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放; 
2、堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

2.3 堆栈数据结构区别:
1、堆(数据结构):堆可以被看成是一棵树,如:堆排序; 
2、栈(数据结构):一种先进后出的数据结构。

(三)Java内存当中的堆栈

关于Java内存管理,是一块很重要的知识点,早些时候总结过,总结的一般,这里//TODO一下,有时间完善:《深入理解JVM》内存模型

3.1 Java的栈内存和堆内存

Java 把内存划分成两种:一种是栈内存,另一种是堆内存。 
  在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。 
  堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。

3.2 Java内存区域概述

1.栈内存空间:保存所有的对象名称(更准确地说是保存了引用的堆内存空间的地址) 
2.堆内存空间:保存每个对象的具体属性内容。 
3.全局数据区:保存static类型的属性。 
4.全局代码区:保存所有的方法定义。

3.3 Java中变量在内存中的分配

1、类变量(static修饰的变量):在程序加载时系统就为它在堆中开辟了内存,堆中的内存地址存放于栈以便于高速访问。静态变量的生命周期–一直持续到整个”系统”关闭。 
2、实例变量:当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量(比如说类实例),然后根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的”物理位置”。 实例变量的生命周期–当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存。 
3、局部变量:局部变量,由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放。

(四) 数据结构中的堆

《算法导论》第六章 关于堆

(五) 数据结构中的栈

算法导论中关于栈是和队列链表等一同讲的,这里发现一篇文章,不涉及到栈以外的东西,总结得不错:

栈的三种理解

(六)小试牛刀

题目来源于:牛客网

关于操作系统heap与stack说法中,正确的是()。
a、stack由编译器自动分配和释放,存放函数的参数值,局部变量,全局变量的值
b、heap一般由程序员分配和释放,若程序员不释放,可能会造成操作系统的内存泄露
c、stack由系统自动分配,无需程序员干涉,heap需要手动申请
d、heap与stack都会在初始大小空间用满时,系统自动增加其大小 

答案:bc

解释:

a:全局变量应该放在全局区,而不是栈区;

b:可能许多人对内存分配上的“栈  stack ”和“堆  heap ”还不是很明白。包括一些科班出身的人也不明白这两个概念。简单的来讲, stack 上分配的内存系统自动释放,  heap 上分配的内存,系统不释放,哪怕程序退出,那一块内存还是在那里。 stack 一般是静态分配内存, heap 上一般是动态分配内存。 
由 malloc 系统函数分配的内存就是从堆上分配内存。从堆上分配的内存一定要自己释放。用 free 释放,不然就是术语——“内存泄露”(或是“内存漏洞”)——  Memory Leak 。于是,系统的可分配内存会随 malloc 越来越少,直到系统崩溃。还是来看看“栈内存”和“堆内存”的差别吧。
参看: http://www.360doc.com/content/05/0929/14/1894_15840.shtml;

c:stack由系统分配,和编译器,看是差不多的,不需要程序员干涉;

d:栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
参考: http://blog.chinaunix.net/uid-24958038-id-3132588.html

原创粉丝点击