什么存储在堆上以及存储在堆栈中的内容?

任何人都可以用C,C ++和Java清楚地解释。 什么都在堆栈上,什么都在堆上,什么时候分配完成。

我所知道的,

每个函数调用的基元,指针或引用变量的所有局部变量都在新的堆栈帧上。

用new或malloc创建的任何东西都在堆上。

我很困惑一些事情。

作为在堆上创建的对象的成员的引用/原语是否也存储在堆上?

那些在每个帧中递归创建的方法的本地成员呢? 它们都在堆栈中,如果是,则在运行时分配堆栈内存吗? 对于文字,它们是代码段的一部分吗? 那么C中的全局变量,C ++ / Java中的static和C中的static。

记忆中程序的结构

以下是加载到内存中时任何程序的基本结构。

+--------------------------+ | | | command line | | arguments | | (argc and argv[]) | | | +--------------------------+ | Stack | | (grows-downwards) | | | | | | | | FREE | | SPACE | | | | | | | | | | (grows upwards) Heap | +--------------------------+ | | | Initialized data | | segment | | | +--------------------------+ | | | Initialized to | | Zero (BSS) | | | +--------------------------+ | | | Program Code | | | +--------------------------+ 

几点需要注意:

  • 数据段
    • 初始化数据段(由程序员初始化为显式初始化程序)
    • 未初始化的数据段(初始化为零数据段 – BSS [带符号的块开始])
  • 代码段
  • 堆栈和堆区域

数据段

数据段包含由包含初始化值的用户显式初始化的全局和静态数据。

数据段的另一部分称为BSS(因为旧的IBM系统将该段初始化为零)。 它是内存的一部分,OS将内存块初始化为零。 这就是未初始化的全局数据和静态获取默认值为零的方式。 该区域是固定的,具有静态尺寸。

基于显式初始化,数据区域被分成两个区域,因为要初始化的变量可以逐个初始化。 但是,未初始化的变量不需要用0逐个显式初始化。 而不是那样,初始化变量的工作留给操作系统。 这种批量初始化可以大大减少加载可执行文件所需的时间。

大多数情况下,数据段的布局是在底层操作系统的控制下,仍有一些加载器对用户进行部分控制。 此信息可用于嵌入式系统等应用程序。

可以使用代码中的指针来寻址和访问此区域。 自动变量在每次需要时初始化变量时都会产生开销,并且需要代码来进行初始化。 但是,数据区中的变量没有这样的运行时重载,因为初始化只执行一次,而且在加载时也是如此。

代码段

程序代码是可执行代码可用于执行的代码区域。 这个区域也是固定的。 这只能被函数指针访问而不能被其他数据指针访问。 此处要注意的另一个重要信息是系统可能将此区域视为只读存储区域,并且在此区域中写入的任何尝试都会导致未定义的行为。

常量字符串可以放在代码或数据区域中,这取决于实现。

写入代码区域的尝试导致未定义的行为。 例如(我将仅给出基于C的示例)以下代码可能导致运行时错误甚至导致系统崩溃。

 int main() { static int i; strcpy((char *)main,"something"); printf("%s",main); if(i++==0) main(); } 

堆栈和堆区域

为了执行,程序使用两个主要部分,堆栈和堆。 堆栈帧是在堆栈中为函数和堆创建的,用于动态内存分配。 堆栈和堆是未初始化的区域。 因此,无论在内存中发生什么,都会成为在该空间中创建的对象的初始(垃圾)值。

让我们看一个示例程序来显示哪些变量存储在哪里,

 int initToZero1; static float initToZero2; FILE * initToZero3; // all are stored in initialized to zero segment(BSS) double intitialized1 = 20.0; // stored in initialized data segment int main() { size_t (*fp)(const char *) = strlen; // fp is an auto variable that is allocated in stack // but it points to code area where code of strlen() is stored char *dynamic = (char *)malloc(100); // dynamic memory allocation, done in heap int stringLength; // this is an auto variable that is allocated in stack static int initToZero4; // stored in BSS static int initialized2 = 10; // stored in initialized data segment strcpy(dynamic,”something”); // function call, uses stack stringLength = fp(dynamic); // again a function call } 

