好吧,我已经完成了一项任务,基本上可以找出内存分配如何适用于我将使用的任何语言.经过一些研究,我有一些问题和怀疑,我希望得到一些见解.例如:
我在这里读到Java确切地指定了堆栈内容的组织方式.查看JVM规范结构,它基本上表示堆栈包含框架,并且框架通过正确分配变量和函数来包含类中的内容.也许我在这里遗漏了一些东西,但我不明白这与C++的作用有何不同.我问,因为第一个链接说Java的堆栈内容规范避免了编译器的不兼容性.
此外,我还没有找到内存段是如何精确组织在一起的.例如,我知道内存分为全局变量,调用堆栈,堆和C++代码,但我不知道堆的地址是否高于堆栈,或者是否取决于实现.我也想知道Java程序是否具有更多,以及它是如何布局的.我想有一个标准,因为JVM必须知道它在哪里使用它,虽然我想它可以只有指针并将其余部分留给操作系统.我想,至少必须有一个事实上的标准.
另一个我不理解的是运行时常量池.它应该是"类文件中的constant_pool表的每类或每接口运行时表示",但我不认为我理解它的作用.它似乎有一个标签来表明有问题的结构是什么类型的?然后是结构的名称(由程序员给出或由底层系统分配?)然后看起来它的其余部分随着标签所描述的不同而变化(线程,数组等).
如果我对运行时常量池的解释是正确的,那么为什么它们和堆栈帧一样必要?是因为堆栈帧只处理堆栈段,运行时常量池还必须有堆分配内存的指针吗?
查看JVM规范结构,它基本上表示堆栈包含框架,并且框架通过正确分配变量和函数来包含类中的内容.也许我在这里遗漏了一些东西,但我不明白这与C++的作用有何不同.我问,因为第一个链接说Java的堆栈内容规范避免了编译器的不兼容性.
在实践中,C++编译器遵循相同的基本策略.然而,标准委员会并未将其视为语言问题.相反,C++编译器遵循这个系统,因为这是大多数CPU和操作系统的设计方式.不同的平台不同意数据是传递给堆栈上的函数还是通过寄存器(RISC机器)传递,无论堆栈是增长还是减少,是否有不同的调用约定允许"正常"调用使用堆栈而其他人使用某些签名else(例如,__ fastcall和naked),是否存在嵌套函数,尾调用支持等等.
事实上,符合标准的C++编译器可以编译成类似于Scheme VM的东西,其中"堆栈"是非常不同的,因为Scheme要求实现支持尾调用和延续.我从来没有见过这样的东西,但这是合法的.
如果您尝试编写垃圾收集器,"编译器不兼容性"最明显:
当前函数及其所有调用者的所有局部变量都在[""堆栈中,但请考虑ucontext.h和Windows Fibers ].对于每个平台(意思是OS + CPU +编译器),有一种方法可以找出["堆栈"]的位置.Tamarin这样做,然后它在GC期间扫描所有内存以查看当地人指向的位置....
这个魔法存在于一个宏MMGC_GET_STACK_EXTENTS中,在头文件MMgc/GC.h中定义....... [T]这是每个平台的单独实现.
在任何给定时刻,一些本地人可能在CPU寄存器中而不在堆栈中.为了解决这个问题,宏使用几行汇编代码将所有寄存器的内容转储到堆栈中.这样MMgc就可以扫描堆栈,它会看到所有局部变量.
此外,Java中的对象通常不会在堆栈上分配.而是引用它们.int,double,booleans和其他原始类型确实在堆栈上分配.在C++中,任何东西都可以在堆栈上分配,它有自己的优缺点列表.
另一个我不理解的是运行时常量池.它应该是"类文件中的constant_pool表的每类或每接口运行时表示",但我不认为我理解它的作用.
考虑:
String s = "Hello World"; int i = "Hello World".length(); int j = 5;
s,i和j都是变量,并且可以在程序的某个稍后的位置进行更改.但是,"Hello World"是String类型的对象,无法更改,5是无法更改的int,"Hello World".length()可以在编译时确定,始终返回11.这些常量是可以在它们上调用有效的对象和方法(好吧,至少在String上),因此需要在某处分配它们.但它们永远无法改变.如果这些常量属于某个类,则它们将分配在每个类的常量池中.其他不属于类的常量数据(如main()线程的ID)在每个运行时常量池中分配("运行时",在本例中为"JVM实例").
C++标准有一些关于类似技术的语言,但实现方式是二进制格式(ELF,a.out,COFF,PE等).标准期望整数数据类型(bool,int,long等)或c样式字符串的常量实际上保存在二进制文件的常量部分中,而其他常量数据(双精度数,浮点数,类)可能存储作为变量以及一个标志,表示"变量"不可修改(使用整数和c样式字符串常量存储它们也是可以接受的,但许多二进制格式不能使它成为一个选项).
一般来说,当一次打开一个以上的程序副本时,可以共享二进制文件的"常量数据部分"(因为在程序的每个副本中,常量数据将是相同的). 在ELF上,此部分称为.rodata部分.