Reputation: 189
I have android application which takes huge String Object from telnet client. Later I Use only small part of the big String. I use
new String(Part of old string);
To separate new string Char array from old Strings char array. So the old String Should get garbage collected, but new string surprisingly still has an reference to the old object. I can see it with "Eclipse Memory Analyzer". And that overflows my tiny 16Meg application memory quickly.
How to avoid that situation?
private WifiChannel parse1(String channLine){
//scanning with "iwlist wlan0 scanning" the getChans1 method
String[] input = channLine.split(System.getProperty("line.separator"));
if (input.length < 4);
String segment[];
String segment2[];
WifiChannel chan = new WifiChannel();
try {
if (input.length > 5){
chan.setMacAddress(new String(input[0]));
segment = input[1].split(":");
chan.setChannel(Integer.parseInt(segment[1].trim()));
segment = input[3].split(" ");
segment2 = segment[20].split("=");
chan.setQuality(new String(segment2[1]));
segment2 = segment2[1].split("/");
chan.setSignalStrength((Integer.parseInt(segment2[0].trim())*100)/Integer.parseInt(segment2[1].trim())+"%");
segment2 = segment[23].split("=");
try{chan.setSignalLevel(Integer.parseInt(segment2[1].trim()));}catch(Exception e){chan.setSignalLevel(0);}
segment = input[5].split(":");
chan.setName(new String(segment[1].replaceAll("^\"|\"$", "")));
for (int i = 6;i<input.length;i++)
if (input[i].contains("Mode"))
segment = input[i].split(":");
chan.setChannelMode(new String(segment[1]));
String band = "";
if(channLine.contains("5.5 Mb/s"))band = band +"b";
if(channLine.contains("12 Mb/s") )band = band +"g";
chan.setBand(new String(band));
}
}catch (Exception e){Log.e("","",e);}
return chan;
}
Method input is also part of bigger String.
Upvotes: 0
Views: 6758
Reputation: 14257
Every String instance is backed by a char array:
public class String {
private final char[] value;
...
}
Because of efficiency reasons, some operations on a string can can create a new string instance that shares the char[]
with the original string. This is possible because all strings are immutable. An example of this is the substring()
method:
public String substring(int start) {
if (start == 0) {
return this;
}
if (start >= 0 && start <= count) {
return new String(offset + start, count - start, value); // !!!
}
throw indexAndLength(start);
}
The line with my comment added calls a constructor that doesn't create a copy of the char[]
, but directly references it. You can't call this constructor directly, because it is package-private and therefore it is used only internally by methods such as substring:
/*
* Internal version of the String(char[], int, int) constructor.
* Does not range check, null check, or copy the character array.
*/
String(int offset, int charCount, char[] chars) {
this.value = chars;
this.offset = offset;
this.count = charCount;
}
Now, if you create a short substring from a very long string, then the short one still references the original large char[]
. Even after the original string is garbage collected, its large array still stays in the memory, even though only only a small portion of it is now accessible by the new substring. This effectively creates a memory leak.
To fix this problem, the usual technique is to create a new string from the substring using the copy constructor, which makes a copy of only the needed range of the original char[]
.
String longString = "1234567890";
// backed by char[] of length 10
String substring = longString.substring(5);
// value is "67890", but still backed by the original char[] of length 10
String copy = new String(substring);
// also has value "67890", but now backed only by char[] of length 5
Edit:
For completeness, this is the source of the copy-contructor. As you can see, if the original string references an array of the same length as of the string itself, then the array doesn't need to be copied, because there are no "dead characters" in it. However, if the array is larger then the length of the string, then a copy of the "live" range of the array is performed.
public String(String toCopy) {
value = (toCopy.value.length == toCopy.count)
? toCopy.value
: Arrays.copyOfRange(toCopy.value, toCopy.offset,
toCopy.offset + toCopy.length());
offset = 0;
count = value.length;
}
Note: All the above source code comes from Android API 15
Upvotes: 3