- 什么是堆叠和堆叠?
- 他们在电脑记忆中身处何方?
- 它们在多大程度上受到操作系统或语言运行时间的控制?
- 其范围是什么?
- 是什么决定了它们的大小?
- 是什么让一个更快?
当前回答
我有些话要说,尽管主要要点已经涵盖在内。
堆堆叠
- 非常快速的进入。
- 存储在记录和档案管理中。
- 函数调用与本地变量和已通过的函数参数一起装入这里 。
- 当程序超出范围时,空间会自动释放。
- 存储在相继内存中 。
堆肥
- 与Stack相比,Stack的准入速度缓慢。
- 存储在记录和档案管理中。
- 动态创建的变量存储在这里, 以后需要使用后释放分配的内存 。
- 存储到记忆分配的地方 指示器总是能存取
有趣的是:
- 如果功能电话储存在堆积中,就会造成两点混乱:
- 由于在堆叠中相继存储, 执行速度会更快。 堆积中的堆积会导致大量时间消耗, 从而使整个程序的执行速度放慢 。
- 如果将功能储存在堆积中(指针指向的沉降器),就不可能返回调用地址(堆叠是由于内存的顺序存储而提供的) 。
其他回答
其他答案只是避免解释静态分配意味着什么。 因此,我将解释三种主要分配形式,以及它们通常与下面的堆积、堆叠和数据段的关系。 我还将在C/C++和Python中展示一些例子,以帮助人们理解。
“ 静态” (AKA静态分配) 变量没有在堆叠上分配 。 不要假设- 许多人这样做, 只是因为“ 静态” 听起来像“ 堆叠 ” 。 它们实际上既不存在于堆叠中,也不存在于堆叠中。 它们属于所谓的“ 堆叠” 的一部分 。数据元数据段.
然而,一般而言最好考虑 " 。范围范围" 和 "寿命寿命而不是"堆"和"堆"
范围指代码中哪些部分可以访问变量。本地范围(只能由当前函数访问)和全球范围尽管范围可能变得更加复杂,但范围(无论何处都可以进入)仍会变得更加复杂。
寿命指变量在程序执行期间分配和交易的时间。通常我们想到的是静静分配(在程序的整个整个期间,将始终可变,因此有助于在多个函数调用中储存相同信息)相对于自动分配(只有在对函数的单一次呼叫中,可变性才能持续,使该函数可用于存储仅在您函数期间使用、一旦完成即可丢弃的信息)和动态分配(期限在运行时界定的可变数据,而不是静态或自动的时间。)
尽管大多数编译者和口译员在使用堆叠、堆肥等方面也采取了类似的做法,但只要行为正确,编译者有时会打破这些公约。例如,由于优化,本地变量可能只存在于一个登记册中,或者完全删除,即使大多数本地变量存在于堆叠中。正如在几个评论中指出的,您可以自由使用一个甚至不使用堆叠或堆积的编译者,而是使用其他一些存储机制(因为堆叠和堆积对这很重要,因为堆叠和堆积对这很重要 ) 。
我将提供一个简单的附加注释的 C 代码来说明所有这一切。 最好的学习方法是在调试器下运行一个程序并观看行为。 如果您喜欢阅读 Python, 请跳到答案的结尾 :
// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;
// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;
// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {
// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed only within MyFunction()
static int someLocalStaticVariable;
// Allocated on the stack each time MyFunction is called
// Deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
int someLocalVariable;
// A *pointer* is allocated on the stack each time MyFunction is called
// This pointer is deallocated when MyFunction returns
// scope - the pointer can be accessed only within MyFunction()
int* someDynamicVariable;
// This line causes space for an integer to be allocated in the heap
// when this line is executed. Note this is not at the beginning of
// the call to MyFunction(), like the automatic variables
// scope - only code within MyFunction() can access this space
// *through this particular variable*.
// However, if you pass the address somewhere else, that code
// can access it too
someDynamicVariable = new int;
// This line deallocates the space for the integer in the heap.
// If we did not write it, the memory would be "leaked".
// Note a fundamental difference between the stack and heap
// the heap must be managed. The stack is managed for us.
delete someDynamicVariable;
// In other cases, instead of deallocating this heap space you
// might store the address somewhere more permanent to use later.
// Some languages even take care of deallocation for you... but
// always it needs to be taken care of at runtime by some mechanism.
// When the function returns, someArgument, someLocalVariable
// and the pointer someDynamicVariable are deallocated.
// The space pointed to by someDynamicVariable was already
// deallocated prior to returning.
return;
}
// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.
区分寿命和范围之所以重要,一个特别令人印象深刻的例子说明为什么区分寿命和范围很重要,那就是变量可以具有本地范围,但有静态的寿命,例如,在上文的代码样本中“某些本地静态可变性 ” 。这些变量可以使我们共同但非正式的命名习惯非常混乱。例如,当我们说“某些本地可变性 ” 。当地当地" 我们通常是指 "本地覆盖范围自动分配变量" 当我们说全球时,我们通常指 "全球范围静态分配可变数" 不幸的是,当它谈到类似的事情"缩放的静态分配变量"很多人只是说..."对不对?".
C/C++中的一些语法选择加剧了这一问题,例如许多人认为全球变量并非“静态”,
int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation
int main() {return 0;}
请注意, 将关键字“ 静态” 放在上面的声明中会防止 var2 具有全球范围。 然而, 全球 val1 具有静态分布。 这不是直观的。 因此, 我试图在描述范围时从不使用“ 静态” 一词, 而是说“ 文件” 或“ 文件有限” 的范围。 但是许多人使用“ 静态” 或“ 静态范围” 来描述一个只能从一个代码文件中访问的变量。 在生命周期中, “ 静态” 是指“ 静态” 或“ 文件有限” 的范围。 但是许多人使用“ 静态” 或“ 静态范围” 来描述一个只能从一个代码文件中访问的变量。始终始终表示变量在程序启动时分配,在程序退出时进行交易。
有些人认为这些概念是特定C/C++/C++。它们不是。例如,下面的Python样本说明了所有三种分配类型(在翻译语言方面可能存在一些我无法进入这里的微妙差异)。
from datetime import datetime
class Animal:
_FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated
def PetAnimal(self):
curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)
class Cat(Animal):
_FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's
class Dog(Animal):
_FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!
if __name__ == "__main__":
whiskers = Cat() # Dynamically allocated
fido = Dog() # Dynamically allocated
rinTinTin = Dog() # Dynamically allocated
whiskers.PetAnimal()
fido.PetAnimal()
rinTinTin.PetAnimal()
Dog._FavoriteFood = 'milkbones'
whiskers.PetAnimal()
fido.PetAnimal()
rinTinTin.PetAnimal()
# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones
其他人直接回答了你的问题,但是,在试图理解堆叠和堆叠时,我认为,考虑传统的UNIX进程的记忆布局(没有线条和线条)是有益的。mmap()
- 以基于分配器为基础。记忆管理词汇表网页上有一个内存布局图。
堆堆和堆堆堆传统上位于进程的虚拟地址空间的对面。当访问时,堆堆会自动增长,最多以内核设定的大小(可以与setrlimit(RLIMIT_STACK, ...)
))当内存分配器援引brk()
或sbrk()
系统呼叫,绘制更多页的物理内存 进入该过程的虚拟地址空间。
在没有虚拟内存的系统中,例如一些嵌入系统,通常适用同样的基本布局,但堆叠和堆积大小固定。然而,在其他嵌入系统(例如基于微芯片的微控制器)中,程序堆叠是一个单独的内存块,无法通过数据移动指示处理,只能通过程序流指示(调用、返回等)进行间接修改或阅读。多个堆叠。从这个意义上说,堆叠是CPU结构的一个元素。
CPU堆和堆肥与CPU和登记簿如何与记忆工作、机器组合语言如何运作、而不是高层次语言本身有实际联系,即使这些语言能决定小事。
所有现代CPU都与“相同”微处理理论合作: 它们都基于所谓的“ 注册者” 和一些“ 堆叠” 来取得性能。 所有 CPU 从一开始就有堆叠登记簿, 并且他们总是在这里, 正如我所知。 议会语言从一开始就是相同的, 尽管有各种变化... 直到微软及其中间语言(IL) 改变了范式, 有了OO虚拟机组装语言。 因此, 我们将来可以有一些 CLI/ CIL CPU (一个MS项目) 。
CPU有堆叠登记册来加速存取记忆,但与其他登记册相比,这些登记册有限,无法完全获取进程的所有可用内存。 这就是为什么我们谈论堆叠和堆积分配的原因。
简而言之,总的来说,堆积是慢慢的,用于“全球”实例和对象内容,因为堆叠的体积很小,速度很快,用于“本地”变量和参考(隐藏的指针要忘记管理它们)。
所以当我们用一种方法使用新关键字时, 引用( int) 是在堆叠中创建的, 但是对象及其所有内容( 价值类型以及对象) 都在堆积中创建, 如果我记得的话。 但是本地基本值类型和阵列是在堆叠中创建的 。
内存存存取的差别在于单元格参考级别:处理堆积,即整个过程的记忆,在处理CPU登记册方面要求比处理堆叠更为复杂,因为如果我记得,CPU堆叠登记册被用作基地址,因此,处理堆积存存存存取的差别就大于当地“更多”的堆叠。
这就是为什么当我们有非常长或无限的循环电话或循环时, 我们很快地被堆积物溢出, 而不冻结现代电脑上的系统...
C# Heap(ing) Vs Stack(ing) in.NET
https://en.wikipedia.org/wiki/Memory_management
https://en.wikipedia.org/wiki/Stack_register
大会语文资源:
短短
一个堆叠用于静态内存分配,一个堆叠用于动态内存分配,两者都存储在计算机的内存记录中。
详细细节
堆叠
堆栈是一个“ LIFO ” (最后的, 首先是) 数据结构, 由 CPU 相当密切地管理和优化。 函数每次声明一个新的变量时, 它就会被“ 挤压” 到堆栈。 然后, 每次函数退出, 所有被该函数推到堆栈的变量都会被解开( 也就是说, 它们会被删除 ) 。 一旦一个堆叠变量被解开, 内存区域就会被其他堆叠变量所利用 。
使用堆叠存储变量的优点是存储存储器的内存为您所管理。 您不需要手动分配内存, 也无需在不再需要时解开内存。 此外, 因为 CPU 组织堆叠内存的效率非常高, 读写到堆叠变量的速度非常快 。
更多可以找到在这里.
堆肥
您计算机的存储器中, 堆积是一个区域, 没有自动为您管理, 也没有由 CPU 进行严格管理。 它是一个更自由的存储区( 并且更大 ) 。 要在堆积上分配存储器, 您必须使用 C 函数内嵌的 malloc () 或 calloc () 。 一旦您在堆积上分配了存储器, 您就有责任使用自由的( ) 来在不再需要该存储器时处理该存储器 。
如果您不这样做, 您的程序将会有所谓的内存泄漏。 也就是说, 堆堆上的内存仍将被搁置( 并且无法用于其它进程 ) 。 正如我们在调试部分看到的那样, 有一个工具被称为Valgrind Valgrind Valgrind 瓦格林它可以帮助你发现内存漏。
与堆叠不同, 堆积的大小没有变量大小限制( 除了您的计算机的明显物理限制之外 ) 。 堆积的内存读和书写要慢一点, 因为人们必须用指针来访问堆积的内存。 我们很快会讨论指针问题 。
与堆叠不同的是,在堆积上创建的变量可以被任意函数进入,在您的程序中的任何地方。堆积变量在范围上基本上是全球性的。
更多可以找到在这里.
堆栈上分配的变量直接存储到内存中, 访问此内存的时间非常快, 程序编译时会处理其分配问题。 当函数或方法调用另一个函数, 转而调用另一个函数等时, 所有这些函数的履行将一直暂停, 直到最后一个函数返回其值。 堆栈总是保留在 LIFO 的顺序中, 最新的保留区块总是要解开的下一个块块。 这样可以非常简单地跟踪堆叠, 从堆叠中释放一个块只是调整一个指针而已 。
堆积上分配的变量的内存在运行时间分配, 访问此内存的时间稍慢一点, 但堆积大小仅受虚拟内存大小的限制。 堆积的元素不互相依赖, 随时可以随机访问。 您可以随时分配块块, 并随时释放它。 这让跟踪堆积中哪些部分在任何特定时间分配或自由, 变得更加复杂 。
如果您确切知道在编译时间之前需要分配多少数据, 您可以使用堆叠, 而它并不太大。 如果您不知道运行时需要多少数据, 或者需要分配很多数据, 您可以使用堆叠 。
在一个多轨情况下, 每串线索将有自己的完全独立的堆叠, 但是它们会共享堆叠 。 堆叠是特定的线条, 堆叠是特定的应用程序 。 堆叠很重要, 在例外处理和丝线处决中需要考虑 。
每一串线索都有堆叠, 而通常应用程序只有一堆(尽管不同类型分配的多堆线索并不罕见) 。
运行时,如果应用程序需要更多堆积,它可以从自由存储中分配内存,如果堆叠需要内存,它可以从为应用程序分配的内存中分配内存。
来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来,来你的问题的答案.
它们在多大程度上受到操作系统或语言运行时间的控制?
当线索创建时, OS 会为每个系统级线索分配书架。 通常情况下, OS 会被语言运行时间调用来分配应用程序的堆积 。
更多可以找到在这里.
其范围是什么?
上方已经给出了 。
“如果你确切知道在编译时间之前你需要分配多少数据,你可以使用堆叠。它并不太大。如果你不知道运行时你需要多少数据,或者你需要分配很多数据,你可以使用堆叠。”
更多可见于在这里.
是什么决定了每个孩子的大小?
堆叠的大小由OS当创建线索时。 程序启动时会设置堆积的大小, 但随着空间需要, 堆积会变大( 分配器要求操作系统的内存更多 ) 。
是什么让一个更快?
堆叠分配速度要快得多, 因为它实际上所做的就是移动堆叠指针。 使用记忆池, 您可以从堆积分配中获取相似的性能, 但是这伴随着一个稍微增加的复杂性和它自己的头痛。
此外,堆叠对堆积不仅是一种绩效考量;它也告诉你很多关于物体预期寿命的情况。
详情可从在这里.