FULL PRODUCT VERSION :
java version "1.9.0-ea"
Java(TM) SE Runtime Environment (build 1.9.0-ea-b93)
Java HotSpot(TM) 64-Bit Server VM (build 1.9.0-ea-b93, mixed mode)
A DESCRIPTION OF THE PROBLEM :
The problem: Java source code suggests that bytes for UTF16 characters are stored in java.lang.String's field "value" as big endian:
// from String.java:
public char charAt(int index) {
if (isLatin1()) {
return StringLatin1.charAt(value, index);
} else {
return StringUTF16.charAt(value, index);
}
}
// from StringUTF16.java:
@HotSpotIntrinsicCandidate
public static char getChar(byte[] val, int index) {
index <<= 1;
return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
((val[index] & 0xff) << LO_BYTE_SHIFT));
}
... however, in fact, JVM applies intrinsic that uses target platfrom's natural endianness. For example, it is little endian on Windows which is x86/x64, but big endian on Solaris SPARC.
I suppose this is done for performance reasons, and it's OK. However, if this is the case, the source code should explicitly reflect this fact.
Also, tools opening HPROF dumps or debuggers that present Strings should be provided with explicit means to find out which endianness is used in particular JVM.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Running the following TestCompactStrings on a little endian machine (e.g. Windows) prints "little endian" and on a big endian machine (e.g. Solaris SPARC) prints "big endian".
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
According to the current Java source code, it should print "big endian" always.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.lang.reflect.Field;
class TestCompactStrings {
public static void main(String[] args) throws Exception {
String s = "\u1234";
Class<? extends String> aClass = s.getClass();
Field valueField = aClass.getDeclaredField("value");
valueField.setAccessible(true);
byte[] bytes = (byte[])valueField.get(s);
if (bytes[0] == 0x12 && bytes[1] == 0x34) {
System.out.println("big endian");
}
else if (bytes[0] == 0x34 && bytes[1] == 0x12) {
System.out.println("little endian");
}
else {
System.out.println("unexpected");
}
}
}
---------- END SOURCE ----------