¿Por qué existe sun.misc.Unsafe y cómo se puede usar en el mundo real? [cerrado]

267

Me encontré con el paquete sun.misc. Inseguro el otro día y me sorprendió lo que podía hacer.

Por supuesto, la clase no está documentada, pero me preguntaba si alguna vez había una buena razón para usarla. ¿Qué escenarios podrían surgir en los que tendrías que usarlo? ¿Cómo podría usarse en un escenario del mundo real?

Además, si lo necesita, ¿eso no indica que algo esté mal con su diseño?

¿Por qué Java incluso incluye esta clase?

Pdeva
fuente
77
Los desarrolladores de JDK están revisando esta API para una posible transformación en una API pública en Java 9. Si la usa, vale la pena tomar 5 minutos para completar la encuesta: surveymonkey.com/s/sun-misc-Unsafe .
Andy Lynch
2
Esta publicación se está discutiendo en meta: meta.stackoverflow.com/questions/299139/…
Jon Clements

Respuestas:

159

ejemplos

  1. VM "intrinsificación". es decir, CAS (Compare-And-Swap) utilizado en tablas de hash sin bloqueo, por ejemplo: sun.misc.Unsafe.compareAndSwapInt puede hacer llamadas JNI reales en código nativo que contiene instrucciones especiales para CAS

    Lea más sobre CAS aquí http://en.wikipedia.org/wiki/Compare-and-swap

  2. La funcionalidad sun.misc.Unsafe de la máquina virtual host se puede usar para asignar objetos no inicializados y luego interpretar la invocación del constructor como cualquier otra llamada a método.

  3. Uno puede rastrear los datos de la dirección nativa. ¡Es posible recuperar la dirección de memoria de un objeto usando la clase java.lang.Unsafe, y operar en sus campos directamente a través de métodos inseguros get / put!

  4. Optimizaciones de tiempo de compilación para JVM. VM de alto rendimiento con "magia", que requiere operaciones de bajo nivel. por ejemplo: http://en.wikipedia.org/wiki/Jikes_RVM

  5. Asignación de memoria, sun.misc.Unsafe.allocateMemory, por ejemplo: - El constructor DirectByteBuffer lo llama internamente cuando se invoca ByteBuffer.allocateDirect

  6. Rastreo de la pila de llamadas y reproducción con valores instanciados por sun.misc.Unsafe, útil para instrumentación

  7. sun.misc.Unsafe.arrayBaseOffset y arrayIndexScale se pueden usar para desarrollar arraylets, una técnica para dividir de manera eficiente matrices grandes en objetos más pequeños para limitar el costo en tiempo real de las operaciones de escaneo, actualización o movimiento en objetos grandes

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

más sobre referencias aquí: http://bytescrolls.blogspot.com/2011/04/interesting-uses-of-sunmiscunsafe.html

zudokod
fuente
1
si obtiene la dirección de un campo usando Inseguro, el GC siempre puede cambiarlo, entonces, ¿no es esa operación bastante inútil?
pdeva
obtener la dirección de los que ha asignado
zudokod
¿Qué quieres decir exactamente con el que he asignado? Esto parece ser usado en lugares donde los objetos fueron creados usando el operador 'nuevo', de ahí mi pregunta.
pdeva
1
unsafe.allocateMemory y poner el valor
zudokod
1
Con respecto al punto 2, me gustaría saber cómo puede invocar al constructor como cualquier otra llamada a método. Porque no encontré ninguna forma de hacerlo a menos que en bytecodes.
Miguel Gamboa
31

Solo de ejecutar una búsqueda en algún motor de búsqueda de códigos obtengo los siguientes ejemplos:

Clase simple para obtener acceso al objeto {@link Unsafe}. {@link Unsafe} * es necesario para permitir operaciones CAS eficientes en matrices. Tenga en cuenta que las versiones en {@link java.util.concurrent.atomic}, como {@link java.util.concurrent.atomic.AtomicLongArray}, requieren garantías de pedido de memoria adicionales que generalmente no son necesarias en estos algoritmos y también son caras en la mayoría de los procesadores.

  • SoyLatte - java 6 para el extracto de osx javadoc

/ ** Clase base para sun.misc. FieldAccessors basado en seguridad para campos estáticos. La observación es que solo hay nueve tipos de campos desde el punto de vista del código de reflexión: los ocho tipos primitivos y el Objeto. El uso de clase insegura en lugar de códigos de bytes generados ahorra memoria y tiempo de carga para los FieldAccessors generados dinámicamente. * /

  • SpikeSource

