Java – is there any way to create a basic array without initialization?
As we know, Java always initializes arrays at creation time That is, new int [1000000] always returns an array with all elements = 0 I understand that this is necessary for an object array, but for the original array (except maybe Boolean), in most cases, we don't care about the initial value
Does anyone know how to avoid this initialization?
Solution
I did some research There is no legal method to create uninitialized arrays in Java Even JNI newxarray creates an initialized array Therefore, it is impossible to accurately know the cost of zeroing the array But I made some measurements:
1) Creation of 1000 byte arrays with different array sizes
long t0 = System.currentTimeMillis(); for(int i = 0; i < 1000; i++) { // byte[] a1 = new byte[1]; byte[] a1 = new byte[1000000]; } System.out.println(System.currentTimeMillis() - t0);
On my PC, byte [1] is 1ms and byte [1000000] is ~ 500 ms. it sounds impressive to me
2) We don't have a fast (native) method to fill arrays in JDK, arrays Fill is too slow, so let's look at 1000 copies of an array of at least 1000000 sizes with the native system arraycopy
byte[] a1 = new byte[1000000]; byte[] a2 = new byte[1000000]; for(int i = 0; i < 1000; i++) { System.arraycopy(a1,a2,1000000); }
It's 700 milliseconds
It gives me reason to believe that a) creating long arrays is expensive b) it seems expensive because of useless initialization
3) Let's sun misc. Unsafe http://www.javasourcecode.org/html/open-source/jdk/jdk-6u23/sun/misc/Unsafe.html. It is protected from external use, but not too much
Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe unsafe = (Unsafe)f.get(null);
This is the cost of memory allocation testing
for(int i = 0; i < 1000; i++) { long m = u.allocateMemory(1000000); }
It needs < 1 ms, if you remember, 500 ms for new bytes [1000000] 4) Unsafe has no direct way to handle arrays It needs to know the class field, but the reflection does not display the field in the array There is not much information about the internal structure of the array. I think this is the specific of the JVM / platform However, like any other Java object, the header field is the same Looks like on my PC / JVM
header - 8 bytes int length - 4 bytes long bufferAddress - 8 bytes
Now, using unsafe, I will create byte [10], allocate a 10 byte memory buffer, and use it as my array element:
byte[] a = new byte[10]; System.out.println(Arrays.toString(a)); long mem = unsafe.allocateMemory(10); unsafe.putLong(a,12,mem); System.out.println(Arrays.toString(a));
It prints
[0,0] [8,15,-114,24,0]
You can see that the data of the thay array is uninitialized
Now I'll change the length of our array (although it still points to 10 bytes of memory)
unsafe.putInt(a,8,1000000); System.out.println(a.length);
It shows 1000000 This is just to prove that the idea is effective
Now performance test I will create an empty byte array A1, allocate a 1000000 byte buffer, and allocate this buffer to a set of A1 length = 10000000
long t0 = System.currentTimeMillis(); for(int i = 0; i < 1000; i++) { byte[] a1 = new byte[0]; long mem1 = unsafe.allocateMemory(1000000); unsafe.putLong(a1,mem); unsafe.putInt(a1,1000000); } System.out.println(System.currentTimeMillis() - t0);
10ms is required
5) In C, there are malloc and alloc. Malloc only allocates memory blocks, and calloc initializes it with zero
CPP
... JNIEXPORT void JNICALL Java_Test_malloc(jnienv *env,jobject obj,jint n) { malloc(n); }
Java
private native static void malloc(int n); for (int i = 0; i < 500; i++) { malloc(1000000); }
Results malloc – 78 MS; calloc – 468 ms
conclusion
>It seems that Java array creation is slow because useless elements are zeroed. > We can't change it, but Oracle can There is no need to change anything in the JLS, just add the local method to Java lang.reflect. Array
public static native xxx [] newUninitialziedXxxArray(int size);
It is applicable to all original byte double and char types It can be used in JDK, just like in Java util. Same as in arrays
public static int[] copyOf(int[] original,int newLength) { int[] copy = Array.newUninitializedIntArray(newLength); System.arraycopy(original,copy,Math.min(original.length,newLength)); ...
Or Java lang.String
public String concat(String str) { ... char[] buf = Array.newUninitializedCharArray(count + otherLen); getChars(0,count,buf,0); ...