Предисловие
Небезопасный класс используется в нескольких классах исходного кода JDK. Этот класс предоставляет некоторые базовые функции для обхода JVM, и его реализация может повысить эффективность. Тем не менее, это обоюдоострый меч: как предвещается его название, он небезопасен, и память, которую она выделяет, должна быть свободной от ручной работы (не переработана GC). Небезопасный класс, предоставляет простую альтернативу определенным функциям JNI: обеспечение эффективности, облегчая ситуацию.
Этот класс принадлежит классу на солнце.
Эта статья в основном о компиляции и переводе следующих статей.
http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/
1. Большинство методов небезопасных API являются собственными реализациями, которые состоят из 105 методов, в основном включающих следующие категории:
(1) Связанная с информацией. В основном вернуть некоторую информацию о памяти низкого уровня: AddchSize (), pageSize ()
(2) Объекты связаны. В основном предоставьте объект и методы манипуляции с доменом: AllocateInstance (), ObjectFieldOffset ()
(3) Связанный с классом. В основном предоставьте классы и его статические методы манипуляции с доменом: staticfieldoffset (), decipenclass (), defineanonymousclass (), EncureClassInitialization ()
(4) Связанные массивы. Метод манипуляции с массивом: arraybaseoffset (), arrayindexscale ()
(5) Синхронизация. В основном обеспечивает низкоуровневые примитивы синхронизации (такие как примитивы CAS на основе процессоров (Compare-and-Swap): Monitorenter (), tryMonitorenter (), monitorexit (), comparandswapint (), putorderedint ()
(6) Связанная с памятью. Метод прямого доступа к памяти (обойти кучу JVM и напрямую манипулировать локальной памятью): AllocateMemory (), CopyMemory (), freememory (), getAddress (), getInt (), putint ()
2. Получение небезопасного экземпляра класса
Небезопасный дизайн класса предоставляется только для HARM Haterup Class Loader и является типичным классом Singleton Pattern. Его метод сбора экземпляра заключается в следующем:
public static untabe getunsafe () {class cc = sun.reflect.reflection.getCallerclass (2); if (cc.getclassloader ()! = null) бросить новое SecurityException ("небезопасно"); вернуть TheUnsafe;}Загрузчик, не являющийся стартовым классом, будет напрямую вызовать метод uncefe.getunsafe () и будет выбросить Security Exception (конкретная причина включает в себя механизм нагрузки родителей класса JVM).
Есть два решения. Одним из них является указание класса, который будет использоваться в качестве класса запуска через параметр JVM - xbootclasspath. Другим методом является отражение Java.
Поле f = uncefe.class.getDeclaredfield ("theUnsafe"); f.setAccessible (true); небезопасно небезопасно = (небезопасно) f.get (null);Брутально установив доступный доступ к True для частного экземпляра Singleton, а затем напрямую получите объект, который небезопасен для метода Get Field. В IDE эти методы будут помечены как ошибка и могут быть разрешены следующими настройками:
Предпочтения -> Java -> Compiler -> Ошибки/Предупреждения -> Умикенный и ограниченный API -> Запретная ссылка -> Предупреждение
3. "Интересные" сценарии применения небезопасного класса
(1) Обход метода инициализации класса. Метод AllocateInstance () становится очень полезным, когда вы хотите обойти конструкторы объектов, шашки безопасности или конструкторы без общественности.
класс A {private long a; // не инициализированное значение публично a () {this.a = 1; // инициализация} public long a () {return this.a; }}Ниже приводится сравнение метода строительства, метода отражения и AllocateInstance ()
A O1 = новый a (); // constructoro1.a (); // Печать 1 a o2 = a.class.newinstance (); // ReflectionO2.a (); // Печать 1 a o3 = (a) небезопасно. AllocateInstance (A.Class); // небезопасно3.A (); // Печать 0
AllocateInstance () вообще не входит в метод конструктора, и в режиме Синглтона мы, кажется, видим кризис.
(2) Модификация памяти
Модификация памяти относительно распространена на языке C. В Java это может быть использовано для обхода шашки безопасности.
Рассмотрим следующие простые правила проверки доступа:
Class Guard {private int access_allowed = 1; public boolean giveAccess () {return 42 == access_allowed; }}При нормальных обстоятельствах раздача всегда возвращает ложь, но это не всегда случается
Guard Guard = new Guard (); Guard.giveAccess (); // false, нет доступа // unkpassunsafe uncafe = getunsafe (); field f = Guard.getClass (). getDeclaredfield ("access_allowed"); небезопасно. // Corruption Guard.giveAccess (); // true, доступРассчитая смещение памяти и используя метод putint (), access_allowed класса модифицируется. Когда структура класса известна, всегда можно рассчитать смещение данных (согласуется с расчетом смещения данных в классе в C ++).
(3) Реализовать функцию sizeof (), похожая на язык C
Реализуйте функцию c-like sizeof () путем объединения функции Java Reflection и ObjectFieldOffset ().
public static long sizeof (Object o) {небезопасно u = getunsafe (); Hashset Fields = new Hashset (); Класс C = O.GetClass (); while (c! = object.class) {for (Field f: c.getdeclaredfields ()) {if ((f.getModifiers () & modifier.static) == 0) {fields.add (f); }} c = c.getsuperclass (); } // СДЕЛАТЬ СВЕДЕНИЕ LONG MAXSIZE = 0; Для (Поле F: Fields) {Long Offset = U.O.ObjectFieldOffset (F); if (offset> maxSize) {maxSize = offset; }} return (((maxSize/8) + 1) * 8; // прокладка}Идея алгоритма очень ясна: начните с базового подкласса, выберите нестатические домены себя и все его суперкласс по очереди, поместите их в хэшсет (повторные расчеты являются только один раз, Java является единственным наследством), а затем используйте объект FiredFortOffset () для получения максимального смещения и, наконец, рассмотреть Aligntance.
В 32-разрядном JVM размер можно получить, прочитав длинное с смещением файла класса 12.
Public Static Long Sizeof (Object Object) {return getUnsafe (). getAddress (normalize (getunsafe (). getInt (Object, 4L)) + 12L);}Функция normalize () - это метод, который преобразует подписанный int в долгосрочной
private static long normalize (int value) {if (value> = 0) return value; return (0l >>> 32) и значение;}Размер двух вычисленных () вычисленных () одинаково. Наиболее стандартной реализацией Sizeof () является использование java.lang.instrument, однако она требует указания параметра командной строки -Javaagent.
(4) Реализация репликации мелкой Java
Стандартная схема мелкой репликации заключается в реализации клонируемого интерфейса или функций репликации, реализованных сами по себе, и они не являются многоцелевыми функциями. Объединив метод Sizeof (), может быть достигнуто мелкое копирование.
статический объект неглубокий (объект obj) {long size = sizeof (obj); Long Start = Toaddress (OBJ); длинный адрес = getunsafe (). Allocatememory (размер); getUnsafe (). Copymemory (Start, Address, Size); вернуть от AddRess (адрес);}Следующие toaddress () и fromAddress () преобразуют объект в его адрес и обратную работу соответственно.
Статический длинный toaddress (Object obj) {Object [] array = new Object [] {obj}; long baseoffset = getunsafe (). Arraybaseoffset (Object []. Class); return normalize (getunsafe (). getInt (array, baseoffset));} static object fromAddress (длинный адрес) {object [] array = new Object [] {null}; long baseoffset = getunsafe (). Arraybaseoffset (Object []. Class); getunsafe (). Putlong (массив, baseoffset, адрес); return Array [0];}Вышеуказанная функция мелкой копии может быть применена к любому объекту Java, и ее размер рассчитывается динамически.
(5) Устранение паролей в памяти
Поля пароля хранятся в строке, однако, переработка строки управляется JVM. Самый безопасный способ - перезаписать поле пароля после его использования.
Field stringValue = string.class.getDeclaredfield ("value"); stringValue.SetAccessible (true); char [] mem = (char []) stringValue.get (пароль); для (int i = 0; i <mem.length; i ++) {mem [i] = '?(6) Динамическая загрузка классов
Стандартным методом динамической загрузки классов является class.forname () (при написании программ JDBC я помню это глубоко). Небезопасно также может динамически загружать файлы класса Java.
byte [] classcontents = getClassContent (); класс c = getUnsafe (). decipleclass (null, classcontents, 0, classcontents.length); c.getMethod ("a"). inloke (c.newinstance (), null); // 1getClassContent () Метод считывает файл класса в массив байтов. Частный статический байт [] getClassContent () бросает исключение {file f = new File ("/home/mishadoff/tmp/a.class"); FileInputStream input = new FileInputStream (f); byte [] content = new Byte [(int) f.length ()]; input.read (content); input.close (); вернуть контент;}Это может быть применено в динамической нагрузке, оформлении, нарезке и других функциях.
(7) Исключение обнаружения пакета является исключением времени выполнения.
getUnsafe (). ThrowException (new ioException ());
Это можно сделать, когда вы не хотите поймать проверенное исключение (не рекомендуется).
(8) Быстрая сериализация
Стандартный сериализуемый Java очень медленный, и также ограничивает, что класс должен иметь общественный конструктор без параметра. Extraizable лучше, он должен указать схему для сериализации класса. Популярные эффективные библиотеки сериализации, такие как Kryo, полагаясь на сторонние библиотеки, увеличат потребление памяти. Вы можете получить фактическое значение домена в классе через getInt (), getLong (), getObject () и другие методы, а также сохраняют информацию, такую как имя класса, в файл вместе. Крио попытался использовать небезопасно, но нет конкретных данных по повышению производительности. (http://code.google.com/p/kryo/issues/detail?id=75)
(9) распределить память в неайвовой куче
Новое использование Java будет выделять память для объектов в куче, а жизненный цикл объекта будет управляться JVM GC.
класс Superarray {private final Static int byte = 1; частный длинный размер; частный длинный адрес; public superarray (длинный размер) {this.size = size; address = getunsafe (). Allocatememory (размер * байт); } public void set (long i, byte value) {getUnsafe (). putbyte (адрес + i * byte, value); } public int get (long idx) {return getUnsafe (). getbyte (адрес + idx * byte); } public long size () {return size; }}Память, выделенная небезопасной, не ограничена integer.max_value и выделена на память без HEAP. При использовании его необходимо быть очень осторожным: если вы забудете вручную перерабатывать его, возникнут утечки памяти; Если вы незаконные доступа к адресу, это приведет к сбою JVM. Его можно использовать, когда вам нужно распределить большие непрерывные области, программирование в реальном времени (не допустить задержку JVM). Java.nio использует эту технологию.
(10) Приложения в параллелизм Java
Используя uncafe.compareandswap (), его можно использовать для реализации эффективных структур данных без блокировки.
Класс Каскаунтер реализует счетчик {частный летучий длинный счетчик = 0; частный небезопасный небезопасный; частное долгое смещение; public cascounter () бросает исключение {uncafe = getunsafe (); offset = unceabe.objectfieldoffset (cascounter.class.getDeclaredfield ("counter")); } @Override public void urcement () {задолго до = counter; while (! uncefe.compareandswaplong (это, смещено, до, до + 1)) {до = счетчика; }} @Override public long getCounter () {return counter; }}Благодаря тестированию вышеуказанная структура данных в основном такая же, как и эффективность атомных переменных Java. Атомные переменные Java также используют метод CompaseAndsWap () Unfea, и этот метод в конечном итоге будет соответствовать соответствующим примитивам ЦП, поэтому он очень эффективен. Вот решение для реализации без блокировки Hashmap (http://www.azulsystems.com/about_us/presentations/lock-free-hash. Идея этого решения: анализировать каждое состояние, создавать копии, модифицировать копии, использовать примитивы CAS, спиновые блокировки). В обычных серверных машинах (Core <32), используя Concurrenthashmap (до JDK8, была реализована 16-канальное блокировка разделения по умолчанию, и ConcurrenthAshmap был реализован с использованием блокировки), очевидно, достаточно.
Суммировать
Вышеуказанное - все содержание этой статьи. Я надеюсь, что содержание этой статьи имеет определенную справочную ценность для каждого обучения или работы. Если у вас есть какие -либо вопросы, вы можете оставить сообщение для общения. Спасибо за поддержку Wulin.com.