gpt4 book ai didi

java - Java “Foo f = new Foo() ”中的对象初始化是否与在C中将malloc用作指针相同?

转载 作者:行者123 更新时间:2023-12-02 22:59:44 27 4
gpt4 key购买 nike

我试图了解Java中对象创建背后的实际过程-并且我想使用其他编程语言。

假设Java中的对象初始化与对C中的结构使用malloc相同是错误的吗?

例子:

Foo f = new Foo(10);
typedef struct foo Foo;
Foo *f = malloc(sizeof(Foo));

这就是为什么说对象位于堆而不是堆栈上的原因吗?因为它们本质上只是数据指针?

最佳答案

在C语言中,malloc()在堆中分配一个内存区域,并返回指向该区域的指针。那就是你所得到的。内存是未初始化的,您不能保证它全为零或其他任何值。

在Java中,像new一样,调用malloc()会执行基于堆的分配,但是您还会获得很多额外的便利(如果愿意,也可以增加开销)。例如,您不必显式指定要分配的字节数。编译器根据您要分配的对象类型为您解决问题。此外,还调用了对象构造函数(如果您想控制初始化的发生方式,可以将参数传递给该对象的构造函数)。当new返回时,可以确保您有一个已初始化的对象。

但是,是的,在调用结束时,malloc()new的结果都只是指向一些基于堆的数据块的指针。

问题的第二部分询问堆栈和堆之间的区别。通过学习(或阅读有关)编译器设计类(class),可以找到更全面的答案。操作系统类(class)也将有所帮助。关于堆栈和堆,SO上也有许多问题和答案。

话虽如此,我将做一个概括性的概述,希望不会太冗长,目的是在较高的层次上解释这些差异。

从根本上讲,拥有两个内存管理系统(即堆和堆栈)的主要原因是为了提高效率。第二个原因是,在某些类型的问题上,每种方法都比另一种方法更好。

作为一个概念,堆栈对于我来说比较容易理解,所以我从堆栈开始。让我们考虑一下C中的此功能...

int add(int lhs, int rhs) {
int result = lhs + rhs;
return result;
}

以上似乎很简单。我们定义一个名为 add()的函数,并传入左右加数。该函数将它们相加并返回结果。请忽略所有可能发生的情况(例如溢出),这与讨论无关。
add()函数的用途似乎很简单,但是我们可以说一下它的生命周期吗?特别是它的内存利用率需要吗?

最重要的是,编译器先验地(即在编译时)知道数据类型有多大,将使用多少种。 lhsrhs参数是 sizeof(int),每个4字节。变量 result也是 sizeof(int)。编译器可以告诉 add()函数使用 4 bytes * 3 ints或总共12个字节的内存。

调用 add()函数时,称为堆栈指针的硬件寄存器将具有指向堆栈顶部的地址。为了分配 add()函数需要运行的内存,所有函数输入代码需要执行的是发出一条汇编语言指令,以将堆栈指针寄存器的值减12。这样做,它将在堆栈上创建存储以用于三个 ints,每个分别代表 lhsrhsresult。通过执行一条指令来获取所需的存储空间在速度方面是一个巨大的胜利,因为一条指令往往在一个时钟节拍内执行(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指令来完成所有这些操作,那么您是对的!这非常复杂,需要一段时间。

但是堆很大。比堆栈大得多。我们可以从它们那里获得很多内存,当我们在编译时不知道需要多少内存时,它们就很棒。因此,我们要权衡一个托管内存系统的速度,该内存系统会礼让我们,而不是在尝试分配太大的内存时崩溃。

我希望这有助于回答您的一些问题。如果您想对上述任何一项进行澄清,请告诉我。

关于java - Java “Foo f = new Foo() ”中的对象初始化是否与在C中将malloc用作指针相同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58514855/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com