一文搞懂栈区和堆区
4958
2019.08.01
2019.08.01
发布于 未知归属地

曾经有一段时间,我一直分不清栈区和堆区的区别。我去了解过内存管理,也知道 new 一个对象会被放在堆区。但是放在堆区到底意味着什么呢?堆区跟栈区有什么不一样呢?

在 Java,C#中,基本值类型是存放在栈区的,引用类型是存在堆区的。在 C++ 中,并不强调在如何在堆区,栈区分配内存。它是用 自动存储周期动态存储周期 来区分的。本地变量是自动存储周期,编译器会把它存在栈区。而对象是动态分配内存的(用 new 关键字创建),存在堆区。在没有垃圾回收机制的语言中,如果堆区中的内存不释放的话会造成内存泄露。

image.png

那什么时候编译器会在栈区给对象分配内存呢?这里有一点需要注意的是,存储对象的栈区跟运行时栈是同一个。运行时栈的存在是为了让程序运行,进行方法调用,其中包含了很多栈帧。一个栈帧中包含了一个方法调用所需用到的所有数据:包括参数,返回地址,本地变量等等。在栈区分配的对象是本地变量的一部分。

栈帧只存在于方法调用过程中,这样做的好处是不需要担心栈分配的对象会造成内存溢出,但相对的一旦方法调用结束了我们就再也没法访问这个对象了。

只有在编译时就知道大小的对象才能在栈中分配内存。我们都知道栈帧的大小在编译时期就已经确定了,也正是因为这样才可以通过相对栈顶指针固定的偏移量来访问栈中的对象。

f7f6863f0c069164ab64b808edd9d448503fda338c943fabec2f2db8e816b70c-stack.jpeg

那什么时候在堆区给对象分配内存呢?你可以把堆区当成一个完全独立于运行时栈的额外存储空间。堆区的内存跟程序运行,方法调用是分开的。当我们在堆区分配内存的时候,操作系统会帮我们找到一块没有被占用的内存。堆区的对象在方法调用结束之后也还是存在于内存中,这是一个很好的特性,但相对的现在需要主动释放堆区中无用的内存了,不然就可能会发生内存泄露。在有内存回收机制的语言中,垃圾回收 会自动释放堆区无用的内存,从而来防止内存泄露的发生。

内存和栈在内存分配的背景下它们的边界其实非常模糊的,理解运行时栈是打破这个迷雾的光明。

我也是在读了 深入理解计算机系统 之后才真正搞懂了堆栈。

6bcbf5784b8efbbc83a2e013c0542c0552502d69948ef2b9a9b69230988d5cf3-book.jpeg

这本书里面有几个章节是说程序运行时在机器层面具体是怎样的。其中有一个章节会讲解汇编语言,这一章有一个子章节叫做 运行时栈。其中会详细解释方法调用中运行时栈到底是怎样的,也会更细致地讲解参数,返回值具体是存在哪的。如果你对这方面的知识感兴趣的话,可以去看一下这本书。

评论 (0)