Short version: If you call string.substring(n,m).intern(), does the string table retain the substring or the original string?
…But I’m not sure that’s the right question to ask, so here’s the long version:
I’m working with legacy Java code (PCGen) that parses files by slurping each in as one big string and then using String.split, .trim, .substring, and StringTokenizer to decompose them into tokens. This is very efficient for parsing, because none of those methods copy the original string, but all point at parts of a shared char[].
After parsing is over, I want to reclaim some memory. Only a few small substrings of the original big string are needed, but the strong reference keeps the big string from being collected. And later I’m suffering OOM, I believe due in part to that huge heap impact of lots of parsed files.
I know I can trim the big string down via new String(String)(copy-on-write). And I know I can reduce string duplication via String.intern (which is important because there’s a lot of redundancy in the parsed files). Do I need to use both to reclaim the greatest quantity of heap, or does .intern() do both? Reading the OpenJDK7 hotspot source code (hotspot/src/share/vm/classfile/symbolTable.cpp) it looks like the string table keeps the whole string and does not trim it for offset/length at all. So I think I need to make a new String and then intern that result. Right?
All that said, switching to a streaming parser would be a big win in terms of memory, but that’s too big a change for the short term.
You can use new String(String) and the intern() method and this will take a copy as required for up to Java 7 update 4. From Java 7 update 5 substring will take a deeper copy, but you may still want to use intern(). Note: Java 7 uses the heap, not the perm gen to store String literals.
on Java 7 update 4 prints
I would expect that Java 6 does not do this.