Java Unsafe API для низкоуровневого манипулирования памятью

Иногда приходится сталкиваться с задачами, которые требуют выхода за рамки стандартных абстракций и безопасности Java. Мы ищем способы оптимизации, решения проблем производительности, или, возможно, просто хотим расширить свой кругозор в Java. Для таких случаев существует Java Unsafe API. Этот инструмент предоставляет нам низкоуровневый доступ к памяти и более широкие возможности для манипуляции данными.
Java изначально создавалась с идеей «ноль амортизации» и скрытия сложности управления памятью от разработчиков. Но есть сферы, где низкоуровневая работа с памятью может сделать наш код более эффективным, быстрым и мощным.
Представьте, что у вас есть приложение для обработки потоков видео. Вы хотите получить каждый кадр как можно быстрее, обработать его и отправить обратно. В этой ситуации быстрое копирование памяти между буферами может сэкономить драгоценные миллисекунды и обеспечить более плавную обработку.
Работа с памятью в Java
Java создавалась как язык программирования, предоставляющий высокий уровень абстракции и облегчающий разработку за счет сокрытия сложных деталей от разработчиков.
Java использует сборщик мусора, который автоматически освобождает память, выделенную для объектов, когда они больше не доступны для программы. Это значительно снижает вероятность утечек памяти и делает код более надежным. Однако, сборка мусора также вносит некоторую амортизацию в работу приложения, и в некоторых сценариях это может привести к неэффективному использованию памяти.
С абстракциями и уровнем безопасности, предоставляемыми Java, приходят и ограничения. Один из основных моментов — это отсутствие прямого доступа к памяти, как это возможно, например, в языках С или C++. В Java, все операции с памятью производятся через ссылки на объекты. Это предотвращает множество ошибок, связанных с некорректной работой с памятью, но также ограничивает возможности оптимизации и низкоуровневой манипуляции.
Кроме того, Java обеспечивает многозадачность и безопасность на уровне виртуальной машины, что также добавляет оверхед и ограничения. Операции, которые могли бы быть атомарными в других языках, могут быть не таковыми в Java из-за гарантий многозадачности.
Зачем нам нужно низкоуровневое управление памятью в Java?
- Производительность: Некоторые задачи требуют максимальной производительности, и уровень абстракции Java может стать преградой. Например, вышеупомянутый сценарий обработки видео — здесь даже небольшие задержки могут стать заметными. Прямой доступ к памяти может помочь ускорить копирование данных и обработку.
- Сериализация и десериализация: Низкоуровневый доступ к памяти может быть полезен при сериализации и десериализации объектов, что может быть важно для реализации собственных протоколов или форматов данных.
- Интеграция с нативным кодом: Когда вы работаете с нативными библиотеками, написанными на языках, где низкоуровневая работа с памятью нормальна, Java Unsafe API может помочь в эффективной интеграции.
- Эксперименты и исследования: Иногда можно использовать Unsafe API для экспериментов и исследований. Это может помочь понять, как работает виртуальная машина Java и какие манипуляции с памятью она выполняет за кулисами.
Unsafe API: основные концепции
Что такое sun.misc.Unsafe?
Java Unsafe API представляет собой класс sun.misc.Unsafe , который находится в пакете sun.misc . Этот класс является частью внутреннего API Java и не является частью стандартной библиотеки. Он был создан, в первую очередь, для использования внутри самой Java Virtual Machine (JVM) и не предназначен для публичного использования.
Нюансы Unsafe API
- Доступ к памяти: Основной задачей Unsafe является предоставление прямого доступа к памяти. Это позволяет выполнять операции чтения и записи данных в определенные адреса памяти. Например, с помощью Unsafe , вы можете создать и управлять собственными объектами вне области видимости управления памятью Java.
- Работа с массивами: Unsafe предоставляет методы для выполнения операций над массивами, включая создание массивов, чтение и запись элементов массива, а также управление памятью, выделение и освобождение.
- Атомарные операции: Одним из ключевых аспектов Unsafe является поддержка атомарных операций. Это позволяет выполнять операции, гарантирующие, что никакие другие потоки не могут вмешаться между началом и завершением операции. Это важно для обеспечения безопасности при многозадачной обработке.
- Off-heap память: Unsafe позволяет выделять и освобождать память за пределами управления памятью Java. Это называется «off-heap» памятью и может быть полезно для снижения накладных расходов при управлении большими объемами данных.
- Небезопасность: Название Unsafe говорит само за себя. Использование этого API представляет риск и потенциально может привести к серьезным ошибкам и нарушению безопасности. Разработчики должны быть осторожными и хорошо понимать, что они делают.
Для начала работы с Java Unsafe API, нам необходимо загрузить экземпляр класса sun.misc.Unsafe . Как уже упоминалось, этот класс не предоставляется публично в стандартной библиотеке Java, поэтому мы должны воспользоваться рефлексией для его получения.
Загрузка экземпляра Unsafe
Для начала, нам нужно получить доступ к статическому полю theUnsafe в классе Unsafe . Это поле содержит экземпляр Unsafe . Однако, оно приватное и не доступно для прямого обращения. Поэтому, мы используем рефлексию для получения доступа к нему. Вот как это можно сделать:
import sun.misc.Unsafe; public class UnsafeAccess < public static void main(String[] args) < Unsafe unsafe = getUnsafeInstance(); // Теперь у нас есть экземпляр Unsafe, и мы можем начать использовать его. >private static Unsafe getUnsafeInstance() < try < // Получаем поле 'theUnsafe' из класса Unsafe. Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); // Извлекаем экземпляр Unsafe из поля. return (Unsafe) field.get(null); >catch (Exception e) < // Обработка ошибок при получении экземпляра Unsafe. throw new RuntimeException("Unable to get Unsafe instance"); >> >
Пример доступа к Unsafe
После успешной загрузки экземпляра Unsafe , вы готовы использовать его для выполнения различных низкоуровневых операций. Например, вы можете создавать объекты без вызова конструкторов, манипулировать полями объектов и выполнять атомарные операции.
import sun.misc.Unsafe; public class UnsafeUsage < public static void main(String[] args) < Unsafe unsafe = getUnsafeInstance(); // Пример: Создание объекта без вызова конструктора MyClass myObject = (MyClass) unsafe.allocateInstance(MyClass.class); // Пример: Манипуляция с полем объекта напрямую unsafe.putInt(myObject, unsafe.objectFieldOffset(MyClass.class.getDeclaredField("myField")), 42); >private static Unsafe getUnsafeInstance() < // Аналогично предыдущему примеру, загрузаем экземпляр Unsafe. // . >static class MyClass < private int myField; >>
Основный методы
1. allocateMemory и freeMemory
Методы allocateMemory и freeMemory позволяют выделять и освобождать память вне области управления памятью Java. Это полезно, когда вам нужно управлять памятью напрямую:
import sun.misc.Unsafe; public class MemoryAllocation < public static void main(String[] args) < Unsafe unsafe = getUnsafeInstance(); long size = 1024; // Размер в байтах // Выделяем память long memoryAddress = unsafe.allocateMemory(size); // Используем выделенную память unsafe.putInt(memoryAddress, 42); // Освобождаем память unsafe.freeMemory(memoryAddress); >private static Unsafe getUnsafeInstance() < // Аналогично предыдущим примерам, загружаем экземпляр Unsafe. // . >>
Метод allocateMemory выделяет память указанного размера, а метод freeMemory освобождает память по указанному адресу. Важно учитывать, что вы должны сами следить за освобождением памяти, иначе это может привести к утечкам памяти.
2. put и get методы
Методы put и get позволяют выполнять операции чтения и записи данных по указанному адресу памяти. Вы можете использовать их для манипуляции данными без создания объектов:
import sun.misc.Unsafe; public class MemoryOperations < public static void main(String[] args) < Unsafe unsafe = getUnsafeInstance(); long memoryAddress = unsafe.allocateMemory(8); // Запись значения в память unsafe.putLong(memoryAddress, 1234567890L); // Чтение значения из памяти long value = unsafe.getLong(memoryAddress); System.out.println("Значение из памяти: " + value); unsafe.freeMemory(memoryAddress); >private static Unsafe getUnsafeInstance() < // Аналогично предыдущим примерам, загружаем экземпляр Unsafe. // . >>
Методы put используются для записи данных в память, а методы get — для чтения данных. Вы указываете тип данных, который хотите прочитать или записать, а также адрес памяти, по которому операция будет выполнена.
3. Методы для работ с массивами
Unsafe предоставляет ряд методов для работы с массивами, включая создание массивов, чтение и запись элементов массива, а также выделение и освобождение памяти для массивов:
import sun.misc.Unsafe; public class ArrayOperations < public static void main(String[] args) < Unsafe unsafe = getUnsafeInstance(); int length = 5; // Выделяем память для массива целых чисел long arrayAddress = unsafe.allocateMemory(length * Integer.BYTES); // Записываем значения в массив for (int i = 0; i < length; i++) < unsafe.putInt(arrayAddress + i * Integer.BYTES, i * 10); >// Читаем значения из массива for (int i = 0; i < length; i++) < int value = unsafe.getInt(arrayAddress + i * Integer.BYTES); System.out.println("Значение в ячейке " + i + ": " + value); >// Освобождаем память для массива unsafe.freeMemory(arrayAddress); > private static Unsafe getUnsafeInstance() < // Аналогично предыдущим примерам, загружаем экземпляр Unsafe. // . >>
В этом примере мы выделили память под массив целых чисел, записали и прочитали значения в массиве. Методы allocateMemory и freeMemory используются для выделения и освобождения памяти для массива, а методы put и get для манипуляции элементами массива.
4. Методы для атомарных операций
Unsafe предоставляет методы для выполнения атомарных операций, что полезно в многозадачных приложениях. Эти методы обеспечивают безопасное выполнение операций, которые иначе могли бы привести к состязательности потоков. Пример использования метода compareAndSwapInt :
import sun.misc.Unsafe; public class AtomicOperations < public static void main(String[] args) < Unsafe unsafe = getUnsafeInstance(); long memoryAddress = unsafe.allocateMemory(4); unsafe.putInt(memoryAddress, 42); // Атомарная замена значения, если оно равно 42 boolean swapped = unsafe.compareAndSwapInt(null, memoryAddress, 42, 100); int value = unsafe.getInt(memoryAddress); System.out.println("Значение после операции: " + value); System.out.println("Замена выполнена: " + swapped); unsafe.freeMemory(memoryAddress); >private static Unsafe getUnsafeInstance() < // Аналогично предыдущим примерам, загружаем экземпляр Unsafe. // . >>
Метод compareAndSwapInt выполняет атомарную замену значения по указанному адресу, только если текущее значение равно указанному. В этом примере, мы попытались заменить значение 42 на 100, и результат операции ( swapped ) сообщает нам о том, была ли замена выполнена.
5. Создание объекта без вызова конструктора
Один из наиболее популярных и полезных примеров использования метода allocateInstance — создание объекта без вызова его конструктора. Это может быть полезно, когда вы хотите избежать инициализации объекта или создать объект, который не имеет публичного конструктора:
import sun.misc.Unsafe; public class ObjectCreationExample < public static void main(String[] args) < Unsafe unsafe = getUnsafeInstance(); MyClass myObject = (MyClass) unsafe.allocateInstance(MyClass.class); myObject.printHello(); // Внимание! Поля объекта могут быть не инициализированы. unsafe.freeMemory(unsafe.objectFieldOffset(MyClass.class.getDeclaredField("myField"))); >private static Unsafe getUnsafeInstance() < // Аналогично предыдущим примерам, загружаем экземпляр Unsafe. // . >static class MyClass < private String myField; public MyClass() < this.myField = "Hello, World!"; >public void printHello() < System.out.println(myField); >> >
Обратите внимание, что при создании объекта с помощью allocateInstance , его поля могут остаться не инициализированными, как в примере выше. Это полезно, когда вы хотите избежать выполнения дорогостоящих операций инициализации в конструкторе.
6. Манипуляция с полями объекта напрямую
Одним из ключевых преимуществ Java Unsafe API является возможность манипулировать полями объекта напрямую, обходя обычные механизмы доступа. Это может быть полезно, например, для сериализации объектов или для оптимизации производительности:
import sun.misc.Unsafe; public class FieldManipulationExample < public static void main(String[] args) < Unsafe unsafe = getUnsafeInstance(); MyClass myObject = new MyClass(); // Получаем смещение поля 'myField' в объекте 'MyClass' long fieldOffset = unsafe.objectFieldOffset(MyClass.class.getDeclaredField("myField")); // Манипулируем значением поля напрямую unsafe.putObject(myObject, fieldOffset, "New Value"); // Читаем значение поля String fieldValue = (String) unsafe.getObject(myObject, fieldOffset); System.out.println("Значение поля 'myField': " + fieldValue); >private static Unsafe getUnsafeInstance() < // Аналогично предыдущим примерам, загружаем экземпляр Unsafe. // . >static class MyClass < private String myField = "Original Value"; >>
Этот пример показывает, как мы можем получить смещение поля объекта и использовать методы putObject и getObject для записи и чтения значений поля напрямую.
Синхронизация и многозадачность
Java предоставляет множество механизмов для обеспечения синхронизации, и одним из них являются атомарные операции и volatile .
Атомарные операции предоставляют нам способ выполнения операций над разделяемыми переменными так, что операция либо полностью выполняется, либо не выполняется вовсе. Это гарантирует целостность данных и защиту от состязательности потоков. В Java, класс sun.misc.Unsafe предоставляет методы для выполнения атомарных операций. Примеры таких методов включают compareAndSwapInt , compareAndSwapLong , getAndAddInt и многие другие.
Пример атомарной операции (также были примеры ранее):
import sun.misc.Unsafe; public class AtomicOperationExample < public static void main(String[] args) < Unsafe unsafe = getUnsafeInstance(); long memoryAddress = unsafe.allocateMemory(4); unsafe.putInt(memoryAddress, 42); // Атомарная замена значения, если оно равно 42 boolean swapped = unsafe.compareAndSwapInt(null, memoryAddress, 42, 100); int value = unsafe.getInt(memoryAddress); System.out.println("Значение после операции: " + value); System.out.println("Замена выполнена: " + swapped); unsafe.freeMemory(memoryAddress); >private static Unsafe getUnsafeInstance() < // Аналогично предыдущим примерам, загружаем экземпляр Unsafe. // . >>
volatile также используется для обеспечения согласованности разделяемых данных. Когда поле объявлено как volatile , это гарантирует, что изменения в этом поле будут видны всем потокам, и операции с ним будут атомарными. Однако, volatile имеет ограничения и не всегда подходит для сложных операций.
Пример использования volatile :
public class VolatileExample < private volatile int sharedValue = 0; public void incrementSharedValue() < sharedValue++; >public int getSharedValue() < return sharedValue; >>
Использование Java Unsafe API может значительно влиять на производительность и многозадачность вашего приложения. С одной стороны, это дает большую гибкость и возможность выполнения низкоуровневых операций. С другой стороны, неправильное использование Unsafe может привести к состязательности потоков, ошибкам и утечкам памяти.
Чтобы максимально использовать потенциал Unsafe , важно следовать bewhere»безопасному» использованию. Это означает, что вы должны знать, когда и где использовать атомарные операции, volatile поля и другие средства синхронизации. Тщательно планируйте свой код и убедитесь, что он безопасен в многозадачной среде.
Однако, не всегда Unsafe является оптимальным решением. В некоторых случаях, использование стандартных механизмов синхронизации, таких как synchronized блоки или java.util.concurrent библиотека, может быть более предпочтительным. Важно обеспечить баланс между производительностью и безопасностью приложения.
Заключение
Java Unsafe AP предоставляет доступ к низкоуровневым операциям, которые могут быть полезными в ряде сценариев разработки. Надеюсь, что эта статья помогла вам лучше понять его принципы, возможности и ограничения.
Статья подготовлена в рамках набора на онлайн-курс «Java Developer. Professional». Чтобы узнать, достаточно ли ваших знаний для прохождения программы курса, пройдите вступительное тестирование.
Как и почему Unsafe используется в Java?
sun.misc.Unsafe был в Java, по крайней мере, еще с Java 1.4 (2004). В Java 9 Unsafe будет скрыт вместе со многими другими классами для внутреннего использования. улучшить ремонтопригодность JVM. Хотя до сих пор неясно, что именно заменит Unsafe, и я подозреваю, что это будет более чем одна вещь, которая заменит его, возникает вопрос, почему он вообще используется?
Делать то, что язык Java не позволяет, но все еще полезно.
Java не допускает многих приемов, доступных для языков более низкого уровня. Для большинства разработчиков это очень хорошая вещь, и она не только спасает вас от себя, но также спасает вас от ваших коллег. Это также упрощает импорт кода с открытым исходным кодом, потому что вы знаете, что существуют ограничения на возможный ущерб. Или, по крайней мере, есть пределы тому, сколько вы можете сделать случайно. Если вы попробуете достаточно сильно, вы все равно можете нанести урон.
Но почему бы вам даже не попробовать? При сборке библиотек многие (но не все) методы в Unsafe полезны, и в некоторых случаях нет другого способа сделать то же самое без использования JNI, что еще более опасно, и вы потеряете «один раз скомпилировать, запустить где угодно» »
Десериализация объектов
При десериализации или построении объекта с использованием каркаса вы предполагаете, что хотите воссоздать объект, который существовал ранее. Вы ожидаете, что будете использовать отражение, чтобы либо вызвать сеттеры класса, либо, скорее, установить внутренние поля напрямую, даже конечные поля. Проблема в том, что вы хотите создать экземпляр объекта, но на самом деле вам не нужен конструктор, так как это может только усложнить ситуацию и иметь побочные эффекты.
Микрохирург с кувалдой
Так получилось, что я работаю в мире Ынтерпрайза. Java, J2EE, Oracle и все такое. По этой части я интересуюсь HighLoad-ом и распределенными приложениями. Помимо этого, в свободное время занимаюсь интересными языками под JVM — Groovy, Scala, Clojure, а так же интересуюсь внутренностями Java и разработкой компиляторов и виртуальных машин.
суббота, 5 марта 2011 г.
sun.misc.Unsafe — немного магии вокруг JVM
Когда-то, выбирая декомпилятор, натолкнулся я на несколько изумительных статей на wasm.ru. Статьи они кажется потерли, или по крайней мере изменили адреса, но сохранили их в виде вордовского файла, который можно загрузить отсюда:
Описывают, что такое Unsafe api, предоставляющее программисту возможность работать с классами, методами и т.п. ниже того уровня , который позволяют штатные средства платформы Java — на фактически на уровне тех самых С-структур, которые используются внутри JVM для предоставления сущностей класс, инстанс класса, метод и т.п.
Что дает возможность делать многие вещи, которые в программировании на Java «традиционно» считаются невыполнимыми — функция sizeOf, (возвращающая точный размер объекта в памяти , взятый напрямую из поля структуры, которая представляет этот объект внутри JVM), наследования от final-класса (делается двумя шагами по сути — в список предков класса добавляется нужный нам класс, и из таблицы модификаторов доступа для этого суперкласса во время выполнения программы удаляется final — и
всего делов ;)).
Ну и уже более изощренные и хакерские штучки — самомодицирующиеся во время выполнения методы..и т.п. Очень рекомендую эти статьи к прочтению :).
Да, конечно — смещения полей в структурах высчитываются на пальцах, класс sun.mics.Unsafe недокументирован (но присутствует в Java с самого ее начала, и очень навряд ли будет из нее удален — я очень сильно подозреваю, что предоставляемые им низкоуровневые возможности используются инструментами типа отладчиков и т.п.), и в продакшне ни один Project Manager такое использоватьне позволит — да и не требуется в обычных приложения никогда столь низкоуровневый доступ к JVM. Но знать, как ява-машины работает с классами внутри себя — полезно и интересно, а знать что из обычной программы на чистой яве можно получать доступ такого уровня — еще интересней.
И напоследок два слова по поводу того, что это gap в ява-машине, который будет закрыт в следующем же релизе и т.п.
Я не думаю, что это дыра в системе безопасности Java. Собственно, суммирую и повторю описанное в первой из статей.
Итак. Есть две точки входа для получения инстанса класса Unsafe, который позволит нам творить черную магию:
1) Получить его можно через Unsage.getUnsafe() — НО! только в том случае, если вызывающий класс был загружен первичным класслоадером (www.tedneward.com/files/Papers/BootClass path /BootClasspath.pdf — тут можно прочитать подробней про иерархию класслоадеров). Это сделать несложно — всего-то добавить ключ -Xbootclasspath в список стартовых опций ява-машины. Но для этого надо иметь доступ к среде выполнения.
2) Можно просто взять private переменную theUnsafe — которая хранит инстанс класса Unsafe внутри него. Но если есть Security Manager и установлена для него соответствующая политика запрещения опасной рефлексии , получить значение этой закрытой переменной не удастся.
Соответственно, мой вывод — это API не является уязвимостью в JVM, вовсе нет.
Потому что точно так же рефлексией (если она не запрещена в политиках безопасности) можно творить безобразия внутри приложения, но и польза от нее может быть большая— надо просто разумно выставлять политики безопасности в каждом конкретном случае.
Да — Unsafe API дает беспрецедентный уровень доступа к среде выполнения Java из программы. Но чтобы получить доступ к этому апи — надо либо иметь соотв. права на той машине, где выполняется приложение (например, возможность задавать параметры запуска ява-машины), либо должен быть соответственно сконфигурирован (без учета этой опасности) Security Manager.
Unsafe java что это
Вы используете устаревший браузер. Этот и другие сайты могут отображаться в нём некорректно.
Необходимо обновить браузер или попробовать использовать другой.
Unsafe Java I — Небезопасная жаба
Дата публикации 20 май 2006
Unsafe Java I — Небезопасная жаба — Архив WASM.RU
- Класс sun.misc.Unsafe
- Структуры виртуальной машины
- Особенности версии 1.5
- Применение на практике
- Бесконечный final
public static Unsafe getUnsafe()
Class class1 = Reflection.getCallerClass(2);
if(class1.getClassLoader() != null)
throw new SecurityException(«Unsafe»);
return theUnsafe;
К счастью существует еще внутренняя переменная theUnsafe, до которой мы можем добраться с помощью Reflection. Всю черновую работу соберем в один класс (назовем его UnsafeUtil), который будем расширять по мере надобности.
public class UnsafeUtil < public static Unsafe unsafe; private static long fieldOffset; private static UnsafeUtil instance = new UnsafeUtil(); private Object obj; Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); unsafe = (Unsafe)f.get(null); fieldOffset = unsafe.objectFieldOffset(UnsafeUtil.class.getDeclaredField("obj")); > catch (Exception ex) < throw new RuntimeException(e);
Конечно можно просто внести UnsafeUtil в список загружаемых Bootloader’ом классов (указав путь в ключе -Xbootclasspath/a) и вызывать getUnsafe() в соответствии с замыслом Sun. Беда в том, что тогда все использующие UnsafeUtil классы также должны быть прописаны в bootclasspath’е (см. главу «5.3 Creation and Loading» в VM spec). Правда например package java.nio как-то ухитряется обходить это ограничение, но как именно пока не очень понятно. К тому же этот способ выходит за рамки «чистого» кода, так как требует дополнительных стартовых опций для виртуальной машины. Так что не будем мудрствовать и ограничимся чтением theUnsafe.
В первую очередь нам понадобятся естественно операции референцирования и дереференцирования, ObjectToAddress и AddressToObject соответственно.
public static long ObjectToAddress (Object o)< instance.obj = o; return unsafe.getLong(instance, fieldOffset); public static Object AddressToObject (long address)< unsafe.putLong(instance, fieldOffset, address); return instance.obj;
С ними мы уже достаточно хорошо вооружены в техническом плане, не хватает только информации по внутреннему устройству Явы. Ее мы найдем в следующем разделе.
Очень похожую реализацию кстати сделал Don Schwarz (http://don.schwarz.name/index.php?p=30). Это одно из очень немногих мест, где можно найти хоть какие-то примеры работы с классом Unsafe. К сожалению Don в свое время не оценил потенциал низкоуровнего программирования в Яве и остановился, сделав всего пару робких шагов. Мы же пойдем дальше.
2. Структуры виртуальной машины
Теперь посмотрим, в каком виде виртуальная машина (версии 1.4) хранит данные в памяти. Поскольку Ява работает с классами и их инстанциями, то ими и займемся.
Инстанция:
instance_struct <
0 magic // всегда равен 1
4 class // указатель на структуру класса, class_struct*
8 . // Дальше идут подряд переменные инстанции(то есть все которые не static),
12 . // порядок пока не очень понятен, судя по всему в порядке объявления и
16 . // обьекты перед примитивными типами
Переменные типа double и long занимают 64 бита, остальные по 32. В памяти инстанции выравниваются по 64-битной границе, дополняются при необходимости нулями. То есть по сути мы имеем обыкновенную сишную структуру плюс указатель на ее описание.
Класс:
class_struct <
0 magic // всегда равен 1
4 class // class_struct*, указатель на структуру класса более высокого уровня, зачем нужен — непонятно
8 . // значение неизвестно
12 super_count // количество уровней наследования: 0x18 — один(наследует от Object), 0х1c — два и т.д. до восьми(0х34), потом 0х10. У интерфейсов тоже 0х10.
16 interface // class_struct*, указатель на какой-либо из интерфейсов класса (на какой именно непонятно), часто просто 0
20 interface_list // указатель на массив с элементами типа class_struct*, все интерфейсы класса
36 . // указатели на структуры восьми высших суперклассов начиная от Object и кончая this (если поместится)
56 size // размер инстанции класса в DWORD’ах
60 this_class // instance_struct*, указатель на инстанцию java.lang.Class соответствующую данному классу
64 access_flags // доступ к классу как описано в VM spec ( 0х1 — public, 0х10 — final и т.д.)
Здесть я привел только те куски, которые мы будем использовать в дальнейшем и в которых я более или менее уверен. На самом деле class_struct значительно длиннее и содержит кроме того указатели на функции класса, статические переменные и все остальное, что может понадобится виртуальной машине. Все эти структуры по понятным причинам нигде не документированы и разбираться надо вручную — хоть и несложно, но достаточно трудоемко. Если у кого-то есть желание помочь, буду только рад.
3. Особенности версии 1.5
С переходом на последнюю (на момент написания) версию 1.5.0_06 внутренние структуры виртуальной машины претерпели некоторые изменения. К счастью небольшие: изменился в основном порядок полей, значения остались в большинстве прежними. Структура класса выглядит теперь следующим образом:
class_struct_1_5 < 0 magic // всегда равен 1
4 class // class_struct*, указатель на структуру класса более высокого уровня, зачем нужен — непонятно
8 . // значение неизвестно
12 size // размер инстанции класса в DWORD’ах
16 super_count // количество уровней наследования: 0x20 — один(наследует от Object), 0х24 — два и т.д. до восьми(0х38), потом 0х14. У интерфейсов тоже 0х14.
20 interface // class_struct*, указатель на какой-либо из интерфейсов класса (на какой именно непонятно), часто просто 0
24 interface_list // указатель на массив с элементами типа class_struct*, все интерфейсы класса
40 . // указатели на структуры восьми высших суперклассов начиная от Object и кончая this (если поместится)
60 this_class // instance_struct*, указатель на инстанцию java.lang.Class соответствующую данному классу