1. O limite superior de alocação de matrizes
O tamanho de uma matriz em Java é limitado porque usa o tipo int como o subscrito da matriz. Isso significa que você não pode solicitar matrizes que excedam o tamanho do número inteiro.max_value (2^31-1). Isso não significa que o limite superior do seu aplicativo de memória seja 2G. Você pode solicitar uma variedade de tipos maiores. por exemplo:
A cópia do código é a seguinte:
final longo [] ar = novo longo [inteiro.max_value];
Isso alocará 16g -8 bytes. é apenas regras gerais, a alocação específica depende da situação real).
Infelizmente, em Java, devido à limitação do tipo de elementos da matriz, será mais problemático operar a memória. Quando se trata de matrizes de operação, o ByTeBuffer deve ser a classe mais útil, que fornece métodos para ler e escrever diferentes tipos Java. Sua desvantagem é que o tipo de matriz de destino deve ser byte [], o que significa que o cache máximo de memória que você aloca pode ser 2G.
2. Use todas as matrizes como matrizes de bytes para operar
Supondo que a memória 2G esteja longe de ser suficiente para nós agora, tudo bem se for 16g. Atribuímos um longo [], mas queremos operá -lo como uma matriz de bytes. Em Java, temos que pedir ajuda aos programadores C - Sun.misc.unsfe. Esta classe possui dois conjuntos de métodos: GetN (objeto, deslocamento), esse método é para obter um valor do tipo especificado da posição em que o objeto é deslocado e retornará e retorne O método putn (objeto, deslocamento, valor) é escrever um valor na posição do deslocamento do objeto.
Infelizmente, esses métodos podem apenas obter ou definir valores de um determinado tipo. Se você copiar dados de uma matriz, também precisará de outro método de copimemoria insegura (srcobject, srcoffset, destoBject, destoffet, contagem). Isso funciona de maneira semelhante ao System.arrayCopy, mas copia bytes em vez de elementos de matriz.
Para acessar os dados de uma matriz através do Sun.misc.unsfe, você precisa de duas coisas:
1. O deslocamento dos dados no objeto da matriz
2. O deslocamento dos elementos copiados nos dados da matriz
Como outros objetos Java, o Arrays possui um cabeçalho de objeto, que é armazenado na frente dos dados reais. O comprimento deste cabeçalho pode ser obtido pelo método inseguro.arraybaseoffset (t []. Classe), onde t é o tipo de elemento da matriz. O tamanho do elemento da matriz pode ser obtido através do método inseguro.arrayIndexScale (t []. Classe). Isso significa que, se você deseja acessar o enésimo elemento do Tipo T, o deslocamento do deslocamento deve ser ArrayOffSet+N*ArrayScale.
Vamos escrever um exemplo simples. Alocamos uma longa matriz e atualizamos alguns bytes dentro dela. Atualizamos o último elemento para -1 (0xfffffffffffffffffffffffffffffff) e, em seguida, limpamos todos os bytes deste elemento um por um.
A cópia do código é a seguinte:
final longo [] AR = novo longo [1000];
final int index = ar.length - 1;
AR [índice] = -1;
System.out.println ("antes de alteração =" + long.toHexString (AR [index]));
para (longo i = 0; i <8; ++ i)
{
insefe.putbyte (AR, LONGARRAYOFFSET + 8L * INDEX + I, (BYTE) 0);
System.out.println ("Após a alteração: i =" + i + ", val =" + long.tohexstring (AR [index]));
}
Se você deseja executar o exemplo acima, você deve adicionar o seguinte bloco de código estático à sua classe de teste:
A cópia do código é a seguinte:
Final estático privado inseguro inseguro;
estático
{
tentar
{
Campo de campo = insefa.class.getDecLaredField ("theUsafe");
field.setAccessible (true);
inseguro = (inseguro) field.get (nulo);
}
Catch (Exceção e)
{
lançar nova execução de tempo de execução (e);
}
}
Private Static final long longarrayoffset = insefe.arraybaseoffset (Long []. Classe);
O resultado da saída é:
A cópia do código é a seguinte:
Antes da mudança = fffffffffffffffff
Após a mudança: i = 0, val = fffffffffffff00
Após a mudança: i = 1, val = fffffffffff0000
Após a mudança: i = 2, val = ffffffffff000000
Após a mudança: i = 3, val = fffffffff00000000
Após a alteração: i = 4, val = ffffff0000000000000
Após a mudança: i = 5, Val = FFFF0000000000000
Após a mudança: i = 6, Val = FF000000000000000
Após a mudança: i = 7, val = 0
3. Alocação de memória de sol.misc.unsfe
Como mencionado acima, em Java Pure, o tamanho da memória que podemos alocar é limitado. Essa restrição foi definida na versão inicial do Java e, naquela época, as pessoas não ousavam compartilhar vários g de memória como essa. Mas agora é a era do big data e precisamos de mais memória. Em Java, há duas maneiras de obter mais memória:
1. Aloce muitos pequenos pedaços de memória e, em seguida, use -os logicamente como um grande pedaço contínuo de memória.
2. Use sun.misc.unsfe.allcatememory (longo) para alocar memória.
O primeiro método é apenas um pouco mais interessante da perspectiva dos algoritmos, então vamos dar uma olhada no segundo método.
Sun.misc.unsfe fornece um conjunto de métodos para alocar, alocar novamente e liberar memória. Eles são muito parecidos com o método MaiC/Free de C:
1. Long inseguro.alocatememory (tamanho longo) -Aloca um pedaço de espaço de memória. Este pedaço de memória pode conter dados de junk (não limpos automaticamente). Se a alocação falhar, uma exceção de java.lang.outOfMemoryError será lançada. Ele retorna um endereço de memória diferente de zero (consulte a descrição abaixo).
2.Unsafe.RealLocatememory (endereço longo, tamanho longo) -RealLocate uma peça de memória e copie dados do buffer de memória antigo (onde o endereço aponta) o bloco de memória recém-alocado. Se o endereço for igual a 0, esse método terá o mesmo efeito que a alocatememoria. Ele retorna o endereço do novo buffer de memória.
3.Unsafe.Freememory (Endereço longo)-Afreto um buffer de memória gerado pelos dois métodos anteriores. Se o endereço for 0, não faça nada.
A memória alocada por esses métodos deve ser usada em um modo chamado Endereço de registro único: o UNSAFE fornece um conjunto de métodos que aceitam apenas um parâmetro de endereço (diferentemente do modo de registro duplo, eles exigem um objeto e um deslocamento de deslocamento). A memória alocada dessa maneira pode ser maior do que a que você configura nos parâmetros Java de -xmx.
Nota: A memória alocada por inseguro não pode ser coletada de lixo. Você precisa tratá -lo como um recurso normal e gerenciá -lo sozinho.
Aqui está um exemplo de uso do inseguro.Alocatememory para alocar memória e também verifica se todo o buffer de memória é legível e escrito:
A cópia do código é a seguinte:
final int size = Integer.max_value / 2;
Final Long Addr = Unsafe.Allocatememory (tamanho);
tentar
{
System.out.println ("endereço inseguro =" + addr);
para (int i = 0; i <tamanho; ++ i)
{
insegu.putbyte (addr + i, (byte) 123);
if (insefa.getbyte (addr + i)! = 123)
System.out.println ("falhou em offset =" + i);
}
}
Finalmente
{
inseguro.freememory (addr);
}
Como você pode ver, usando Sun.misc.unsfe, você pode escrever um código de acesso à memória muito geral: Não importa que tipo de memória seja alocada em Java, você pode ler e escrever qualquer tipo de dados à vontade.