/ * FinalFields que se envían a través del cable ... ¿cómo desarmar y recrear el objeto en el lado receptor? No queremos invocar al constructor ya que establecería valores para los campos finales. Tenemos que recrear el campo final exactamente como estaba en el lado del remitente. Sun.misc.Unsefe hace esto por nosotros. * /

Hay muchos otros ejemplos, solo siga el enlace de arriba ...

Asaf
fuente
25

Interesante, nunca había oído hablar de esta clase (que probablemente sea algo bueno, de verdad).

Una cosa que me viene a la mente es usar Unsafe # setMemory para poner a cero los búferes que contenían información confidencial en un punto (contraseñas, claves, ...). Incluso podría hacer esto en campos de objetos "inmutables" (por otra parte, supongo que una reflexión antigua también podría ser útil aquí). Sin embargo, no soy un experto en seguridad, así que tómalo con un grano de sal.

Mike Daniels
fuente
44
I'd never even heard of this class... ¡Te lo he dicho muchas veces! suspiro + :(
Tim Bender
77
No tendría sentido, ya que Java utiliza un recolector de basura generacional de copiado y su información confidencial probablemente ya esté ubicada en otro lugar en la memoria 'libre' a la espera de ser sobrescrita.
Daniel Cassidy
39
Nunca he oído hablar de él tampoco, pero me encanta su park()documentación: "Bloquea el subproceso actual, regresando cuando se produce una desconexión de equilibrio, o ya se ha producido una desconexión de equilibrio, o el hilo se interrumpe, o, si no es absoluto y el tiempo no es cero, el dado el tiempo transcurrido nanosegundos, o si es absoluto, la fecha límite dada en milisegundos desde que Epoch ha pasado, o falsamente (es decir, regresando sin 'razón') ". Casi tan bueno como "la memoria se libera cuando el programa sale o, a intervalos aleatorios, lo que ocurra primero".
Aroth
1
@Daniel, interesante, no lo había considerado. Ahora puedes ver por qué no soy un experto en seguridad. :)
Mike Daniels
22

Basado en un análisis muy breve de la biblioteca Java 1.6.12 usando eclipse para el rastreo de referencia, parece que cada funcionalidad útil de Unsafeestá expuesta de maneras útiles.

Las operaciones CAS se exponen a través de las clases Atomic *. Las funciones de manipulación de la memoria se exponen a través de DirectByteBuffer Sync. Las instrucciones (park, unpark) se exponen a través del AbstractQueuedSynchronizer, que a su vez es utilizado por las implementaciones de Lock.

Tim Bender
fuente
Los AtomicXXXUpdaters son demasiado lentos y cuando realmente los necesita: CAS: no puede permitirse el lujo de usarlos. Si va a hacer el metal, no utilizará los niveles de abstracción y numerosas comprobaciones. Fallar el CAS es malo en un bucle esp. cuando el hardware decide predecir erróneamente la rama (debido a una alta contención) pero tener pocas comparaciones / ramas más simplemente duele. Park / Unpark está expuesto a través de LockSupportno AQS (este último es más un bloqueo implícito que park / unpark)
bestsss
21

Unsafe.throwException : permite lanzar excepciones comprobadas sin declararlas.

Esto es útil en algunos casos en los que se trata de reflexión o AOP.

Suponga que construye un proxy genérico para una interfaz definida por el usuario. Y el usuario puede especificar qué excepción es lanzada por la implicación en un caso especial simplemente declarando la excepción en la interfaz. Entonces, esta es la única manera que sé, de generar una excepción marcada en la Implementación dinámica de la interfaz.

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();
    }
}
Ralph
fuente
14
puede hacer lo mismo Thread.stop(Throwable)sin necesidad de inseguridad, en el mismo hilo puede lanzar cualquier cosa de todos modos (no hay verificación de compilación)
bestsss
Puede hacerlo solo a través de bytecode (o use Lomboc para hacerlo por usted)
Antimony
1
@bestsss Ese método se ha desactivado y arroja un UnsupportedOperationExceptionen el hilo actual a partir de Java 8. Sin embargo, la versión sin argumentos que arroja ThreadDeathtodavía funciona.
gparyani
@damryfbfnetsi, no he seguido las discusiones centrales de jdk durante bastante tiempo y no tengo planes de mudarme a Java 8. Sin embargo, esta es una idea bastante desconcertante, ya que de todos modos es trivial ser implementado por la generación de bytecode, a menos que ahora el verificador realmente compruebe si El método declara los objetos arrojables ... pero eso podría ser incompatible con versiones anteriores, ya que los metadatos sobre la excepción lanzada se podían descartar.
bestsss
10

