предисловие
Когда дело доходит до объектов Java, первая реакция автора такова:Каждый объект в Java (за исключением базовых типов) наследуется отObject
объект. Я полагаю, что это также первое впечатление большинства программистов от объектов Java.Object
Все объекты Java могут быть представлены. Однако это понимание остается только на уровне языка, а на более глубоком уровне JVM объекты по-прежнему используютObject
выражать?Очевидно нет. JVM обычно реализуется на языке, отличном от Java, и используется для анализа и запуска программ Java.У нее есть собственная модель для представления различных функций языка Java, в том числеObject
. Давайте возьмем HotSpot в качестве примера для обсуждения объектной модели Java объектов Java на уровне JVM.
HotSpot реализован на языке C++.Следующая JVM относится к HotSpot, если не указано иное.
Java-программа черезnew
оператора для создания объекта Прежде чем погрузиться в объектную модель Java HotSpot, давайте взглянемnew
Конкретная реализация оператора.
// hotspot/src/share/vm/interpreter/interpreterRuntime.cpp
...
// HotSpot中new操作符的实现函数
IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, ConstantPool* pool, int index))
Klass* k_oop = pool->klass_at(index, CHECK);
instanceKlassHandle klass (THREAD, k_oop);
// Make sure we are not instantiating an abstract klass
klass->check_valid_for_instantiation(true, CHECK);
// Make sure klass is initialized
klass->initialize(CHECK);
// At this point the class may not be fully initialized
// ...
oop obj = klass->allocate_instance(CHECK);
thread->set_vm_result(obj);
IRT_END
...
Приведенный выше фрагмент кода взят из исходного кода HotSpot.new
Функция реализации оператора, без глубокого анализа конкретного значения каждой строки, самая интуитивно понятная функция, которую дает нам этот код:первый справаklass
объект для инициализации, а затем использовать его для созданияoop
объект. Здесь мы можем примерно предположить,oop
Представляет объект Java. А вотklass
и на ЯвеClass
Кажется, что между ними существует тесная связь, во-первых, они очень похожи друг на друга, а другое может быть дополнительно подтверждено 16-й строкой кода. Те, кто немного знаком с механизмом отражения Java, должны быть знакомы с 16-й строкой кода, потому что она похожа на использованиеClass.newInstance()
метод созданияObject
Объекты похожи.
На самом деле, как догадывалось выше, HotSpot использует модель Oop-Klass для представления объектов Java.
Система наследования ООП
здесьOopНе объектно-ориентированное программирование, аOrdinary object pointer(Обычный указатель объекта) — это система, используемая HotSpot для представления информации об экземплярах объектов Java. вoop
Это высший родительский класс в системе ООП, вся система наследования выглядит следующим образом:
// hotspot/src/share/vm/oops/oopsHierarchy.hpp
...
// Oop的继承体系
typedef class oopDesc* oop;
typedef class instanceOopDesc* instanceOop;
typedef class arrayOopDesc* arrayOop;
typedef class objArrayOopDesc* objArrayOop;
typedef class typeArrayOopDesc* typeArrayOop;
...
Каждый объект Java имеет свой уникальный жизненный цикл, мы используемnew
Оператор создает его, затем выполняет с ним различные операции (например, получение свойств члена, вызов функций-членов, блокировку и т. д.) и, наконец, восстанавливает сборщик мусора. Так как же JVM реализует эту серию опытов с объектами Java? JVM использует Oop для представления объекта Java, и, естественно, этот опытoop
Связанный.
oop
Существует два подклассаinstanceOop
а такжеarrayOop
. Первый представляет собой обычный объект в Java, а второй представляет объект-массив.arrayOop
Есть также два подкласса,objArrayOop
представляет собой массив обычных типов объектов, аtypeArrayOopDesc
представляет массив базового типа. Как показано ниже,oop
Структура хранилища в основном состоит из заголовка объекта и тела объекта.
заголовок объекта oop
oop
В основном есть два свойства члена:
// hotspot/src/share/vm/oops/oop.hpp
class oopDesc {
...
private:
// 用于存储对象的运行时记录信息,如哈希值、GC分代年龄、锁状态等
volatile markOop _mark;
// Klass指针的联合体,指向当前对象所属的Klass对象
union _metadata {
// 未采用指针压缩技术时使用
Klass* _klass;
// 采用指针压缩技术时使用
narrowKlass _compressed_klass;
} _metadata;
...
}
_mark
а также_metadata
называетсязаголовок объекта, где первый хранит информацию о записи объекта во время выполнения, а второй является указателем на объект, которому принадлежит текущий объектKlass
объект.
По некоторым историческим причинам HotSpot поместил
markOop
в систему ООП, но не наследуетoop
, поэтому описанная выше система ООП его не включает.
markOop
Структура хранения 32-битной и 64-битной систем различна, но конкретная хранимая информация одинакова.В этом разделе представлена только структура хранения в 32-битной системе. В 32-битной системеmarkOop
Всего он занимает 32 бита, а структура хранения следующая:
Как видно из рисунка, вmarkOop
, и в разных состояниях его структура немного отличается.нет замкаОтносится к состоянию объекта, когда он не заблокирован;Блокировка смещения, как следует из названия, она будет смещена в сторону первого потока, который обращается к блокировке. Когда к блокировке синхронизации обращается только один поток, JVM оптимизирует ее как смещенную блокировку, что эквивалентно отсутствию семантики синхронизации; - возникает конкуренция потоков, смещенная блокировка будет расширяться доЛегкий замок, последний реализуется с помощью CAS (Compare And Swap), что позволяет избежать переключения между режимом пользователя и режимом ядра; если потоку не удается получить облегченную блокировку, блокировка будет продолжать расширяться дотяжелый замок, в это время JVM будет применять мьютекс к операционной системе, поэтому потребление производительности также является самым высоким.
oop
Предоставляет 4 метода для определения состояния текущего объекта:
// hotspot/src/share/vm/oops/oop.hpp
class oopDesc {
...
bool is_locked() const;
bool is_unlocked() const;
bool has_bias_pattern() const;
...
bool is_gc_marked() const;
}
// hotspot/src/share/vm/oops/oop.inline.hpp
...
inline bool oopDesc::is_gc_marked() const {
return mark()->is_marked();
}
inline bool oopDesc::is_locked() const {
return mark()->is_locked();
}
inline bool oopDesc::is_unlocked() const {
return mark()->is_unlocked();
}
inline bool oopDesc::has_bias_pattern() const {
return mark()->has_bias_pattern();
}
...
Как видно из приведенного выше кода,oop
перечислитьmarkOop
метод, чтобы определить, был ли заблокирован текущий объект, является ли это смещенной блокировкой,markOop
Затем это реализуется путем оценки битов флага в его структуре хранения, как показано в следующем коде:
// hotspot/src/share/vm/oops/markOop.hpp
class markOopDesc: public oopDesc {
...
// unlocked_value = 1
// lock_mask_in_place = right_n_bits(2),is_locked()利用存储结构的最右边两位
// 来判断当前对象是否是加锁状态。值得注意的是,偏向锁并不属于加锁状态。
bool is_locked() const {
return (mask_bits(value(), lock_mask_in_place) != unlocked_value);
}
// lock_mask_in_place = right_n_bits(3),is_unlocked()并不是简单地对is_locked()
// 的结果取反,而是使用存储结构的最右边三位来判断。值得注意的是,偏向锁也并不属于无锁状态。
bool is_unlocked() const {
return (mask_bits(value(), biased_lock_mask_in_place) == unlocked_value);
}
// marked_value = 3
// lock_mask_in_place = right_n_bits(2),当锁标志位的值为3(二进制为11)时返回true。
bool is_marked() const {
return (mask_bits(value(), lock_mask_in_place) == marked_value);
}
// biased_lock_pattern = 5
// biased_lock_mask_in_place = right_n_bits(3),当存储结构的最后三位的值为5(二进制
// 为101)时返回true
bool has_bias_pattern() const {
return (mask_bits(value(), biased_lock_mask_in_place) == biased_lock_pattern);
}
...
}
тело объекта
JVM хранит поля объектов Java вoop
изобъектсередина,oop
Предоставляется ряд методов для получения и установки полей, а также конкретные реализации для каждого базового типа.
// hotspot/src/share/vm/oops/oop.hpp
class oopDesc {
...
// 返回成员属性的地址
void* field_base(int offset) const;
// 如果成员是基础类型,则用特有的方法
jbyte* byte_field_addr(int offset) const;
jchar* char_field_addr(int offset) const;
jboolean* bool_field_addr(int offset) const;
jint* int_field_addr(int offset) const;
jshort* short_field_addr(int offset) const;
jlong* long_field_addr(int offset) const;
jfloat* float_field_addr(int offset) const;
jdouble* double_field_addr(int offset) const;
Metadata** metadata_field_addr(int offset) const;
// 同样是成员的地址获取方法,在GC时使用
template <class T> T* obj_field_addr(int offset) const;
...
// instanceOop获取和设置其成员属性的方法
oop obj_field(int offset) const;
volatile oop obj_field_volatile(int offset) const;
void obj_field_put(int offset, oop value);
void obj_field_put_raw(int offset, oop value);
void obj_field_put_volatile(int offset, oop value);
// 如果成员时基础类型,则使用其特有的方法,这里只列出针对byte类型的方法
jbyte byte_field(int offset) const;
void byte_field_put(int offset, jbyte contents);
...
}
Конкретная реализация показана в следующем коде:
// hotspot/src/share/vm/oops/oop.inline.hpp
...
// 获取对象中field的地址
inline void* oopDesc::field_base(int offset) const {
return (void*)&((char*)this)[offset];
}
// 获取普通对象field的地址,对调用field_base的结果进行转型得到
template <class T> inline T* oopDesc::obj_field_addr(int offset) const {
return (T*)field_base(offset);
}
// 基础类型特有的实现与obj_field_addr类似,只是转型成特有的基础类型指针
inline jbyte* oopDesc::byte_field_addr(int offset) const {
return (jbyte*) field_base(offset);
}
...
// 获取field前需要先判断是否采用了指针压缩技术,先根据offset调用obj_field_addr
// 得到field的地址,然后调用load_decode_heap_oop得到实例
inline oop oopDesc::obj_field(int offset) const {
return UseCompressedOops ?
load_decode_heap_oop(obj_field_addr<narrowOop>(offset)) :
load_decode_heap_oop(obj_field_addr<oop>(offset));
}
// 直接对指针解引用得到field
inline oop oopDesc::load_decode_heap_oop(oop* p) {
return *p;
}
...
// 设置field前需要先判断是否采用了指针压缩技术,同样也是先根据offset得到地址,
// 然后在设置field的值
inline void oopDesc::obj_field_put(int offset, oop value) {
UseCompressedOops ?
oop_store(obj_field_addr<narrowOop>(offset), value) :
oop_store(obj_field_addr<oop>(offset), value);
}
// 设置field前先更新barrier
template <class T> inline void oop_store(T* p, oop v) {
if (always_do_update_barrier) {
oop_store((volatile T*)p, v);
} else {
update_barrier_set_pre(p, v);
oopDesc::encode_store_heap_oop(p, v);
update_barrier_set((void*)p, v);
}
}
// 设置field时直接更新指针指向的值
inline void oopDesc::encode_store_heap_oop(oop* p, oop v) {
*p = v;
}
Как видно из приведенного выше фрагмента кода, каждое поле находится вoop
Каждый имеет соответствующее смещение (offset),oop
Адрес поля получается через смещение, а затем по адресу получаются конкретные данные. следовательно,Поле в объекте Java хранит не сам объект, а адрес объекта.
Суммировать
HotSpot использует модель Oop-Klass для представления объектов Java, где Klass соответствует типу (классу) объекта Java, а Oop соответствует экземпляру (экземпляру) объекта Java. ООП — это система наследования, в которойoop
Это высший родительский класс в системе, и его структура хранения может быть разделена на заголовок объекта и тело объекта. В заголовке объекта хранятся некоторые метаданные объекта, а в теле объекта хранятся определенные атрибуты членов. Примечательно,Если свойство-член относится к обычному объектному типу, тоoop
просто сохраните его адрес.
Все мы знаем, что обычные методы в Java (без статического и финального оформления) динамически связываются, а в C++ динамическое связывание осуществляется черезвиртуальная функцияЧтобы достичь этого, цена состоит в том, что каждый объект C++ должен поддерживатьтаблица виртуальных функций. Характерной чертой Java является то, что все является объектом.Если каждый объект поддерживает виртуальную таблицу функций, накладные расходы памяти будут очень большими.JVM оптимизировала это.Таблица виртуальных функций больше не поддерживается каждым объектом, а поддерживается типом класса.Все объекты, принадлежащие к этому типу, совместно используют таблицу виртуальных функций.. Поэтому мы неoop
Найдите соответствующую логику вызова метода на приведенном выше, эта часть кода размещенаklass
в.
Содержимое, связанное с Klass, будет представлено в следующей статье «Объектная модель Java — Oop-Klass (2)».