Por que o sun.misc.Unsafe existe e como pode ser usado no mundo real? [fechadas]


267

Me deparei com o pacote sun.misc.Unsafe no outro dia e fiquei surpreso com o que poderia fazer.

Obviamente, a classe não é documentada, mas eu estava pensando se havia alguma boa razão para usá-la. Quais cenários podem surgir onde você precisaria usá-lo? Como pode ser usado em um cenário do mundo real?

Além disso, se você fazer necessidade ele, isso não indica que algo provavelmente está errado com o seu design?

Por que o Java inclui essa classe?


7
Os desenvolvedores do JDK estão atualmente revisando essa API para possível transformação em uma API pública em Java 9. Se você a usar, vale a pena levar 5 minutos para preencher a pesquisa: surveymonkey.com/s/sun-misc-Unsafe .
Andy Lynch

2
Esta postagem está sendo discutida em meta: meta.stackoverflow.com/questions/299139/…
Jon Clements

Respostas:


159

exemplos

  1. VM "intrinsificação". ou seja, CAS (comparar e trocar) usado em tabelas de hash sem bloqueio, por exemplo: sun.misc.Unsafe.compareAndSwapInt, pode fazer chamadas JNI reais em código nativo que contém instruções especiais para CAS

    leia mais sobre o CAS aqui http://en.wikipedia.org/wiki/Compare-and-swap

  2. A funcionalidade sun.misc.Unsafe da VM do host pode ser usada para alocar objetos não inicializados e, em seguida, interpretar a chamada do construtor como qualquer outra chamada de método.

  3. É possível recuperar os dados do endereço nativo. É possível recuperar o endereço de memória de um objeto usando a classe java.lang.Unsafe e operar seus campos diretamente através de métodos inseguros de obtenção / colocação!

  4. Otimizações de tempo de compilação para JVM. Alto desempenho da VM usando "mágica", exigindo operações de baixo nível. por exemplo: http://en.wikipedia.org/wiki/Jikes_RVM

  5. Alocando Memória, sun.misc.Unsafe.allocateMemory, por exemplo: - O construtor DirectByteBuffer a chama internamente quando ByteBuffer.allocateDirect é chamado

  6. Rastreando a pilha de chamadas e reproduzindo com valores instanciados por sun.misc.Unsafe, útil para instrumentação

  7. sun.misc.Unsafe.arrayBaseOffset e arrayIndexScale podem ser usados ​​para desenvolver arraylets, uma técnica para dividir eficientemente grandes matrizes em objetos menores para limitar o custo em tempo real da verificação, atualização ou movimentação de operações em objetos grandes

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

mais sobre referências aqui - http://bytescrolls.blogspot.com/2011/04/interesting-uses-of-sunmiscunsafe.html


1
se você obtém o endereço de um campo usando Inseguro, ele sempre pode ser alterado pelo GC; portanto, essa operação não é muito inútil?
Pdeva

obter o endereço para os que você alocados
zudokod

o que exatamente você quer dizer com aquele que eu aloquei. isso parece ser usado em locais onde objetos foram criados usando o operador 'new', portanto, minha pergunta.
Pdeva

1
unsafe.allocateMemory e colocar o valor
zudokod

1
Com relação ao ponto 2, eu gostaria de saber como você pode chamar o construtor como qualquer outra chamada de método? Porque eu não encontrei nenhuma maneira de fazer isso, a não ser nos bytecodes.
Miguel Gamboa

31

Apenas ao executar uma pesquisa em algum mecanismo de pesquisa de código, obtenho os seguintes exemplos:

Classe simples para obter acesso ao objeto {@link Unsafe}. {@link Unsafe} * é necessário para permitir operações eficientes do CAS em matrizes. Observe que as versões em {@link java.util.concurrent.atomic}, como {@link java.util.concurrent.atomic.AtomicLongArray}, exigem garantias adicionais de pedido de memória que geralmente não são necessárias nesses algoritmos e também são caras. na maioria dos processadores.

  • SoyLatte - java 6 para osx javadoc excerpt

/ ** Classe base para FieldAccessors com base em segurança.misc. Para campos estáticos. A observação é que existem apenas nove tipos de campos do ponto de vista do código de reflexão: os oito tipos primitivos e Objeto. O uso da classe Insegura, em vez de gerados por códigos, economiza memória e tempo de carregamento para os FieldAccessors gerados dinamicamente. * /

  • SpikeSource

