java慎用String.substring(int start, int end)
来源:互联网 发布:淘宝售假限制宝贝数量 编辑:程序博客网 时间:2024/06/14 02:43
1:问题的抛出
今天在安卓项目中使用后台线程操作一个大文件,分块读取文件中的所有内容,每次操作加载一个小块进行解析,解析到指定的文本内容之后会加载并常驻内存中,即使所有我解析到的文本内容被加入到内存中也不会很大,这样不会造成内存泄露问题。原理如此,但是最终依然出现oom。
2:问题的排查
仔细检查之后发现线程中所有的产生的对象都已经在操作结束之后回收(即:生成的对象没有引用)。代码很简短,挨个排查,发现一个string对象调用了substring(int start, int end)方法,查看了一下substring方法的注释:
Returns a string containing a subsequence of characters from this string. The returned string shares this string's backing array.
注释中的这句话"shares this string's backing array"让我产生一个疑问:什么是backing array,会不会是原来string的所有内容数组?带着这个问题,我把调用该方法的代码注释掉再次运行,哈哈,果然顺畅了。
3:产生的原因
定位到String.substring(int start, int end)的源代码:
public String substring(int start, int end) { if (start == 0 && end == count) { return this; } // NOTE last character not copied! // Fast range check. if (start >= 0 && start <= end && end <= count) { return new String(offset + start, end - start, value); } throw startEndAndLength(start, end); }
可见,正常情况下返回的是new String(offset + start, end - start, value);这个新的字符串,重点看看那个value参数到底是什么呢,源码中定义为:
private final char[] value;
我在构造自己的String的时候使用的是String(byte[] data)构造方法,追溯到value数组初始化的地方:
public String(byte[] data, int offset, int byteCount, Charset charset) { if ((offset | byteCount) < 0 || byteCount > data.length - offset) { throw failedBoundsCheck(data.length, offset, byteCount); } // We inline UTF-8, ISO-8859-1, and US-ASCII decoders for speed and because 'count' and // 'value' are final. String canonicalCharsetName = charset.name(); if (canonicalCharsetName.equals("UTF-8")) { byte[] d = data; char[] v = new char[byteCount]; int idx = offset; int last = offset + byteCount; int s = 0;outer: while (idx < last) { byte b0 = d[idx++]; if ((b0 & 0x80) == 0) { // 0xxxxxxx // Range: U-00000000 - U-0000007F int val = b0 & 0xff; v[s++] = (char) val; } else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) || ((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe) == 0xfc)) { int utfCount = 1; if ((b0 & 0xf0) == 0xe0) utfCount = 2; else if ((b0 & 0xf8) == 0xf0) utfCount = 3; else if ((b0 & 0xfc) == 0xf8) utfCount = 4; else if ((b0 & 0xfe) == 0xfc) utfCount = 5; // 110xxxxx (10xxxxxx)+ // Range: U-00000080 - U-000007FF (count == 1) // Range: U-00000800 - U-0000FFFF (count == 2) // Range: U-00010000 - U-001FFFFF (count == 3) // Range: U-00200000 - U-03FFFFFF (count == 4) // Range: U-04000000 - U-7FFFFFFF (count == 5) if (idx + utfCount > last) { v[s++] = REPLACEMENT_CHAR; continue; } // Extract usable bits from b0 int val = b0 & (0x1f >> (utfCount - 1)); for (int i = 0; i < utfCount; ++i) { byte b = d[idx++]; if ((b & 0xc0) != 0x80) { v[s++] = REPLACEMENT_CHAR; idx--; // Put the input char back continue outer; } // Push new bits in from the right side val <<= 6; val |= b & 0x3f; } // Note: Java allows overlong char // specifications To disallow, check that val // is greater than or equal to the minimum // value for each count: // // count min value // ----- ---------- // 1 0x80 // 2 0x800 // 3 0x10000 // 4 0x200000 // 5 0x4000000 // Allow surrogate values (0xD800 - 0xDFFF) to // be specified using 3-byte UTF values only if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) { v[s++] = REPLACEMENT_CHAR; continue; } // Reject chars greater than the Unicode maximum of U+10FFFF. if (val > 0x10FFFF) { v[s++] = REPLACEMENT_CHAR; continue; } // Encode chars from U+10000 up as surrogate pairs if (val < 0x10000) { v[s++] = (char) val; } else { int x = val & 0xffff; int u = (val >> 16) & 0x1f; int w = (u - 1) & 0xffff; int hi = 0xd800 | (w << 6) | (x >> 10); int lo = 0xdc00 | (x & 0x3ff); v[s++] = (char) hi; v[s++] = (char) lo; } } else { // Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff v[s++] = REPLACEMENT_CHAR; } } if (s == byteCount) { // We guessed right, so we can use our temporary array as-is. this.offset = 0; this.value = v; this.count = s; } else { // Our temporary array was too big, so reallocate and copy. this.offset = 0; this.value = new char[s]; this.count = s; System.arraycopy(v, 0, value, 0, s); } } else if (canonicalCharsetName.equals("ISO-8859-1")) { this.offset = 0; this.value = new char[byteCount]; this.count = byteCount; Charsets.isoLatin1BytesToChars(data, offset, byteCount, value); } else if (canonicalCharsetName.equals("US-ASCII")) { this.offset = 0; this.value = new char[byteCount]; this.count = byteCount; Charsets.asciiBytesToChars(data, offset, byteCount, value); } else { CharBuffer cb = charset.decode(ByteBuffer.wrap(data, offset, byteCount)); this.offset = 0; this.count = cb.length(); if (count > 0) { // We could use cb.array() directly, but that would mean we'd have to trust // the CharsetDecoder doesn't hang on to the CharBuffer and mutate it later, // which would break String's immutability guarantee. It would also tend to // mean that we'd be wasting memory because CharsetDecoder doesn't trim the // array. So we copy. this.value = new char[count]; System.arraycopy(cb.array(), 0, value, 0, count); } else { this.value = EmptyArray.CHAR; } } }
看看源码就终于明白了,value长度就是原有byte数组根据不同编码计算得到的结果,其内容自然是字符串中所有数据内容。
4:总结
java中的String.substring(int start, int end)方法返回的新字符串仍然保持原来字符串的数据引用,如果数据量比较大,这里需要注意一下会不会产生内存问题。
0 0
- java慎用String.substring(int start, int end)
- js:string.substring(int start,int end)
- public String substring(int start, int end)
- public String substring(int start, int end)
- String中subString(int start,int end) index的问题
- Java StringBuffer.substring(int start, int end)方法
- Paint.getTextBounds (String text, int start, int end, Rect bounds)
- java String substring(int beginIndex,int endIndex)
- Enumerable.Range(int:start,int:end)
- public String substring(int beginIndex, int endIndex)(Java)
- String.subString(start,end)用法
- 核心处理:使用List.subList(int start,int end);进行段位截取。 [java] view plain copy import java.util.ArrayList;
- java 正则(3) matches() / find() / lookingAt / start end / replaceAll / appendReplacement / group(int)
- java int string convert
- java string int
- Java String转int
- java String to int
- java string转int
- web.xml中出现<servlet-name>default</servlet-name>是什么意思?
- PAXOS算法3
- IOS AlertView动态添加按钮
- 去掉字符串中的 数字,字母,或者汉字
- Linux 下OCCI( Oracle C++ Call Interface )安装和使用
- java慎用String.substring(int start, int end)
- ET模式下epoll编程需要注意的问题
- 练习,输出1/3-3/5+5/7-7/9…+19/21的结果
- 后台架构常用开源项目
- [Cocos2d-x] loading加载动画
- c++电话面试题集锦
- 仿微信5.2界面解析
- 计算机视觉相关领域链接
- vc6.0学习笔记-常用控件的使用