Right, indeed this change was made in 7u6. There is no API change for this, as this change is strictly an implementation change, not an API change, nor is there an API to detect which behavior the running JDK has. However, it is certainly possible for applications to notice a difference in performance or memory utilization because of the change. In fact, it's not difficult to write a program that works in 7u4 but fails in 7u6 and vice-versa. We expect that the tradeoff is favorable for the majority of applications, but undoubtedly there are applications that will suffer from this change.
It's interesting that you're concerned about the case where string values are shared (prior to 7u6). Most people I've heard from have the opposite concern, where they like the sharing and the 7u6 change to unshared values is causing them problems (or, they're afraid it will cause problems).
In any case the thing to do is measure, not guess!
First, compare the performance of your application between similar JDKs with and without the change, e.g. 7u4 and 7u6. Probably you should be looking at GC logs or other memory monitoring tools. If the difference is acceptable, you're done!
Assuming that the shared string values prior to 7u6 cause a problem, the next step is to try the simple workaround of new String(s.substring(...))
to force the string value to be unshared. Then measure that. Again, if the performance is acceptable on both JDKs, you're done!
If it turns out that in the unshared case, the extra call to new String()
is unacceptable, then probably the best way to detect this case and make the "unsharing" call conditional is to reflect on a String's value
field, which is a char[]
, and get its length:
int getValueLength(String s) throws Exception {
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
return ((char[])field.get(s)).length;
}
Consider a string resulting from a call to substring()
that returns a string shorter than the original. In the shared case, the substring's length()
will differ from the length of the value
array retrieved as shown above. In the unshared case, they'll be the same. For example:
String s = "abcdefghij".substring(2, 5);
int logicalLength = s.length();
int valueLength = getValueLength(s);
System.out.printf("%d %d ", logicalLength, valueLength);
if (logicalLength != valueLength) {
System.out.println("shared");
else
System.out.println("unshared");
On JDKs older than 7u6, the value's length will be 10, whereas on 7u6 or later, the value's length will be 3. In both cases, of course, the logical length will be 3.