/ * FinalFields que são enviados através da ligação. Como desassociar e recriar o objeto no lado receptor? Não queremos chamar o construtor, pois ele estabeleceria valores para os campos finais. Temos que recriar o campo final exatamente como no lado do remetente. O sun.misc.Unsafe faz isso por nós. * /

Existem muitos outros exemplos, basta seguir o link acima ...


25

Interessante, eu nunca tinha ouvido falar dessa aula (o que provavelmente é uma coisa boa, na verdade).

Uma coisa que vem à mente é usar o Unsafe # setMemory para zerar buffers que continham informações confidenciais em um ponto (senhas, chaves, ...). Você pode até fazer isso em campos de objetos "imutáveis" (então, novamente, suponho que uma reflexão simples e antiga possa fazer o truque aqui também). Eu não sou especialista em segurança, então leve isso com um pouco de sal.


4
I'd never even heard of this class... Eu já falei sobre isso tantas vezes! suspiro + :(
Tim Bender

7
Não faria sentido, já que o Java usa um coletor de lixo geracional de cópia e suas informações confidenciais provavelmente já estarão localizadas em algum outro lugar na memória 'livre' esperando para serem substituídas.
Daniel Cassidy

39
Também nunca ouvi falar disso, mas eu adoro a park()documentação deles : "Bloqueie o encadeamento atual, retornando quando ocorrer um descompasso de balanceamento, ou já tiver ocorrido um descompasso de balanceamento, ou o encadeamento for interrompido ou, se não for absoluto e o tempo não for zero, o dado tempo, os nanossegundos transcorreram, ou se forem absolutos, o prazo especificado em milissegundos desde que a Época passou, ou espúrios (ou seja, retornando sem motivo) ". Quase tão bom quanto "a memória é liberada quando o programa sai ou, a intervalos aleatórios, o que ocorrer primeiro".
Aroth 15/04

1
@ Daniel, interessante, eu não tinha considerado isso. Agora você pode ver por que não sou especialista em segurança. :)
Mike Daniels

22

Com base em uma análise muito breve da biblioteca Java 1.6.12 usando eclipse para rastreamento de referência, parece que todas as funcionalidades úteis Unsafesão expostas de maneiras úteis.

As operações do CAS são expostas através das classes Atomic *. As funções de manipulação de memória são expostas através das instruções do DirectByteBuffer Sync (estacionar, desarmar) são expostas através do AbstractQueuedSynchronizer, que por sua vez é usado pelas implementações de bloqueio.


Os AtomicXXXUpdaters são muito lentos e quando você realmente precisa deles: CAS - você não pode usá-los realmente. Se você for fazer o metal, não usará os níveis de abstração e inúmeras verificações. Falhar no CAS é ruim em um loop esp. quando o hardware decide fazer uma previsão incorreta da ramificação (devido à alta contenção), mas ter mais algumas comparações / ramificações doerá. Parque / Desestacionar são expostos por meio LockSupportnão AQS (esta última é mais uma impl bloqueio do parque / desestacionar.)
bestsss

21

Unsafe.throwException - permite lançar exceções verificadas sem declará-las.

Isso é útil em alguns casos em que você lida com reflexão ou AOP.

Suponha que você Construa um proxy genérico para uma Interface definida pelo usuário. E o usuário pode especificar qual exceção é lançada pela implementação em um caso especial, apenas declarando a exceção na interface. Então esta é a única maneira que conheço, de gerar uma exceção verificada na Implementação dinâmica da interface.

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();
    }
}

14
você pode fazer o mesmo w / Thread.stop(Throwable)nenhuma necessidade para inseguro, no mesmo segmento que você pode jogar qualquer coisa de qualquer maneira (não há nenhuma verificação de compilação)
bestsss

Você pode fazer isso puramente através de bytecode (Ou use Lomboc para fazer isso por você)
Antimônio

1
@bestsss Esse método foi excluído e lança um UnsupportedOperationExceptionno thread atual a partir do Java 8. No entanto, a versão sem argumento que lança ThreadDeathainda funciona.
gparyani

@damryfbfnetsi, eu não acompanho as discussões principais do jdk há algum tempo e não tenho planos de mudar para o java 8. No entanto, essa é uma idéia bastante intrigante, já que é trivial ser implementado pela geração de bytecode de qualquer maneira, a menos que agora o verificador realmente verifique se eles O método declara os lançáveis ​​... mas isso pode ser incompatível com o anterior, pois os metadados sobre a exceção lançada estavam livres para serem descartados.
bestsss 5/06/2014

