Подтвердить что ты не робот

Что представляет собой представление метода в памяти?

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

  • Является ли метод, созданный для каждого объекта в памяти отдельно или выполняемый все объекты одного типа совместно используют один экземпляр метода?
  • Если последний, как исполняющий поток знает, какой объект атрибуты для использования?
  • Можно ли изменить код метода в С# с отражением для одного и только один объект многих объектов тот же тип?
  • Статический метод, который не использует атрибуты класса, всегда безопасен в потоке?

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

4b9b3361

Ответ 1

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

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

class Foo {
  private int x;

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

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

переводится на этот код псевдо-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 является параметром, он является поточно-локальным, поэтому каждый поток может работать на другом объекте одновременно, даже если он выполняет тот же код.

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

Ответ 2

Я попытаюсь ответить на это в контексте С#. Существуют в основном 3 разных типа методов

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

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

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

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

  • Является ли метод, созданный для каждого объекта в памяти отдельно или все объекты одного и того же типа используют один экземпляр метода?

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

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

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

Ответ 3

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

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

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

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

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