Tom*_*icz 19
源代码中的每个方法(用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)
Run Code Online (Sandbox Code Playgroud)
转换为此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)
Run Code Online (Sandbox Code Playgroud)
您必须在代码和局部变量及其操作的参数(数据)之间划出差异.数据存储在每个线程本地的调用堆栈中.代码可以由多个线程执行,每个线程都有自己的指令指针副本(放在当前执行的方法中).另外因为this是一个参数,它是线程本地的,所以每个线程可以同时在不同的对象上操作,即使它运行相同的代码.
也就是说,您无法仅修改一个实例的方法,因为方法代码在所有实例之间共享.
Java规范并没有规定如何进行内存布局,并且只要实现符合重要的规范,各种实现就可以随心所欲。
话虽如此,主流Oracle JVM(HotSpot)的工作原理就是所谓的oops-普通对象指针。它们由标头的两个词组成,后跟包含实例成员字段的数据(内联存储为原始类型,并作为引用成员字段的指针)。
两个标题词之一(类词)是指向klassOop的指针。这是一种特殊的oop,它持有指向类的实例方法的指针(基本上,Java等效于C ++ vtable)。klassOop是对应于Java类型的Class对象的VM级别表示。
如果您对底层细节感到好奇,可以通过在OpenJDK源代码中查找某些oop类型的定义来了解更多信息(klassOop是一个很好的起点)。
tl; dr Java为每种类型的每种方法保留一个代码块。代码的Blob在该类型的每个实例之间共享,并且隐藏该指针用于了解要使用哪个实例的成员。