Clase insegura

Una colección de métodos para realizar operaciones inseguras de bajo nivel. Aunque la clase y todos los métodos son públicos, el uso de esta clase es limitado porque solo el código confiable puede obtener instancias de la misma.

Un uso de esto es en java.util.concurrent.atomicclases:

Margus
fuente
6

Para una copia de memoria eficiente (más rápida de copiar que System.arraycopy () para bloques cortos al menos); tal como lo utilizan los códecs Java LZF y Snappy . Usan 'getLong' y 'putLong', que son más rápidos que hacer copias byte a byte; especialmente eficiente al copiar cosas como bloques de 16/32/64 bytes.

StaxMan
fuente
1
Doh, arraycopy utiliza bucles SSE en x86-64 que son mejores que getLong/putLong(y también tienes que calcular la dirección)
bestsss
¿Realmente has medido esto? Para bloques más cortos veo un rendimiento consistentemente mejor en x86-64 cuando uso la combinación de getLong/ putLong: idealmente preferiría la System.arraycopy()simplicidad y todo; pero las pruebas reales mostraron lo contrario para los casos que he probado.
StaxMan
Sí, utilizando inseguro no podría ningún rendimiento significativo de desinflar impl. Para varios bytes, las copias largas en matrices grandes get / putLong podrían funcionar cuando el compilador tiene que verificar las longitudes. Algunos impl. agregue una valla de memoria más allá de System.arrayCopy (aunque se puede deshabilitar / habilitar), por lo que podría ser el verdadero culpable.
bestsss
Okay. Es posible que los JDK más nuevos hayan cambiado esto; originalmente cuando observé una operación más rápida (con JDK 1.6) también me sorprendió. O tal vez estoy olvidando alguna diferencia específica en el uso. Estas son optimizaciones difíciles (y posiblemente inestables), incluso cuando funcionan, y es esencial medir los efectos.
StaxMan
5

Recientemente estuve trabajando en reimplementar la JVM y descubrí que se implementa un sorprendente número de clases en términos de Unsafe. La clase está diseñada principalmente para los implementadores de la biblioteca Java y contiene características que son fundamentalmente inseguras pero necesarias para construir primitivas rápidas. Por ejemplo, existen métodos para obtener y escribir desplazamientos de campo sin procesar, mediante sincronización a nivel de hardware, asignación y liberación de memoria, etc. No está destinado a ser utilizado por programadores normales de Java; es indocumentado, específico de implementación e inherentemente inseguro (de ahí el nombre). Además, creo que SecurityManagerno permitirá el acceso a él en casi todos los casos.

En resumen, existe principalmente para permitir que los implementadores de la biblioteca accedan a la máquina subyacente sin tener que declarar todos los métodos en ciertas clases como AtomicIntegernativo. No debería necesitar usarlo ni preocuparse por él en la programación Java de rutina, ya que el objetivo es hacer que el resto de las bibliotecas sean lo suficientemente rápidas como para no necesitar ese tipo de acceso.

templatetypedef
fuente
en realidad, el SecurityManager no permite el acceso a él solo si la reflexión está desactivada
amara
@ sparkleshy- ¿Puedes dar más detalles sobre esto?
templatetypedef
mientras que consigue una instancia de getUnsafe tiene requisitos bastante estrictos, Unsafe.class.getDeclaredField("theUnsafe")con .setAccessible(true)y luego .get(null)va a conseguirlo también
Amara
@ sparkleshy: me sorprende que funcione, el gerente de seguridad debería estar señalando eso.
templatetypedef
5

Úselo para acceder y asignar grandes cantidades de memoria de manera eficiente, como en su propio motor de vóxel. (es decir, juego de estilo Minecraft).

En mi experiencia, la JVM a menudo no puede eliminar la verificación de límites en el lugar donde realmente la necesita. Por ejemplo, si está iterando sobre una matriz grande, pero el acceso a la memoria real está oculto debajo de una llamada de método no virtual * en el bucle, la JVM aún puede realizar una verificación de límites con cada acceso a la matriz, en lugar de una vez justo antes el lazo. Por lo tanto, para obtener ganancias de rendimiento potencialmente grandes, puede eliminar la verificación de límites de JVM dentro del bucle a través de un método que emplea sun.misc.Nosafe para acceder a la memoria directamente, asegurándose de hacer cualquier verificación de límites usted mismo en los lugares correctos. (Usted está gonna comprobación de límites en algún nivel, ¿verdad?)
* por no virtual, quiero decir que la JVM no debería tener que resolver dinámicamente cualquiera que sea su método en particular, porque ha garantizado correctamente que la clase / método / instancia son una combinación de static / final / what-have-you.

