string can be said to be one of the most common Java types, but I've recently heard JDK6 inside String.substring There is a memory leak bug, gang stunned! Let's see what the situation is.
This is the code that can cause the exception in thread "main" Java.lang.OutOfMemoryError:Java Heap space:
public class testgc { private string largestring = New string (new byte[100000]); string getstring () { return this.largestring.substring (0, 2);//In JDK6 will cause out of memory, there is no problem with JDK7 and 8// return new String ("AB");// return this.largestring.substring (0,2) + "";//jdk6 solution, does not appear out of memory// Return new string (this.largeString.substring (0, 2)),/jdk6 resolution, does not appear out of memory } public static void main (String[] args) { java.util.list<string> list = new java.util.arraylist<string> (; for ) (int i = 0; i < 100000; i++) &NBSP;{&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;TESTGC &NBSP;GC&NBSP;=&NBSP;NEW&NBSP;TESTGC (); list.add (Gc.getstring ()); } system.out.println ("Over" + list.size ()); }}
But it's safe to run with JDK8. Note that before the online article to see the installation of JDK8, only need to select the compiler option in Eclipse is JDK6, I can not experiment, Think of the string is the JDK inside the Rt.jar class, even if it is compiled to JDK6 code, run time or use the JDK8 string ah, it is not possible to reproduce the bug is normal. To reproduce, you can only download the installation JDK6.
Some people think that this will be out of memory because the TESTGC object has a very large largestring object, but its real call getstring method, TESTGC object can be completely recycled, largestring can also be recycled, The JVM's automatic garbage collection should have no bugs, or else! Instead of changing the GetString method directly to return a string object, you can see that there is no problem.
Now let's see why JDK6 inside, substring can lead to mistakes. Ctrl+b (Idea's view source shortcut key point to see), the code is as follows
Public string substring (Int beginindex, int endindex) { if (beginindex < 0) { throw new stringindexoutofboundsexception (beginindex); } if ( Endindex > count) { throw new Stringindexoutofboundsexception (EndIndex); } if (beginIndex > endindex) { throw new Stringindexoutofboundsexception (Endindex - beginindex); } return ((beginindex == 0) && (endindex == count)) ? this : new string (Offset + beginIndex , endindex - beginindex, value); }
The first few lines are mainly to do scope check, the most important is
New String (offset + beginindex, endindex-beginindex, value);
String (int offset, int count, Char value[]) {this.value = Value;this.offset = Offset;this.count = count;}
You can see that the substring in JDK6 the entire value of the original large string, which is the array that holds the actual char in the string.
/** The value is used for character storage. */private final char value[];
And just by modifying beginindex and offset to achieve the reuse of value, to avoid the trouble of array copy (and can improve a bit of performance), but the problem is that if the original string is large, and substring retained for a long time, It is possible to cause the entire large value to not be recycled. The fix for JDK6 is to force the generation of a new string to avoid reusing the value in the original string, such as:
Return this.largeString.substring (0,2) + "";//jdk6, the solution does not appear out of the memory
In fact, this is precisely the JDK8 inside of the implementation of the way. Upper Src:
Public string substring (Int beginindex, int endindex) { if (beginindex < 0) { throw new stringindexoutofboundsexception (BeginIndex); } if (endindex > value.length) { throw new stringindexoutofboundsexception (endIndex); } int subLen = endIndex - beginIndex; if (sublen < 0) { throw new stringindexoutofboundsexception ( Sublen); } return ((beginIndex == 0 ) && (endindex == value.length) ? this : new string (value, Beginindex, sublen); }
.
Public string (Char value[], int offset, int count) { if (offset < 0) { throw new stringindexoutofboundsexception (offset); } if (count < 0) { throw new Stringindexoutofboundsexception (count); } // note: offset or count might be near - 1>>>1. if (offset > value.length - count) { throw New stringindexoutofboundsexcEption (Offset + count); } this.value = arrays.copyofrange (Value, offset, offset+count); }
As you can see, the array copy is finally done for value.
In fact, JDK8 modification is also mixed, some people think that JDK6 inside the way to achieve better, more efficient, as long as their attention can avoid the problem, this is the question of the benevolent see, just need to know, JDK6 string of this small pit is good.
Reference articles
http://droidyue.com/blog/2014/12/14/substring-memory-issue-in-java/
http://www.programcreek.com/2013/09/the-substring-method-in-jdk-6-and-jdk-7/
Java string.substring memory leak?