In short, the previous way of keeping the same underlying character array and just updating the {offset, count} indexes has a drawback in that if the original string is large, it is prevented from being GC'd if one keeps a reference to even a single substring generated from it.
So, it's a trade-off between the original and new behaviour; the original way more or less caps the memory usage at the size of the original string, but at the expense of not being able to GC it if even a single substring exists, while the new way increases memory usage for each substring generated but does not prevent any of the strings from being GC'd.
This is why the article's code example yields such a huge difference in memory usage in Java 6 vs Java 7; it is effectively a sort of "anti-pattern" when used against the new `substring()` method. (i.e. iterating through a large string and generating lots of sub-strings)
The article I linked to, which came out in late 2012, basically had the same advice:
"If you are writing parsers and such, you can not rely any more on the implicit caching provided by String. You will need to implement a similar mechanism based on buffering and a custom implementation of CharSequence"
0. http://www.javaadvent.com/2012/12/changes-to-stringsubstring...