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

El otro día encontré el paquete sun.misc.Unsafe y me sorprendió lo que podría hacer.

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

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

¿Por qué Java incluso incluye esta clase?

ejemplos

  1. VM “intrinsificación”. es decir, CAS (Compare-And-Swap) utilizado en Tablas Hash Libres de 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 anfitriona se puede usar para asignar objetos no inicializados y luego interpretar la invocación del constructor como cualquier otra llamada a método.

  3. Se puede rastrear los datos desde 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 comstackción para JVM. Alto rendimiento VM usando “magia”, que requiere operaciones de bajo nivel. Por ejemplo: http://en.wikipedia.org/wiki/Jikes_RVM

  5. Asignando memoria, sun.misc.Unsafe.allocateMemory por ejemplo: – El constructor DirectByteBuffer lo llama internamente cuando se invoca ByteBuffer.allocateDirect

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

  7. sun.misc.Unsafe.arrayBaseOffset y arrayIndexScale se pueden usar para desarrollar arraylets, una técnica para dividir de manera eficiente grandes matrices en objetos más pequeños para limitar el costo en tiempo real de escanear, actualizar o mover operaciones 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

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

  • Notación de objetos Java: úselo para un procesamiento de matriz más eficiente, citando el javadoc

Clase simple para obtener acceso al objeto {@link Unsafe}. {@link Unsafe} * es necesario para permitir operaciones eficientes de CAS 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 orden de memoria adicionales que generalmente no son necesarias en estos algoritmos y también son costosas. en la mayoría de los procesadores.

  • SoyLatte – java 6 para osx javadoc extracto

/ ** Clase base para sun.misc. FieldAccessors basado en hardware 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 bytecodes 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 destrabar y recrear el objeto en el lado de recepción? 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. El sun.misc.Unsafe hace esto por nosotros. * /

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

Interesante, nunca había escuchado sobre esta clase (que probablemente sea algo bueno, de verdad).

Una cosa que salta a la mente es utilizar Unsafe # setMemory para poner a cero los búferes que contenían información confidencial en un punto (contraseñas, claves, …). Podrías incluso hacer esto en campos de objetos “inmutables” (entonces supongo que también el antiguo reflection podría ser el truco). Aunque no soy un experto en seguridad, así que tómalo con un grano de sal.

Unsafe.throwException – permite lanzar una excepción marcada sin declararlos.

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

Supongamos que crea un proxy genérico para una interfaz definida por el usuario. Y el usuario puede especificar qué excepción arroja la implementación en un caso especial simplemente declarando la excepción en la interfaz. Entonces esta es la única forma que conozco, para elevar una excepción comprobada 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(); } } 

Basado en un análisis muy breve de la biblioteca Java 1.6.12 que usa eclipse para el rastreo de referencia, parece como si cada funcionalidad útil de Unsafe expuesta de manera útil.

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

Clase insegura

Una colección de métodos para realizar operaciones de bajo nivel e inseguras. 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 este.

Un uso de esto es en las clases java.util.concurrent.atomic :

  • AtomicIntegerArray
  • AtomicLongArray

Para una copia de memoria eficiente (más rápida para 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 cuando se copian elementos como bloques de 16/32/64 bytes.

Úselo para acceder y asignar grandes cantidades de memoria de manera eficiente, ¡como en su propio motor Voxel! (es decir, juego al estilo de Minecraft.)

En mi experiencia, la JVM a menudo no puede eliminar la comprobación de límites en el lugar que realmente la necesita. Por ejemplo, si está iterando sobre una matriz grande, pero el acceso real a la memoria está metido debajo de una llamada a un método no virtual en el bucle, la JVM aún puede realizar una comprobación de límites con cada acceso a la matriz, en lugar de una sola vez el lazo. Por lo tanto, para aumentos de rendimiento potencialmente grandes, puede eliminar la comprobación de límites de JVM dentro del ciclo a través de un método que emplea sun.misc.Unsafe para acceder directamente a la memoria, asegurándose de hacer comprobaciones de límites en los lugares correctos. (Vas a ver los límites en algún nivel, ¿verdad?)
* por no virtual, me refiero a que la JVM no debería tener que resolver dinámicamente cualquiera que sea su método en particular, porque ha garantizado correctamente que class / method / instance son alguna combinación de static / final / what-have-you.

Para mi motor de vóxeles de fabricación casera, esto resultó en una ganancia de rendimiento dramática durante la generación de fragmentos y la serialización (en los lugares donde estaba leyendo / escribiendo todo el conjunto a la vez). Los resultados pueden variar, pero si su problema es la falta de eliminación de límites, entonces esto lo solucionará.

Hay algunos problemas potencialmente importantes con esto: específicamente, cuando proporciona la capacidad de acceder a la memoria sin límites, verificando a los clientes de su interfaz, probablemente abusen de ella. (No olvide que los hackers 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 forma tal que no se pueda abusar del acceso a la memoria, o debe ser extremadamente cuidadoso para validar los datos del usuario antes de que pueda mezclarse alguna vez con su interfaz peligrosa. Teniendo en cuenta las cosas catastróficas que un hacker puede hacer con el acceso a la memoria sin marcar, es mejor tomar ambos enfoques.

Hace poco estuve trabajando en la reimplementación de la JVM y descubrí que se implementan 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 crear primitivas rápidas. Por ejemplo, hay métodos para obtener y escribir desplazamientos de campo sin procesar, utilizar la sincronización a nivel de hardware, asignar y liberar memoria, etc. No está destinado a ser utilizado por progtwigdores Java normales; es indocumentado, específico de la implementación e intrínsecamente inseguro (¡de ahí el nombre!). Además, creo que el SecurityManager no permitirá el acceso a él en casi todos los casos.

En resumen, existe principalmente para permitir que los implementadores de bibliotecas accedan a la máquina subyacente sin tener que declarar todos los métodos en ciertas clases como AtomicInteger nativo. No debería necesitar usar o preocuparse por ello en la progtwigción Java de rutina, ya que el objective es hacer que el rest de las bibliotecas sea lo suficientemente rápido para que no necesite ese tipo de acceso.

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

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

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

No lo he usado yo mismo, pero supongo que si tienes una variable que solo se lee de vez en cuando por más de un hilo (por lo que realmente no quieres que sea volátil) podrías usar putObjectVolatile al escribirlo en el hilo principal y readObjectVolatile al hacer las lecturas raras de otros hilos.

Lo necesita si necesita reemplazar la funcionalidad provista 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 más rápida / más grande de búfer / de tamaño variable de ByteBuffer, o la adición de una variable atómica, por ejemplo, una que no es compatible actualmente.

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

Un ejemplo de su uso es el método aleatorio, que llama a los inseguros para cambiar la semilla .

Este sitio también tiene algunos usos de él .

El objeto parece estar disponible para funcionar a un nivel inferior al que normalmente 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 del nivel del código, por lo que es más fácil de progtwigr. Al usar la biblioteca Insegura, estás completando de manera efectiva las operaciones de bajo nivel que normalmente se realizarían por ti.

Como woliveirajr declaró “random ()” usa Unsafe para inicializar al igual que muchas otras operaciones usarán la función allocateMemory () incluida en Inseguro.

Como progtwigdor, 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 flotando en los principales productos)