Почему существует sun.misc.Unsafe и как его можно использовать в реальном мире?

Я наткнулся на sun.misc.Unsafe пакет на днях и был поражен тем, что он мог сделать.

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

Кроме того, если вам это нужно, значит, это не указывает на то, что что-то не так с вашим дизайном?

Почему Java даже включает этот класс?

Ответ 1

примеры

  • Взаимодействие VM. " т.е. CAS (Compare-And-Swap), используемый в таблицах Hash-Free Hash например: sun.misc.Unsafe.compareAndSwapInt он может превращать реальные вызовы JNI в собственный код, содержащий специальные инструкции для CAS

    Узнайте больше о CAS здесь http://en.wikipedia.org/wiki/Compare-and-swap

  • Функция sun.misc.Unsafe главной виртуальной машины может использоваться для выделения неинициализированных объектов, а затем интерпретировать вызов конструктора как любой другой вызов метода.

  • Можно отслеживать данные из собственного адреса. Можно получить адреса памяти объектов, используя класс java.lang.Unsafe, и работать с его полями напрямую через небезопасные методы get/put!

  • Оптимизация времени компиляции для JVM. HIgh производительность VM с использованием "магии", требующей операций низкого уровня. например: http://en.wikipedia.org/wiki/Jikes_RVM

  • Выделение памяти, sun.misc.Unsafe.allocateMemory, например: - Конструктор DirectByteBuffer внутренне вызывает его при вызове ByteBuffer.allocateDirect

  • Трассировка стека вызовов и повторное воспроизведение со значениями, созданными sun.misc.Unsafe, полезными для аппаратуры

  • sun.misc.Unsafe.arrayBaseOffset и arrayIndexScale могут использоваться для разработки arraylets, метода эффективного разбиения больших массивов на более мелкие объекты, чтобы ограничить затраты времени на сканирование, обновление или перемещение на больших объектах в режиме реального времени

  • http://robaustin.wikidot.com/how-to-write-to-direct-memory-locations-in-java

подробнее о ссылках здесь - http://bytescrolls.blogspot.com/2011/04/interesting-uses-of-sunmiscunsafe.html

Ответ 2

Просто от запуска search в некоторой поисковой системе кода я получаю следующие примеры:

Простой класс для доступа к объекту {@link Unsafe}. {@link Unsafe}  * требуется для эффективной работы CAS на массивах. Обратите внимание, что  версии в {@link java.util.concurrent.atomic}, например {@link  java.util.concurrent.atomic.AtomicLongArray}, требуют дополнительного упорядочения памяти  гарантии, которые обычно не нужны в этих алгоритмах, а также  дорогой для большинства процессоров.

  • SoyLatte - java 6 для фрагмента osx javadoc

/** Базовый класс для sun.misc.Unsafe FieldAccessors для статических     поля. Наблюдение состоит в том, что существует только девять типов     поля с точки зрения кода отражения: восемь примитивных     типов и объектов. Использование класса Unsafe вместо сгенерированного     байт-коды сохраняют память и время загрузки для     динамически генерируемые FieldAccessors. */

  • SpikeSource

/*  FinalFields, которые отправляются через провод. Как размонтировать и воссоздать объект на  принимающая сторона? Мы не хотим вызывать конструктор, так как он установит значения для  конечные поля. Мы должны воссоздать окончательное поле точно так же, как и на стороне отправителя.  Sun.misc.Unsafe делает это для нас.  */

Есть много других примеров, просто следуйте приведенной выше ссылке...

Ответ 3

Интересно, я даже не слышал об этом классе (что, вероятно, хорошо, действительно).

Одна вещь, которая подскакивает, заключается в использовании Unsafe # setMemory для обнуления буферов, содержащих конфиденциальную информацию в одной точке (пароли, ключи,...). Вы могли бы даже сделать это в полях "неизменяемых" объектов (тогда, опять же, я полагаю, что простое старое отражение может сделать здесь трюк). Я не эксперт по безопасности, хотя и принимаю это с солью.

Ответ 4

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

Операции CAS отображаются через классы Atomic *. Функции манипуляции с памятью отображаются через DirectByteBuffer Инструкции синхронизации (park, unpark) отображаются через AbstractQueuedSynchronizer, который, в свою очередь, используется реализациями Lock.

Ответ 5

Unsafe.throwException - позволяет исключить проверенное исключение, не объявляя их.

Это полезно в некоторых случаях, когда вы имеете дело с отражением или AOP.

Предположим, что вы создаете общий прокси для пользовательского интерфейса. И пользователь может указать, какое исключение вызывается импликацией в специальном случае, просто объявив исключение в интерфейсе. Тогда это единственный способ, которым я знаю, создать проверенное исключение в динамической реализации интерфейса.

import org.junit.Test;
/** need to allow forbidden references! */ import sun.misc.Unsafe;

/**
 * Demonstrate how to throw an undeclared checked exception.
 * This is a hack, because it uses the forbidden Class {@link sun.misc.Unsafe}.
 */
public class ExceptionTest {

    /**
     * A checked exception.
     */
    public static class MyException extends Exception {
        private static final long serialVersionUID = 5960664994726581924L;
    }

    /**
     * Throw the Exception.
     */
    @SuppressWarnings("restriction")
    public static void throwUndeclared() {
        getUnsafe().throwException(new MyException());
    }

    /**
     * Return an instance of {@link sun.misc.Unsafe}.
     * @return THE instance
     */
    @SuppressWarnings("restriction")
    private static Unsafe getUnsafe() {
        try {

            Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe");
            singleoneInstanceField.setAccessible(true);
            return (Unsafe) singleoneInstanceField.get(null);

        } catch (IllegalArgumentException e) {
            throw createExceptionForObtainingUnsafe(e);
        } catch (SecurityException e) {
            throw createExceptionForObtainingUnsafe(e);
        } catch (NoSuchFieldException e) {
            throw createExceptionForObtainingUnsafe(e);
        } catch (IllegalAccessException e) {
            throw createExceptionForObtainingUnsafe(e);
        }
    }

