内存中的方法表示是什么?

Emi*_*elt 22 c# java memory clr jvm

在思考Java/C#中的编程时,我想知道如何在内存中表示属于对象的方法,以及这个事实如何涉及多线程.

  1. 是单独为内存中的每个对象实例化的方法,还是同一类型的所有对象共享方法的一个实例?
  2. 如果是后者,执行线程如何知道要使用哪个对象的属性?
  3. 是否可以在C#中使用反射修改方法的代码,并且只能修改同一类型的多个对象中的一个对象?
  4. 一个不使用类属性的静态方法总是线程安全吗?

我试着对这些问题下定决心,但我对他们的答案非常不确定.

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是一个参数,它是线程本地的,所以每个线程可以同时在不同的对象上操作,即使它运行相同的代码.

也就是说,您无法仅修改一个实例的方法,因为方法代码在所有实例之间共享.


kit*_*yst 5

Java规范并没有规定如何进行内存布局,并且只要实现符合重要的规范,各种实现就可以随心所欲。

话虽如此,主流Oracle JVM(HotSpot)的工作原理就是所谓的oops-普通对象指针。它们由标头的两个词组成,后跟包含实例成员字段的数据(内联存储为原始类型,并作为引用成员字段的指针)。

两个标题词之一(类词)是指向klassOop的指针。这是一种特殊的oop,它持有指向类的实例方法的指针(基本上,Java等效于C ++ vtable)。klassOop是对应于Java类型的Class对象的VM级别表示。

如果您对底层细节感到好奇,可以通过在OpenJDK源代码中查找某些oop类型的定义来了解更多信息(klassOop是一个很好的起点)。

tl; dr Java为每种类型的每种方法保留一个代码块。代码的Blob在该类型的每个实例之间共享,并且隐藏该指针用于了解要使用哪个实例的成员。