java substring 性能_《Java程序性能优化》subString()方法的内存泄露
String的构造
首先了解下String的构造,String内部使用char [] value 来存储字符。
需要注意 offset和count在1.7已经没有了。
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;//仅仅出现在1.7以下
/** The count is the number of characters in the String. */
private final int count;//仅仅出现在1.7以下
测试用例
环境: jdk1.6 之后为jdk1.7
运行参数 这里指定了最大堆为50m . 如果list仅仅存(5-1)*100个字符 应该是完全没问题的。
`-Xms50m -Xmx50m -XX:+PrintGCDetails`
public class MyTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
List list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
list.add(new HugeStr().getSub());
// list.add(new ImprovedHugeStr().getSub());
}
Object o = list.get(99);
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
Object o1 = value.get(o);
if (o1 instanceof char[]) {
System.out.println(((char[]) o1).length);
}
}
public static class HugeStr{
private String str = new String(new char[1024*1024]);
public String getSub(){
return str.substring(1, 5);//1.7已经修复, 内部采用Arrays.copyOfRange
}
}
public static class ImprovedHugeStr{
private String str = new String(new char[1024*1024]);
public String getSub(){
return new String(str.substring(1, 5));//new String的时候会使用Arrays.copyOfRange 拷贝需要的部分。
}
}
}
jdk1.6 String代码部分实现
需要注意subString时传入的value是原字符串的char [] value.
new String 时,判断了offset与count.通过 Arrays.copyOfRange生成字符数组。
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);//此处value储存有原字符串的所有字符①
}
// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {//subString最终调用此方法②
this.value = value;
this.offset = offset;
this.count = count;
}
public String(String original) {
int size = original.count;
char[] originalValue = original.value;
char[] v;
if (originalValue.length > size) {
// The array representing the String is bigger than the new
// String itself. Perhaps this constructor is being called
// in order to trim the baggage, so make a copy of the array.
int off = original.offset;
v = Arrays.copyOfRange(originalValue, off, off+size);//③此处保证修复后的代码不再泄露
} else {
// The array representing the String is the same
// size as the String, so no point in making a copy.
v = originalValue;
}
this.offset = 0;
this.count = size;
this.value = v;
}
由上面代码可以知道。当使用HugeStr的getSub的时候,str.subString(1,5)返回的子串包含了原字符串的char数组 value.
jdk1.6中使用测试subString
①处 subString的value (v)和②处value代表原字符串str 的字符数组(长度大于4)。由于v的长度远大4,所以很快就内存不够,当内存不够发生gc的时候,v一直被子串b引用,导致无法回收。所以会报OOM。
看下测试效果:
一个子串包含了原字符串的所有char,1024*1024=148576
jdk1.6中使用修复后的代码测试subString
而ImprovedHugeStr的gutSub的时候, 实际在new String(str.substring(1, 5));的时候,返回的Arrays.copyOfRange拷贝的值(③)。已经失去了原字符串str 包括其value的引用,即使原字符串的value 很大,但是已经不被GC Roots对象链接到,会被回收。区区4长度*100的字符串, 不会引起OOM.
效果如图:
JDK1.7中已经修复了该问题
在new String内部使用了 Array.copyOfRange 拷贝。 原value 不再引用。当内存不够发生gc的时候,原value被回收。
jdk1.7 String代码部分实现
关键在 subString后调用的构造方法有改变。也是通过Arrays.copyOfRange拷贝。
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) {//subString最终调用此构造方法
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);//此处是关键
}
public String(String original) {
this.value = original.value;//1.7的构造方法有改变。 成员不再有 count,offset
this.hash = original.hash;
}
测试效果:
参考
java substring 性能_《Java程序性能优化》subString()方法的内存泄露相关推荐
- java jpa性能_[Java Performance] 数据库性能最佳实践 - JPA和读写优化
数据库性能最佳实践 当应用须要连接数据库时.那么应用的性能就可能收到数据库性能的影响. 比方当数据库的I/O能力存在限制,或者因缺失了索引而导致运行的SQL语句须要对整张表进行遍历.对于这些问题.只相 ...
- 大专java考试试题_专科—程序设计基础题库-java.doc
专科-程序设计基础题库-java 专科15级<程序设计基础>题库100道 总共抽8道题,每小题12.5分,共100分. 按题型:顺序(1道).分支(1道).单循环(2道).多循环(1道). ...
- 打开网页被Java拦截怎么办_应用程序已被java安全阻止怎么办
我们经常会通过浏览器来浏览各种网页,然而有时候会遇到各种提示,例如就有不少 具体步骤如下: 1.完成JAVA版本更新,然后刷新有问题的网页页面,以排除JAVA版本问题引起JAVA安全阻止: 2.从开始 ...
- java取负数_[Java] 告别“CV 工程师”码出高效!(基础篇)
作为一名资深的 CV 工程师,某天,当我再一次日常看见满屏的报错信息与键盘上已经磨的泛白的 Ctrl.C.V 这三个按键时,我顿悟了. 百度谷歌复制粘贴虽然很香,但是总是依靠前人种树,终会有一天失去乘 ...
- nio java 内核拷贝_大文件拷贝,试试NIO的内存映射
最近项目里有个需求需要实现文件拷贝,在java中文件拷贝流的读写,很容易就想到IO中的InputStream和OutputStream之类的,但是上网查了一下文件拷贝也是有很多种方法的,除了IO,还有 ...
- java方法区内存泄露_深入理解java虚拟机-第二章:java内存区域与内存泄露异常...
2.1概述: java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解vm是怎么去使用内存的. 2 ...
- java控制语句练习题_[Java初探实例篇02]__流程控制语句知识相关的实例练习
本例就流程控制语句的应用方面,通过三个练习题来深入学习和巩固下学习的流程控制语句方面的知识,设计到,if条件判断语句,switch多分支语句,for循环语句及其嵌套多层使用,while循环语句. 练习 ...
- java 崩溃日志_Android收集程序崩溃日志的方法
安卓Android如何手机程序崩溃日志并上传到服务器呢?直接会用到Thread线程里面的UncaughtExceptionHandler接口方法,我们可以自定义一个类CrashHandler,代码如下 ...
- java打包拆包_[Java] Java 打包成jar包 和 解压jar包
解压jar包 jar xf xxx.jar 打包成jar包 方法一:通过jar命令 jar命令的用法: 下面是jar命令的帮助说明: 用法:jar {ctxui}[vfm0Me] [jar-file] ...
- 断言java怎么用_[java] java断言的使用
所谓断言(assertion)是一个Java语句,布尔表达式,程序员认为在程序执行时该表达式的值应该为true.系统通过计算该布尔表达式执行断言,若该表达式为false系统会报告一个错误. 1.断言是 ...
最新文章
- 深入研究ConcurrentHashMap 源码从7到8的变迁
- 大学教师辞职创业,已向高校捐赠超10亿元!多以个人名义……
- Tomcat(四):tomcat图形管理和身份认证
- 一些意想不到的小bug。
- 信息检索Information Retrieval评价指标
- 【STM32】FreeRTOS任务相关API
- 在文件中读取列表功能
- matlab rootdir,Python cfg.ROOT_DIR属性代码示例
- Linux RS-232 程式設計
- DEDECMS系统后台添加菜单列表
- Log:日志选型调研『一』
- termux使用教程python手机_渗透测试|利用手机攻击电脑(Termux终端初体验)
- scratch少儿编程实例教程(模拟动画片项目式教学)——幻影龙动画编程
- C/C++基础讲解(二十六)之数值计算与趣味数学篇(打鱼还是晒网与怎样存钱以获取最大利息)
- 云服务器密码登录异常的解决办法
- vue3条码批量打印
- sw运行很卡怎么办_怎样才能提高Solidworks的运行速度/SW运行慢怎么办?
- 【概率论与数理统计】期末复习抱佛脚:公式总结与简单例题(完结)
- (function(){}())与(function(){})()的区别
- vue设置国际化字体