堆栈和堆:
堆栈
堆栈只是每个程序启动时具有的特定内存范围。当该程序运行时,CPU实际上会存储指向堆栈顶部的指针。
当调用函数时,由编译器生成的代码将值(参数的副本以及调用函数的代码的返回地址)写入到被此堆栈指针(SP)引用的内存中。然后它修改SP指向稍微远一点的位置,即参数之后的位置。
当您的函数返回时,它将返回值写入由SP指向的内存位置,然后跳转代码执行回调用函数的代码。该代码然后从SP位置复制返回值,并递减SP。
这个区域称为堆栈,因为当您声明局部变量或使用参数调用函数时,程序会将值复制到其中。
然后在从函数返回时,它会弹出参数和局部变量。
(这是理论上的工作原理。在实践中,编译器将写入指令以将值复制到CPU寄存器中,如果可以的话。还有返回值)。
堆
堆只是程序分配的所有其他内存,通常通过系统调用(Linux中的brk)(由C中的malloc调用)来分配。程序可以有许多内存块,它要求操作系统为其分配。这些内存块(作为整体)称为堆。
在Java中:
当您使用指针变量并将其分配给使用new ExampleObject()创建的对象时,实际上正在执行两件事。在这种情况下,指针(引用)变量将在堆栈指针位置创建。然后移动堆栈指针(增加8个字节,即指针值的大小),然后new()函数将从堆区域获取新的内存引用,然后该引用的值将被复制到局部指针变量中。
实际上,像Java这样的语言在执行程序时,已经由操作系统分配了一定大小的堆栈和内存(称为堆),只有当空间不足时才会请求更多内存。
阅读如何CPU工作,特别是它们有存储值的寄存器,其中之一是堆栈指针。此外,阅读它们如何执行加法和减法也很重要,因为它们通常不会将一个地址中的数与另一个地址中的数相加(例如在添加时)。如果您查看汇编指令(类似于Java字节码),它们更经常做的是:
例如,一个函数 int addnum(int a, int b) {return a+b;}
a. 从SP中加载数字,即SP指向的位置,到寄存器1中
b. 从SP之前的位置(SP-1)加载数字到寄存器2中
c. 调用Add CPU指令,将结果存储在寄存器R3中
d. 将R3值复制到SP+1中
可能看起来像这样:
像这样调用代码:(请注意,这些是虚构的示例CPU指令 - 它们对于每个CPU都不同,并且Java有自己的类似字节码。我只是用于举例STORESP => 写入堆栈,LOADSP => 从堆栈指针加载)
int x
x = addnum(9,6)
INCSP +1 #allocate x at location SP and increment SP by 1
# start function call
# make 3 spaces, for a, b, and b and return value
INCSP +3 #add 3 to SP register
STORESP 9,0 # copy 9 value to SP-0
STORESP 6,-1 # copy 6 value to SP-1
JUMP addnum # jump to executing the function code
然后,就是函数本身。
LOADSP,0,R1
LOADSP,-1,R2
ADDREG,R1,R2,R3
STORESP,R3,-2
RETURN
然后再次调用函数:
将结果存储在x中(复制SP-2)(到SP-3)。
LOADSP,-2,R1
STORESP,R1,-3
现在函数调用已经完成。因此,通过将SP减去3来丢弃为a和b分配的堆栈空间以及返回值。
ADDSP -3
现在结果存储在 'x' 中。
当然,这只是一个简化且不准确的例子,只是为了帮助理解。
但是,如果您能够了解这些低级别的东西是如何工作的,即使只是像添加两个数字这样的基本操作,那么它也将帮助您理解函数中参数是如何传递的以及堆栈概念的重要性。
祝好运!