10

Classe insegura

Uma coleção de métodos para executar operações inseguras e de baixo nível. Embora a classe e todos os métodos sejam públicos, o uso dessa classe é limitado, porque apenas o código confiável pode obter instâncias dela.

Um uso é nas java.util.concurrent.atomicclasses:


6

Para uma cópia de memória eficiente (mais rápida de copiar que System.arraycopy () para blocos curtos, pelo menos); conforme usado pelos codecs Java LZF e Snappy . Eles usam 'getLong' e 'putLong', que são mais rápidos que fazer cópias byte a byte; especialmente eficiente ao copiar itens como blocos de 16/32/64 bytes.


1
Doh, usos arraycopy SSE laços em x86-64, que são melhores do que getLong/putLong(e você tem que calcular o endereço também)
bestsss

Você realmente mediu isso? Para blocos mais curtos, vejo desempenho consistentemente melhor no x86-64 ao usar a combinação de getLong/ putLong: idealmente, preferiria System.arraycopy()a simplicidade e tudo; mas os testes reais mostraram o contrário nos casos que testei.
StaxMan

sim usando inseguro eu não poderia qualquer desempenho significativo fora impl impl. Por vários bytes, cópias longas em matrizes grandes, get / putLong podem funcionar de fato quando o compilador precisa verificar os comprimentos. Alguns impl. adicione cerca de memória além de System.arrayCopy (embora possa ser desativado / ativado), para que esse possa ser o verdadeiro culpado.
bestsss 10/06

Está bem. É possível que JDKs mais recentes tenham alterado isso; originalmente, quando observei uma operação mais rápida (com o JDK 1.6), fiquei surpreso também. Ou talvez eu esteja esquecendo alguma diferença específica no uso. Essas são otimizações complicadas (e possivelmente instáveis), mesmo quando funcionam, e é essencial medir os efeitos.
StaxMan

5

Recentemente, eu estava trabalhando na reimplementação da JVM e descobri que um número surpreendente de classes é implementado em termos de Unsafe. A classe é projetada principalmente para os implementadores de bibliotecas Java e contém recursos que são fundamentalmente inseguros, mas necessários para a criação de primitivas rápidas. Por exemplo, existem métodos para obter e gravar deslocamentos brutos de campo, usando sincronização no nível de hardware, alocando e liberando memória, etc. Não se destina a ser usado por programadores Java normais; não é documentado, é específico da implementação e é inerentemente inseguro (daí o nome!). Além disso, acho que o SecurityManageracesso à Internet não será permitido em quase todos os casos.

Em resumo, ele existe principalmente para permitir que os implementadores de bibliotecas acessem a máquina subjacente sem precisar declarar todos os métodos em determinadas classes, como AtomicIntegernativo. Você não precisa usar ou se preocupar com isso na programação Java de rotina, pois o ponto principal é tornar o restante das bibliotecas rápido o suficiente para que você não precise desse tipo de acesso.


na verdade, o SecurityManager não permite acesso a ele apenas se reflexão é desativado
amara

@ sparkleshy- Você pode elaborar isso?
templatetypedef

ao obter uma instância de getUnsafe tem requisitos bastante rigorosos, Unsafe.class.getDeclaredField("theUnsafe")com .setAccessible(true)e, em seguida, .get(null)vai obtê-lo também
amara

@ sparkleshy- Estou surpreso que funcione - o gerente de segurança deve estar sinalizando isso.
templatetypedef

5

Use-o para acessar e alocar grandes quantidades de memória com eficiência, como em seu próprio mecanismo voxel! (ou seja, jogo no estilo Minecraft).

Na minha experiência, a JVM geralmente não consegue eliminar a verificação de limites no local em que você realmente precisa. Por exemplo, se você estiver iterando em uma matriz grande, mas o acesso à memória real estiver oculto sob uma chamada de método * não virtual * no loop, a JVM ainda poderá executar uma verificação de limites com cada acesso à matriz, em vez de uma vez antes o laço. Portanto, para obter ganhos de desempenho potencialmente grandes, é possível eliminar a verificação de limites da JVM dentro do loop por meio de um método que emprega sun.misc.Unsafe para acessar a memória diretamente, certificando-se de fazer a verificação de limites nos locais corretos. (Você está sentirei limites verificar em algum nível, certo?)
* por não virtual, quero dizer que a JVM não deveria resolver dinamicamente qualquer que seja o seu método específico, porque você garantiu corretamente que classe / método / instância são uma combinação de static / final / what-have-you.