Para mi motor de vóxel de cosecha propia, esto resultó en un aumento dramático del rendimiento durante la generación y la serialización de fragmentos (en algunos lugares donde estaba leyendo / escribiendo en toda la matriz de una vez). Los resultados pueden variar, pero si la falta de eliminación de límites es su problema, esto lo solucionará.

Hay algunos problemas potencialmente importantes con esto: específicamente, cuando proporciona la capacidad de acceder a la memoria sin verificar los límites a los clientes de su interfaz, probablemente abusarán de ella. (No olvide que los piratas informáticos también pueden ser clientes de su interfaz ... especialmente en el caso de un motor de vóxel escrito en Java). Por lo tanto, debe diseñar su interfaz de tal manera que no se pueda abusar del acceso a la memoria, o usted debe ser extremadamente cuidadoso para validar datos de usuario antes de que pueda nunca, nunca se mezclan con su interfaz peligroso. Teniendo en cuenta las cosas catastróficas que un pirata informático puede hacer con el acceso a la memoria sin control, probablemente sea mejor adoptar ambos enfoques.

Philip Guin
fuente
4

Las colecciones fuera del montón pueden ser útiles para asignar grandes cantidades de memoria y desasignarla inmediatamente después de su uso sin interferencia de GC. Escribí una biblioteca para trabajar con matrices / listas fuera del montón basadas en sun.misc.Unsafe.

alexkasko
fuente
4

Hemos implementado grandes colecciones como Arrays, HashMaps, TreeMaps usando Unsafe.
Y para evitar / minimizar la fragmentación, implementamos un asignador de memoria utilizando los conceptos de dlmalloc sobre inseguro.
Esto nos ayudó a obtener el rendimiento en concurrencia.

pradipmw
fuente
3

Unsafe.park()y Unsafe.unpark()para la construcción de estructuras de control de concurrencia personalizadas y mecanismos de programación cooperativa.

andersoj
fuente
24
disponible públicamente comojava.util.concurrent.locks.LockSupport
bestsss
1

No lo he usado yo mismo, pero supongo que si tiene una variable que solo se lee ocasionalmente en más de un hilo (por lo que realmente no desea que sea volátil), puede usar el putObjectVolatileal escribirlo en el hilo principal y readObjectVolatileal hacer las lecturas raras de otros hilos.

Matt Crinklaw-Vogt
fuente
1
pero de acuerdo con la discusión en el siguiente hilo, los volátiles sin contenido son casi tan rápidos como los no volátiles de todos modos stackoverflow.com/questions/5573782/…
pdeva
no puede reemplazar la semántica volátil con escrituras simples y lecturas volátiles ... esta es una receta para el desastre, ya que puede funcionar en una configuración pero no en otra. Si está buscando tener una semántica volátil con un solo hilo de escritura, puede usar AtomicReference.lazySet en el hilo de escritura y obtener () en los lectores (vea esta publicación para una discusión sobre el tema). Las lecturas volátiles son relativamente baratas, pero no gratuitas, consulte aquí .
Nitsan Wakart el
"... podrías usar putObjectVolatile al escribirlo ..." No estaba sugiriendo escrituras simples.
Matt Crinklaw-Vogt
1

Lo necesita si necesita reemplazar la funcionalidad proporcionada por una de las clases que la usa actualmente.

Esto puede ser una serialización / deserialización personalizada / más rápida / más compacta, una versión de búfer / redimensionable más rápida / más grande de ByteBuffer, o agregar una variable atómica, por ejemplo, una no admitida actualmente.

Lo he usado para todo esto en algún momento.

Peter Lawrey
fuente
0

El objeto parece ser la disponibilidad para trabajar a un nivel inferior al que permite el código Java. Si está codificando una aplicación de alto nivel, la JVM abstrae el manejo de la memoria y otras operaciones fuera del nivel de código, por lo que es más fácil de programar. Al usar la biblioteca insegura, está completando operaciones de bajo nivel que normalmente se realizarían por usted.

Como dijo woliveirajr, "random ()" usa Unsafe para sembrar, al igual que muchas otras operaciones usarán la función allocateMemory () incluida en Unsafe.

Como programador, probablemente podría salirse con la suya sin necesitar esta biblioteca, pero tener un control estricto sobre los elementos de bajo nivel es útil (es por eso que todavía hay ensamblaje y (en menor medida) código C a la deriva en los principales productos)

Grambot
fuente