    private static RuntimeException createExceptionForObtainingUnsafe(final Throwable cause) {
        return new RuntimeException("error while obtaining sun.misc.Unsafe", cause);
    }


    /**
     * scenario: test that an CheckedException {@link MyException} can be thrown
     * from an method that not declare it.
     */
    @Test(expected = MyException.class)
    public void testUnsingUnsaveToThrowCheckedException() {
        throwUndeclared();
    }
}

Ответ 6

Класс Unsafe

Набор методов для выполнения низкоуровневых, небезопасных операций. Хотя класс и все методы общедоступны, использование этого класса ограничено, потому что только доверенный код может получить его экземпляры.

Одно из них - в классах java.util.concurrent.atomic:

Ответ 7

Для эффективной копии памяти (быстрее скопировать, чем System.arraycopy() для коротких блоков); как используется Java LZF и Snappy кодеки. Они используют "getLong" и "putLong", которые быстрее, чем копирование побайтно; особенно эффективны при копировании таких вещей, как блоки 16/32/64 байтов.

Ответ 8

Используйте его для эффективного доступа и распределения больших объемов памяти, например, в вашем собственном движке вокселей! (т.е. игра в стиле Minecraft.)

По моему опыту, JVM часто не может устранить проверку границ, в которой вы действительно нуждаетесь. Например, если вы выполняете итерацию по большому массиву, но фактический доступ к памяти подпадает под вызов не виртуального * метода в цикле, JVM может все еще выполнять проверку границ с каждым доступом к массиву, а не раз перед этим петля. Таким образом, для потенциально большого прироста производительности вы можете исключить проверку границ JVM внутри цикла с помощью метода, который использует sun.misc.Unsafe для непосредственного доступа к памяти, следя за тем, чтобы все границы проверялись в правильных местах. (Вы проведете проверку на каком-то уровне, не так ли?)
* не виртуальным, я имею в виду, что JVM не должен динамически решать все, что ваш конкретный метод, потому что вы правильно гарантировали, что класс/метод/экземпляр представляют собой некоторую комбинацию статических/конечных/вы.суб >

Для моего доморощенного двигателя воксела это привело к резкому увеличению производительности при генерации и сериализации блоков (в тех местах, где я сразу читал/записывал весь массив). Результаты могут различаться, но если недостатком ограничений является ваша проблема, тогда это исправит.

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

Ответ 9

Недавно я работал над переопределением JVM и обнаружил, что удивительное количество классов реализовано с точки зрения Unsafe. Класс в основном предназначен для разработчиков библиотеки Java и содержит функции, которые являются принципиально небезопасными, но необходимыми для создания быстрых примитивов. Например, существуют методы получения и записи исходных смещений полей, использование аппаратной синхронизации, выделение и освобождение памяти и т.д. Он не предназначен для использования нормальными Java-программистами; он недокументирован, специфичен для реализации и по своей сути небезопасен (отсюда и название!). Более того, я думаю, что SecurityManager будет запрещать доступ к нему почти во всех случаях.

Короче говоря, он в основном существует, чтобы позволить библиотечным реализаторам доступ к базовому компьютеру без необходимости объявлять каждый метод в определенных классах, например AtomicInteger native. Вы не должны использовать или беспокоиться об этом в рутинном программировании на Java, поскольку все дело в том, чтобы сделать остальные библиотеки достаточно быстрыми, чтобы вам не нужен такой доступ.

Ответ 11

Unsafe.park() и Unsafe.unpark() для построения пользовательских структур управления concurrency и механизмов совместного планирования.

Ответ 12

Мы внедрили огромные коллекции, такие как массивы, HashMaps, TreeMaps, используя Unsafe.
Чтобы избежать/минимизировать фрагментацию, мы реализовали распределитель памяти, используя концепции dlmalloc по небезопасным.
Это помогло нам получить производительность в concurrency.

Ответ 13

Не использовал его сам, но я полагаю, что если у вас есть переменная, которую иногда читают более чем один поток (так что вы действительно не хотите, чтобы она была изменчивой), вы могли бы использовать putObjectVolatile при записи он в основном потоке и readObjectVolatile при выполнении редких чтений из других потоков.

Ответ 15

Объект, по-видимому, доступен для работы на более низком уровне, чем обычно позволяет код Java. Если вы кодируете приложение высокого уровня, JVM абстрагирует обработку памяти и другие операции от уровня кода, поэтому ее проще программировать. Используя библиотеку Unsafe, вы эффективно выполняете операции низкого уровня, которые обычно выполняются для вас.

Как заявил woliveirajr, "random()" использует Unsafe для семян, так же как и многие другие операции будут использовать функцию allocateMemory(), включенную в Unsafe.

Как программист, вы, вероятно, могли бы уйти, никогда не нуждаясь в этой библиотеке, но жесткий контроль над элементами низкого уровня пригодится (почему существует еще Ассамблея и (в меньшей степени) C-код, дрейфующий в основных продуктах )

Ответ 16

Вам это нужно, если вам нужно заменить функциональность, предоставляемую одним из классов, который использует ее в настоящее время.

Это может быть обычная/быстрая/более компактная сериализация/десериализация, более быстрая/большая версия ByteBuffer для буфера/изменяемого размера или добавление атомной переменной, например. один из них не поддерживается в настоящее время.

Я использовал его для всех из них в какое-то время.