1. El límite superior de la asignación de matriz
El tamaño de una matriz en Java es limitado porque utiliza el tipo INT como subíndice de matriz. Esto significa que no puede solicitar matrices que excedan el tamaño de entero.max_value (2^31-1). Esto no significa que el límite superior de su aplicación de memoria sea 2G. Puede solicitar una variedad de tipos más grandes. Por ejemplo:
La copia del código es la siguiente:
final long [] ar = new Long [Integer.max_value];
Esto asignará 16G -8 bytes. es solo reglas generales, la asignación específica depende de la situación real).
Desafortunadamente, en Java, debido a la limitación de tipo de elementos de matriz, será más problemático operar la memoria. Cuando se trata de matrices operativas, ByteBuffer debería ser la clase más útil, que proporciona métodos para leer y escribir diferentes tipos de Java. Su desventaja es que el tipo de matriz de destino debe ser byte [], lo que significa que el caché de memoria máximo que asigna puede ser 2G.
2. Use todas las matrices como matrices de bytes para operar
Suponiendo que la memoria 2G está lejos de ser suficiente para nosotros ahora, está bien si es 16 g. Hemos asignado un largo [], pero queremos operarlo como una matriz de bytes. En Java tenemos que pedir ayuda de los programadores C - Sun.misc.unsafe. Esta clase tiene dos conjuntos de métodos: getN (objeto, desplazamiento), este método es obtener un valor del tipo especificado de la posición donde el compensación de objeto se compensa y devolverlo aquí representa el tipo para devolver el valor. El método PUTN (objeto, compensación, valor) es escribir un valor en la posición de la compensación del objeto.
Desafortunadamente, estos métodos solo pueden obtener o establecer valores de cierto tipo. Si copia datos de una matriz, también necesita otro método de inseguro, copyMemory (srcObject, srcoffset, desestobject, destofet, count). Esto funciona de manera similar a System.ArrayCopy, pero copia bytes en lugar de elementos de matriz.
Para acceder a los datos de una matriz a través de Sun.Misc.unsafe, necesita dos cosas:
1. La compensación de datos en el objeto de matriz
2. El desplazamiento de los elementos copiados en los datos de la matriz
Al igual que otros objetos Java, Arrays tiene un encabezado de objetos, que se almacena frente a los datos reales. La longitud de este encabezado se puede obtener mediante el método inseguro. El tamaño del elemento de matriz se puede obtener a través del método inseguro.arrayindexscale (t []. Class). Esto significa que si desea acceder al enésimo elemento del Tipo T, su compensación de compensación debe ser ArrayOffset+N*ArrayScale.
Vamos a escribir un ejemplo simple. Asignamos una matriz larga y actualizamos algunos bytes dentro de ella. Actualizamos el último elemento a -1 (0xfffffffffffffffffffffffff), y luego eliminamos todos los bytes de este elemento uno por uno.
La copia del código es la siguiente:
final largo [] ar = nuevo largo [1000];
Final int index = ar.length - 1;
ar [índice] = -1;
System.out.println ("antes de cambiar =" + long.tohexString (ar [index]));
para (largo i = 0; i <8; ++ i)
{
unsafe.putbyte (AR, LongarRayoffset + 8L * índice + i, (byte) 0);
System.out.println ("después del cambio: i =" + i + ", val =" + long.tohexString (ar [index]));
}
Si desea ejecutar el ejemplo anterior, debe agregar el siguiente bloque de código estático a su clase de prueba:
La copia del código es la siguiente:
Final estático privado inseguro inseguro;
estático
{
intentar
{
Campo campo = unsafe.class.getDeclaredfield ("TheUnsafe");
campo.setAccessible (verdadero);
unsafe = (inseguro) campo.get (nulo);
}
captura (excepción e)
{
tirar nueva runtimeException (e);
}
}
Private estático final longArrayOffset = unsafe.ArrayBaseOffset (long []. class);
El resultado de la salida es:
La copia del código es la siguiente:
Antes del cambio = ffffffffffffffffff
Después del cambio: i = 0, val = fffffffffffff00
Después del cambio: i = 1, val = fffffffffff0000
Después del cambio: i = 2, val = ffffffffff000000
Después del cambio: i = 3, val = ffffffff00000000
Después del cambio: i = 4, val = ffffff0000000000000
Después del cambio: i = 5, val = FFFF0000000000000
Después del cambio: i = 6, val = ff00000000000000000
Después del cambio: i = 7, val = 0
3. Asignación de memoria de Sun.misc.unsafe
Como se mencionó anteriormente, en Java puro, el tamaño de la memoria que podemos asignar es limitado. Esta restricción se estableció en la versión inicial de Java, y en ese momento la gente no se atrevió a compartir varias G de memoria como esta. Pero ahora es la era de Big Data, y necesitamos más memoria. En Java, hay dos formas de obtener más memoria:
1. Asigne muchos pequeños trozos de memoria y luego úselos lógicamente como una gran parte de la memoria continua.
2. Use sun.misc.unsafe.allcatememory (largo) para asignar memoria.
El primer método es un poco más interesante desde la perspectiva de los algoritmos, así que echemos un vistazo al segundo método.
Sun.misc.unsafe proporciona un conjunto de métodos para asignar, reasignar y liberar la memoria. Son muy similares al método Malloc/Free de C:
1. Long Insafe. Allocatememory (tamaño largo) -Loce un espacio de memoria. Esta memoria puede contener datos basura (no se borra automáticamente). Si la asignación falla, se lanzará una excepción de java.lang.outOfMemoryError. Devuelve una dirección de memoria distinta de cero (consulte la descripción a continuación).
2.Unsafe.ReallCatememory (dirección larga, tamaño largo) -RealLocate un trozo de memoria y copia datos del búfer de memoria anterior (donde la dirección apunta a) el bloque de memoria recién asignado. Si la dirección es igual a 0, este método tiene el mismo efecto que la asignememoria. Devuelve la dirección del nuevo búfer de memoria.
3.Unsafe.Freememoria (dirección larga) -Free un búfer de memoria generado por los dos métodos anteriores. Si la dirección es 0, no haga nada.
La memoria asignada por estos métodos debe usarse en un modo llamado dirección de registro único: Unsafe proporciona un conjunto de métodos que aceptan solo un parámetro de dirección (a diferencia del modo de registro dual, requieren un objeto y un desplazamiento de desplazamiento). La memoria asignada de esta manera puede ser más grande de lo que configura en los parámetros Java de -xmx.
Nota: La memoria asignada por insegura no puede ser recolectada de basura. Debe tratarlo como un recurso normal y administrarlo usted mismo.
Aquí hay un ejemplo de usar inseguro. AllocateMemory para asignar memoria, y también verifica si todo el búfer de memoria es legible y escrito:
La copia del código es la siguiente:
final int size = integer.max_value / 2;
ADDR LARGO FINAL = UNSAFE.AlLOCATEMEMEMORY (tamaño);
intentar
{
System.out.println ("una dirección unrafe =" + addr);
para (int i = 0; i <size; ++ i)
{
unsafe.putbyte (addr + i, (byte) 123);
if (unsafe.getbyte (addr + i)! = 123)
System.out.println ("falló en offset =" + i);
}
}
Finalmente
{
Unsafe.Freememory (ADDR);
}
Como puede ver, con Sun.Misc.Unsafe puede escribir un código de acceso de memoria muy general: no importa qué tipo de memoria se asigne en Java, puede leer y escribir cualquier tipo de datos a voluntad.