FULL PRODUCT VERSION :
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
Oracle JRockit(R) (build R28.0.1-21-133393-1.6.0_20-20100512-2131-windows-x86_64, compiled mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2003 Server 64-bit
Microsoft Windows [Version 5.2.3790]
EXTRA RELEVANT SYSTEM CONFIGURATION :
Please replicate using JRockit's compiler and JVM (javac and java distributed by JRockit). I am also using 64-bit JVM.
A DESCRIPTION OF THE PROBLEM :
This seems to be a problem for the JRockit JVM only. About 2-3 years ago, JRockit introduced an optimization strategy for System.arraycopy() (method to copy elements from a source array to a destination array). If a certain list of conditions are met, then instead of copying over the elements, it will instead just point the destination array to the source array (making both of them point to the same array object in memory). Here is a link to a PDF document from BEA Systems (prior owners of JRockit) that describes this (it's actually a request to the U.S. Patent office that describes this). Please take the time to read through it:
http://www.freepatentsonline.com/20080148246.pdf
There is a whole list of conditions that needs to be met for this optimization to happen in the JVM (see PDF link above), among them if the source and destination arrays are the same length, and if the whole array is being copied. The problem is, that there are times where a programmer can really get messed up, since in the Java contract, it does not state that the destination array and source array can end up being the same object, just that the elements get copied over. Here is why. Say there is this piece of code:
String[] arr1 = new String[1000000];
String[] arr2 = new String[1000000];
for( int i = 0; i < 1000000; i++ )
arr1[i] = Integer.toString(1000000-i);
System.arraycopy(arr1, 0, arr2, 0, arr1.length);
Arrays.sort(arr2);
What the programmer intends is that that the destination array "arr2" should get sorted, but the source array "arr1" should be left alone. What will actually happen, is that the source array "arr1" will get sorted too, since the destination array was sorted, and the destination array was made to point to the source array! We really got messed up this way, because we assumed that the source array would not be sorted, and had logic that relied on this. It took us a week of debugging (and angry users) until we were able to figure out what was going wrong. What's more, it doesn't seem to happen every time, so it's not easily caught during development.
I would like to suggest adding another condition that needs to be met, before the JVM can point the destination array to the source array. The condition would be, that if the source array is still being referenced in the code after the System.arraycopy(), then the destination array cannot be modified in any way. If it does, then we cannot point the destination array to the source array, since the modifications done to the destination array will also be reflected in the source array (and it is still being referenced after the System.arraycopy()).
I would also think that the comments for System.arraycopy() should be modified, to explicitly state that the destination array may actually point to the source array if certain conditions are met. As it is, it seems like a violation of the Java contract, which only states that the elements are being copied.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run source code I provided, using JRockit's javac.exe and java.exe. The program goes around in a loop (100 times) until it can re-create the problem. It does this:
- Creates an array, and populates it with String objects in descending order.
- Calls System.arraycopy() to copy it to another array.
- Sorts the destination array so it should be in ascending order.
At each iteration of the loop, it will print out whether the destination array now points to the source array, and will print out the first element of each. When the arrays do not point to each other, you get this:
Try #1
not the same object
arr1[0]: 1000000 arr2[0]: 1
Here you can see that it's not the same object, and that the first element of the arrays do not match.
However, when the arrays do point to each other, you get this:
Try #5
same object!
arr1[0]: 1 arr2[0]: 1
Now you can see that the objects are the same, and the first elements of the arrays match, because sorting the destination array also sorted the source array. When it hits this case, the program exits. On my machine, it takes around 5 loops for it to happen.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
not the same object
arr1[0]: 1000000 arr2[0]: 1
ACTUAL -
same object!
arr1[0]: 1 arr2[0]: 1
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.util.*;
public class TestArrayCopy {
public static void main(String [] args) {
for( int i = 0; i < 100; i++ ) {
ArrayCopy.arraycopy(i+1);
try {
Thread.sleep(500);
}
catch(Exception e) {}
}
}
}
class ArrayCopy {
public static void arraycopy(int tryNum) {
System.out.println("\nTry #" + tryNum);
String[] arr1 = new String[1000000];
String[] arr2 = new String[1000000];
for( int i = 0; i < arr1.length; i++ )
arr1[i] = Integer.toString(1000000-i);
System.arraycopy(arr1, 0, arr2, 0, arr1.length);
Arrays.sort(arr2);
if( arr2 == arr1 ) {
System.out.println("same object!");
System.out.println("arr1[0]: " + arr1[0] + " arr2[0]: " + arr2[0]);
System.exit(0);
}
else {
System.out.println("not the same object");
System.out.println("arr1[0]: " + arr1[0] + " arr2[0]: " + arr2[0]);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Do not use System.arraycopy()!
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
Oracle JRockit(R) (build R28.0.1-21-133393-1.6.0_20-20100512-2131-windows-x86_64, compiled mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2003 Server 64-bit
Microsoft Windows [Version 5.2.3790]
EXTRA RELEVANT SYSTEM CONFIGURATION :
Please replicate using JRockit's compiler and JVM (javac and java distributed by JRockit). I am also using 64-bit JVM.
A DESCRIPTION OF THE PROBLEM :
This seems to be a problem for the JRockit JVM only. About 2-3 years ago, JRockit introduced an optimization strategy for System.arraycopy() (method to copy elements from a source array to a destination array). If a certain list of conditions are met, then instead of copying over the elements, it will instead just point the destination array to the source array (making both of them point to the same array object in memory). Here is a link to a PDF document from BEA Systems (prior owners of JRockit) that describes this (it's actually a request to the U.S. Patent office that describes this). Please take the time to read through it:
http://www.freepatentsonline.com/20080148246.pdf
There is a whole list of conditions that needs to be met for this optimization to happen in the JVM (see PDF link above), among them if the source and destination arrays are the same length, and if the whole array is being copied. The problem is, that there are times where a programmer can really get messed up, since in the Java contract, it does not state that the destination array and source array can end up being the same object, just that the elements get copied over. Here is why. Say there is this piece of code:
String[] arr1 = new String[1000000];
String[] arr2 = new String[1000000];
for( int i = 0; i < 1000000; i++ )
arr1[i] = Integer.toString(1000000-i);
System.arraycopy(arr1, 0, arr2, 0, arr1.length);
Arrays.sort(arr2);
What the programmer intends is that that the destination array "arr2" should get sorted, but the source array "arr1" should be left alone. What will actually happen, is that the source array "arr1" will get sorted too, since the destination array was sorted, and the destination array was made to point to the source array! We really got messed up this way, because we assumed that the source array would not be sorted, and had logic that relied on this. It took us a week of debugging (and angry users) until we were able to figure out what was going wrong. What's more, it doesn't seem to happen every time, so it's not easily caught during development.
I would like to suggest adding another condition that needs to be met, before the JVM can point the destination array to the source array. The condition would be, that if the source array is still being referenced in the code after the System.arraycopy(), then the destination array cannot be modified in any way. If it does, then we cannot point the destination array to the source array, since the modifications done to the destination array will also be reflected in the source array (and it is still being referenced after the System.arraycopy()).
I would also think that the comments for System.arraycopy() should be modified, to explicitly state that the destination array may actually point to the source array if certain conditions are met. As it is, it seems like a violation of the Java contract, which only states that the elements are being copied.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run source code I provided, using JRockit's javac.exe and java.exe. The program goes around in a loop (100 times) until it can re-create the problem. It does this:
- Creates an array, and populates it with String objects in descending order.
- Calls System.arraycopy() to copy it to another array.
- Sorts the destination array so it should be in ascending order.
At each iteration of the loop, it will print out whether the destination array now points to the source array, and will print out the first element of each. When the arrays do not point to each other, you get this:
Try #1
not the same object
arr1[0]: 1000000 arr2[0]: 1
Here you can see that it's not the same object, and that the first element of the arrays do not match.
However, when the arrays do point to each other, you get this:
Try #5
same object!
arr1[0]: 1 arr2[0]: 1
Now you can see that the objects are the same, and the first elements of the arrays match, because sorting the destination array also sorted the source array. When it hits this case, the program exits. On my machine, it takes around 5 loops for it to happen.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
not the same object
arr1[0]: 1000000 arr2[0]: 1
ACTUAL -
same object!
arr1[0]: 1 arr2[0]: 1
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.util.*;
public class TestArrayCopy {
public static void main(String [] args) {
for( int i = 0; i < 100; i++ ) {
ArrayCopy.arraycopy(i+1);
try {
Thread.sleep(500);
}
catch(Exception e) {}
}
}
}
class ArrayCopy {
public static void arraycopy(int tryNum) {
System.out.println("\nTry #" + tryNum);
String[] arr1 = new String[1000000];
String[] arr2 = new String[1000000];
for( int i = 0; i < arr1.length; i++ )
arr1[i] = Integer.toString(1000000-i);
System.arraycopy(arr1, 0, arr2, 0, arr1.length);
Arrays.sort(arr2);
if( arr2 == arr1 ) {
System.out.println("same object!");
System.out.println("arr1[0]: " + arr1[0] + " arr2[0]: " + arr2[0]);
System.exit(0);
}
else {
System.out.println("not the same object");
System.out.println("arr1[0]: " + arr1[0] + " arr2[0]: " + arr2[0]);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Do not use System.arraycopy()!