在C语言中,malloc()
在堆中分配一个内存区域并返回一个指向它的指针。那就是你所得到的。内存是未初始化的,您不能保证它全为零或其他任何值。
在Java中,调用new
就像一样进行基于堆的分配malloc()
,但是您还会获得很多额外的便利(如果愿意,也可以增加开销)。例如,您不必显式指定要分配的字节数。编译器会根据您要分配的对象类型为您解决问题。此外,还调用了对象构造函数(如果您想控制初始化的发生方式,可以将其传递给参数)。当new
返回时,你保证有一个已初始化的对象。
但是,是的,在调用结束时,malloc()
和的结果new
都只是指向某些基于堆的数据块的指针。
问题的第二部分询问堆栈和堆之间的区别。通过学习(或阅读有关)编译器设计课程,可以找到更全面的答案。操作系统课程也将有所帮助。关于堆栈和堆,SO上也有许多问题和答案。
话虽如此,我将给出一个总体概述,我希望它不要太冗长,并且旨在从较高的角度解释这些差异。
从根本上讲,拥有两个内存管理系统(即堆和堆栈)的主要原因是为了提高效率。第二个原因是,在某些类型的问题上,每种方法都比另一种方法更好。
作为一个概念,堆栈对于我来说比较容易理解,因此我从堆栈开始。让我们考虑一下C中的此功能...
int add(int lhs, int rhs) {
int result = lhs + rhs;
return result;
}
以上似乎很简单。我们定义一个名为的函数,add()
并传入左右加数。该函数将它们相加并返回结果。请忽略所有可能发生的情况(例如溢出),这与讨论无关。
该add()
函数的目的似乎很简单,但是我们可以说出它的生命周期吗?特别是它的内存利用率需要吗?
最重要的是,编译器先验地(即在编译时)知道数据类型有多大以及将使用多少种。的lhs
和rhs
参数是sizeof(int)
,4个字节的每个。变量result
也是sizeof(int)
。编译器可以告诉该add()
函数使用4 bytes * 3 ints
或总共12个字节的内存。
add()
调用该函数时,称为堆栈指针的硬件寄存器将具有指向堆栈顶部的地址。为了分配add()
函数需要运行的内存,所有函数输入代码需要执行的是发出一条汇编语言指令,以将堆栈指针寄存器的值减12。这样做,它将在堆栈上创建三个存储空间。ints
,每一个用于lhs
,rhs
,和result
。通过执行一条指令来获取所需的存储空间在速度方面是一个巨大的胜利,因为一条指令往往在一个时钟周期内执行(1 GHz CPU每秒执行十亿分之一秒)。
同样,从编译器的角度来看,它可以创建一个映射到看起来像索引数组的变量的映射:
lhs: ((int *)stack_pointer_register)[0]
rhs: ((int *)stack_pointer_register)[1]
result: ((int *)stack_pointer_register)[2]
同样,所有这些都非常快。
该add()
函数退出时必须进行清理。它通过从堆栈指针寄存器中减去12个字节来实现。这类似于对的调用,free()
但是它仅使用一条CPU指令,并且只需要花费一勾。非常非常快。
现在考虑基于堆的分配。当我们不知道先验需要多少内存时(即我们只会在运行时了解它),这就会起作用。
考虑以下功能:
int addRandom(int count) {
int numberOfBytesToAllocate = sizeof(int) * count;
int *array = malloc(numberOfBytesToAllocate);
int result = 0;
if array != NULL {
for (i = 0; i < count; ++i) {
array[i] = (int) random();
result += array[i];
}
free(array);
}
return result;
}
请注意,该addRandom()
函数在编译时不知道count
参数的值。因此,array
像我们将其放入堆栈那样尝试定义是没有意义的,如下所示:
int array[count];
如果count
太大,可能会导致我们的堆栈变得太大,并覆盖其他程序段。当此堆栈溢出发生时,您的程序将崩溃(或更糟)。
因此,在直到运行时都不知道需要多少内存的情况下,我们使用malloc()
。然后,我们可以只在需要时问我们需要的字节数,malloc()
然后检查它是否可以出售那么多字节。如果可以的话,很好,我们将其取回,否则,我们将得到一个NULL指针,该指针告诉我们对malloc()
失败的调用。值得注意的是,该程序不会崩溃!当然,作为资源程序员,您可以决定如果资源分配失败,则不允许您的程序运行,但是由程序员启动的终止不同于虚假的崩溃。
所以现在我们必须回头看看效率。堆栈分配器非常快-一条分配指令,一条释放指令,这是由编译器完成的,但是请记住,堆栈是用于已知大小的局部变量之类的,因此它往往很小。
另一方面,堆分配器要慢几个数量级。它必须在表中进行查找,以查看其是否有足够的可用内存来出售用户所需的内存量。它有它出售该内存,以使后更新这些表肯定没有其他人可以使用块(这簿记可能需要分配到备用内存本身除了它计划鬻)。分配器必须采用锁定策略,以确保它以线程安全的方式出售内存。当记忆终于来了free()
d,它发生在不同的时间,并且通常没有可预见的顺序,分配器必须找到连续的块并将它们缝合在一起以修复堆碎片。如果这听起来需要完成一条以上的CPU指令,那是对的!这非常复杂,需要一段时间。
但是堆很大。比堆栈大得多。我们可以从它们那里获得很多内存,当我们在编译时不知道需要多少内存时,它们就很棒。因此,我们要权衡一个托管内存系统的速度,该内存系统会礼让我们,而不是在尝试分配太大的内存时崩溃。
我希望这有助于回答您的一些问题。如果您想对上述任何一项进行澄清,请告诉我。