在思考Java / C#中的编程时,我想知道如何在内存中表示属于对象的方法,以及这个事实如何涉及多线程。
我试着对这些问题下定决心,但我对他们的答案非常不确定。
答案 0 :(得分:17)
源代码中的每个方法(使用Java,C#,C ++,Pascal,我认为每个OO和过程语言......)在二进制文件和内存中只有一个副本。
一个对象的多个实例具有单独的字段,但它们共享相同的方法代码。从技术上讲,有一个过程采用隐藏的this
参数来提供对对象执行方法的错觉。实际上,您正在调用一个过程并将结构(一个字段)与其他参数一起传递给它。这是一个简单的Java对象和或多或少等效的伪C代码:
class Foo {
private int x;
int mulBy(int y) {
return x * y
}
}
Foo foo = new Foo()
foo.mulBy(3)
转换为此pseude-C代码(封装由编译器和运行时/ VM强制执行):
struct Foo {
int x = 0;
}
int Foo_mulBy(Foo *this, int y) {
return this->x * y;
}
Foo* foo = new Foo();
Foo_mulBy(foo, 3)
您必须在代码和局部变量及其操作的参数(数据)之间划出差异。数据存储在每个线程本地的调用堆栈中。代码可以由多个线程执行,每个线程都有自己的指令指针副本(放在当前执行的方法中)。另外因为this
是一个参数,它是线程本地的,所以每个线程可以同时在不同的对象上运行,即使它运行相同的代码。
话虽如此,你不能只修改一个实例的方法,因为方法代码在所有实例之间共享。
答案 1 :(得分:4)
我将尝试在C#的上下文中回答这个问题。基本上有3种不同类型的方法
执行代码时,基本上有两种在堆上形成的对象。
回答你的问题,
这是理解对象的错误方法。 所有方法仅按类型 。以这种方式看待它。方法只是一组指令。第一次调用特定方法时,IL代码被JIT到本机指令中并保存在内存中。下次调用此地址时,将从方法表中获取地址,并再次执行相同的指令。
2. 如果是后者,执行线程如何知道要使用哪个对象的属性? 对Type进行的每个静态方法调用都会导致从相应的Type对象中查找方法表并查找JITed指令的地址。如果方法不是静态的,则在线程的本地堆栈上维护调用该方法的相关对象。基本上,您可以获得堆栈中最近的对象。那就是 总是 我们想要调用该方法的对象。
3. 是否可以在C#中使用反射修改方法的代码,并且只修改同一类型的多个对象中的一个对象? 不,现在不可能。 (而且我很感激)。原因是反射只允许代码检查。如果你弄清楚某些方法实际意味着什么,你将无法在同一个程序集中更改代码。
答案 2 :(得分:3)
Java规范没有规定如何进行内存布局,不同的实现可以做任何他们喜欢的事情,只要它符合重要的规范。
话虽如此,主流Oracle JVM(HotSpot)的工作原理是oops - 普通对象指针。它们包含两个单词标题,后跟数据,其中包含实例成员字段(原始类型的内联存储,以及引用成员字段的指针)。
两个标题词中的一个 - 类词 - 是指向klassOop的指针。这是一种特殊类型的oop,它包含指向类的实例方法的指针(基本上是Java等价的C ++ vtable)。 klassOop类似于与Java类型对应的Class对象的VM级表示。
如果您对低级细节感到好奇,可以通过查看OpenJDK源代码来了解一些oop类型的定义(klassOop是一个很好的起点)。
tl; dr Java为每种类型的每种方法保存一个代码块。代码块在每个类型的实例之间共享,隐藏此指针用于知道要使用的实例成员。