或者考虑一个更复杂的例子,

 // command line arguments may be stored in a separate area int main(int numOfArgs, char *arguments[]) { static int i; // stored in BSS int (*fp)(int,char **) = main; // points to code segment static char *str[] = {"thisFileName","arg1", "arg2",0}; // stored in initialized data segment while(*arguments) printf("\n %s",*arguments++); if(!i++) fp(3,str); } 

希望这可以帮助!

在C / C ++中:局部变量分配在当前堆栈帧(属于当前函数)上。 如果静态分配对象,则在堆栈上分配整个对象,包括其所有成员变量。 使用递归时,每次调用函数时都会创建一个新的堆栈帧,并在堆栈上分配所有局部变量。 堆栈通常具有固定大小,并且该值通常在编译/链接期间写入可执行二进制头中。 然而,这是特定于操作系统和平台的,某些操作系统可能会在需要时动态增加堆栈。 因为堆栈的大小通常是有限的,所以当您使用深度递归时,或者有时甚至在静态分配大型对象时没有递归时,您可能会耗尽堆栈。

堆通常被视为无限空间(仅受可用物理/虚拟内存的限制),您可以使用malloc / new(以及其他堆分配函数)在堆上分配对象。 在堆上创建对象时,将在其中创建其所有成员变量。 您应该将对象视为连续的内存区域(此区域包含成员变量和指向虚方法表的指针),无论它在何处分配。

文字,常量和其他“固定”的东西通常被编译/链接到二进制文件中作为另一个段,所以它不是真正的代码段。 通常,您无法在运行时从此段中分配或释放任何内容。 然而,这也是特定于平台的,它可能在不同平台上的工作方式不同(例如,iOS Obj-C代码有很多常量引用直接插入到代码段之间,在函数之间)。

至少在C和C ++中,这是特定于实现的。 标准没有提到“堆栈”或“堆”。

在Java中,局部变量可以分配给堆栈(除非优化掉)

对象中的基元和引用位于堆上(因为对象位于堆上)

在创建线程时预先分配堆栈。 它不使用堆空间。 (但是创建一个线程会导致创建一个线程本地分配缓冲区,这会大大减少可用内存)

将唯一的字符串文字添加到堆中。 原始文字可能在某个地方的代码中(如果没有被优化掉)是否一个字段是静态的也没有区别。

Java虚拟机规范的 第3.5节描述了运行时数据区域(堆栈和堆)。

C和C ++语言标准都没有指定是将某些东西存储在堆栈还是堆栈中。 它们只定义对象的生命周期,可见性和可修改性; 由实现将这些需求映射到特定平台的内存布局。

通常 ,使用*alloc函数分配的任何内容都驻留在堆上,而auto变量和函数参数驻留在堆栈上。 字符串文字可能位于“其他地方”(它们必须在程序的生命周期内分配和可见,但尝试修改它们是未定义的); 某些平台使用单独的只读内存段来存储它们。

请记住,有一些真正奇怪的平台可能不符合常见的堆栈堆模型。

要回答关于C ++堆栈和堆栈的问题:

我首先要说的是,在没有new的情况下创建的对象被存储为堆栈中的连续单元,或者在某个全局段(特定于平台)中是全局的。

对于在堆上使用new创建的对象,其成员变量作为堆上的一个连续内存块存储。 对于基元和嵌入对象的成员变量,就是这种情况。 对于作为指针和引用类型成员变量的成员变量,原始指针值存储在对象中。 该值指向的内容可以存储在任何位置(堆,堆栈,全局)。 一切皆有可能。

对于对象方法中的局部变量,它们存储在堆栈中,而不是存储在堆上的对象连续空间内。 堆栈通常在运行时创建固定大小。 每个线程有一个。 局部变量甚至可能不占用堆栈上的空间,因为它们可能会被优化(如Paul所说)。主要的一点是它们不是因为它们是堆上对象的成员函数而不在堆上。 如果它们是指针类型的局部变量,它们可以存储在堆栈上并指向堆或堆栈上的某些内容!