Para o meu mecanismo de voxel desenvolvido em casa, isso resultou em um ganho de desempenho dramático durante a geração e a serialização de blocos (em locais onde eu estava lendo / escrevendo para toda a matriz de uma só vez). Os resultados podem variar, mas se o problema for a falta de eliminação de limites, será corrigido.

Existem alguns problemas potencialmente importantes com isso: especificamente, quando você fornece a capacidade de acessar a memória sem verificação de limites para os clientes da sua interface, eles provavelmente a abusarão. (Não se esqueça que os hackers também podem ser clientes da sua interface ... especialmente no caso de um mecanismo voxel escrito em Java.) Portanto, você deve projetar sua interface de maneira a não prejudicar o acesso à memória ou você deve ser extremamente cuidadoso ao validar os dados do usuário antes que eles possam se misturar à sua interface perigosa. Considerando as coisas catastróficas que um hacker pode fazer com acesso não verificado à memória, provavelmente é melhor adotar as duas abordagens.


4

Coleções fora da pilha podem ser úteis para alocar grandes quantidades de memória e desalocá-la imediatamente após o uso, sem interferência do GC. Eu escrevi uma biblioteca para trabalhar com matrizes / listas fora da pilha com base em sun.misc.Unsafe.


4

Implementamos coleções enormes, como Arrays, HashMaps, TreeMaps usando Unsafe.
E para evitar / minimizar a fragmentação, implementamos o alocador de memória usando os conceitos de dlmalloc sobre inseguro.
Isso nos ajudou a obter o desempenho em simultâneo.


3

Unsafe.park()e Unsafe.unpark()para a construção de estruturas personalizadas de controle de concorrência e mecanismos de agendamento cooperativo.


24
publicamente disponível comojava.util.concurrent.locks.LockSupport
bestsss 28/10/11

1

Eu ainda não o usei, mas suponho que se você tiver uma variável que, ocasionalmente, é lida por mais de um segmento (para que você não queira torná-lo volátil), você pode usá- putObjectVolatilelo ao escrevê-lo no segmento principal e readObjectVolatileao fazer leituras raras de outros tópicos.


1
mas de acordo com a discussão sobre o fio abaixo, voto contra voláteis são quase tão rápido quanto os não-voláteis de qualquer maneira stackoverflow.com/questions/5573782/...
pdeva

você não pode substituir a semântica volátil por gravações simples e leituras voláteis ... essa é uma receita para o desastre, pois pode funcionar em uma configuração, mas não em outra. Se você deseja ter semântica volátil com um único segmento de gravador, pode usar AtomicReference.lazySet no segmento de escrita e obter () nos leitores (consulte esta postagem para uma discussão sobre o tópico). As leituras voláteis são relativamente baratas, mas não gratuitas, veja aqui .
Nitsan Wakart

"... você pode usar o putObjectVolatile ao escrevê-lo ..." Eu não estava sugerindo gravações simples.
precisa saber é o seguinte

1

É necessário se você precisar substituir a funcionalidade fornecida por uma das classes que a utiliza atualmente.

Isso pode ser serialização / desserialização mais compacta / rápida / mais compacta, uma versão mais rápida / maior de buffer / redimensionável do ByteBuffer ou adicionar uma variável atômica, por exemplo, uma variável não suportada atualmente.

Eu tenho usado para todos estes em algum momento.



0

O objeto parece estar disponível para funcionar em um nível inferior ao que o código Java normalmente permite. Se você estiver codificando um aplicativo de alto nível, a JVM abstrai o manuseio da memória e outras operações para longe do nível do código, para que seja mais fácil programar. Ao usar a biblioteca Insegura, você está efetivamente concluindo operações de baixo nível que normalmente seriam feitas para você.

Como woliveirajr afirmou, "random ()" usa Unsafe para propagar, assim como muitas outras operações usarão a função assignateMemory () incluída em Unsafe.

Como programador, você provavelmente poderia se safar de nunca precisar dessa biblioteca, mas ter um controle estrito sobre os elementos de baixo nível é útil (é por isso que ainda existe o código Assembly e (em menor grau) C de derivação nos principais produtos)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.