Что такое метод представления в памяти? - PullRequest
21 голосов
/ 21 ноября 2011

Подумав немного о программировании на Java / C #, я удивился, как методы, принадлежащие объектам, представлены в памяти и как этот факт касается многопоточности.

  1. Является ли метод, созданный для каждого объекта в памяти, отдельно или все объекты одного типа совместно используют один экземпляр метода?
  2. Если последнее, как исполняющий поток узнает, какой объект атрибуты использовать?
  3. Можно ли изменить код метода в C # с отражением для одного и только одного объекта из множества объектов того же типа?
  4. Является ли статический метод, который не использует атрибуты класса, всегда потокобезопасным?

Я пытался решить эти вопросы, но я не очень уверен в их ответах.

Ответы [ 3 ]

17 голосов
/ 21 ноября 2011

Каждый метод в вашем исходном коде (в Java, C #, C ++, Pascal, я думаю, что каждый ОО и процедурный язык ...) имеет только одну копию в двоичных файлах и в памяти.

Несколько экземпляров одного объекта имеют отдельные поля, но все имеют один и тот же код метода. Технически существует процедура, которая принимает скрытый параметр this, чтобы создать иллюзию выполнения метода над объектом. В действительности вы вызываете процедуру и передаете ей структуру (пакет полей) вместе с другими параметрами. Вот простой объект Java и более или менее эквивалентный псевдо-C код:

class Foo {
  private int x;

  int mulBy(int y) {
    return x * y
  }
}

Foo foo = new Foo()
foo.mulBy(3)

переводится в этот код псевдо-C (инкапсуляция выполняется компилятором и средой выполнения / виртуальной машиной):

struct Foo {
    int x = 0;
}

int Foo_mulBy(Foo *this, int y) {
    return this->x * y;
}

Foo* foo = new Foo();
Foo_mulBy(foo, 3)

Вы должны провести различие между кодом и локальными переменными и параметрами, с которыми он работает ( data ). Данные хранятся в стеке вызовов, локально для каждого потока. Код может быть выполнен несколькими потоками, каждый поток имеет свою собственную копию указателя инструкции (место в методе, который он выполняет в настоящее время). Кроме того, поскольку this является параметром, он является локальным для потока, поэтому каждый поток может одновременно работать с другим объектом, даже если он выполняет один и тот же код.

При этом нельзя изменять метод только одного экземпляра, поскольку код метода является общим для всех экземпляров.

4 голосов
/ 22 ноября 2011

Я попытаюсь ответить на этот вопрос в контексте C #. В основном есть 3 различных типа методов

  • виртуальный
  • невиртуальном
  • статические

Когда ваш код выполняется, у вас обычно есть два вида объектов, которые формируются в куче.

  • Объект, соответствующий типу объекта. Это называется Type Object. Он содержит указатель объекта типа, индекс блока синхронизации, статические поля и таблицу методов.
  • Объект, соответствующий самому объекту, который содержит все нестатические поля.

В ответ на ваши вопросы

  1. Создается ли метод для каждого объекта в памяти отдельно или все объекты одного типа совместно используют один экземпляр метода?

Это неправильный способ понимания объектов. Все методы для типа только . Рассмотрим этот вариант. Метод - это просто набор инструкций. При первом вызове определенного метода код IL JIT вставляется в собственные инструкции и сохраняется в памяти. При следующем вызове этот адрес выбирается из таблицы методов, и те же инструкции выполняются снова.

2. Если последнее, как исполняющий поток узнает, какие атрибуты объекта использовать? Каждый статический вызов метода для типа приводит к поиску таблицы методов из соответствующего объекта типа и поиску адреса инструкции JITed. В случае методов, которые не являются статическими, соответствующий объект, для которого вызывается метод, сохраняется в локальном стеке потока. По сути, вы получаете ближайший объект в стеке. Это всегда объект, для которого мы хотим, чтобы метод был вызван.

3. Можно ли изменить код метода в C # с отражением для одного и только одного объекта из множества объектов одного типа? Нет, сейчас это невозможно. (И я благодарен за это). Причина в том, что отражение допускает только проверку кода. Если вы выясните, что на самом деле означает какой-либо метод, вы не сможете изменить код в той же сборке.

3 голосов
/ 22 ноября 2011

Спецификации Java не предписывают, как делать разметку памяти, и различные реализации могут делать все, что им нравится, при условии, что это соответствует спецификации там, где это важно.

Сказав это, основной JVM Oracle (HotSpot) работает от вещей, называемых упс - указатели на обычные объекты. Они состоят из двух слов заголовка, за которыми следуют данные, которые содержат поля элементов экземпляра (хранятся встроенными для примитивных типов и как указатели для полей ссылочных элементов).

Одно из двух слов заголовка - слово класса - является указателем на klassOop. Это особый тип oop, который содержит указатели на методы экземпляра класса (в основном Java-эквивалент C ++ vtable). KlassOop является своего рода представлением уровня Class объекта Class, соответствующего типу Java.

Если вам интересны подробности низкого уровня, вы можете узнать гораздо больше, посмотрев в исходном коде OpenJDK определение некоторых типов oop (klassOop - хорошее место для начала).

tl; dr Java содержит один блок кода для каждого метода каждого типа. Капли кода распределяются между каждым экземпляром типа, и эти скрытые указатели используются, чтобы знать, какие члены экземпляра использовать.

...