当前位置:编程学习 > JAVA >>

案例分析:java中substring引发的Full gc

问题定位
由于应用频繁地Full gc,就dump了内存下来用MAT分析,发现有个map占用了98%的内存,于是找到这个map
 
 
[java]  
private ConcurrentMap<String, String> nick2numid = new ConcurrentHashMap<String, String>();  
 
private ConcurrentMap<String, String> nick2numid = new ConcurrentHashMap<String, String>();存放的是nick与id的映射关系,从MAT中找到map的每一个entry如下图所示: 
 
 
\
 
这里解释一下两个概念
 
Shallow Heap:对象占用了多少内存(单位:字节)
 
Retained Heap:如果对象被回收会释放多少内存,也就是对象hold住的内存
 
map的key是一个String类型,其Shallow Heap为32byte,Retained Heap为1104byte。一般对于String类型,具有不可变性,这两个值应该相等才对,带着疑惑找到了问题所在,分步描述如下:
 
1、从页面传了一个参数到后端,这个参数携带了cookie的内容,恰好就是1104byte这么长,不妨设置这个参数为cookie;
 
2、后端拿到cookie这个参数后,需要其中的nick的值,采用的是String类的split方法(&做为分隔符)得到一个数组,其中有一项的值为nick=xxx
 
3、将nick的值做为key放入nick2numid中
 
 
[java]  
nick2numid.put("xxx",id)  
 
nick2numid.put("xxx",id)最终发现问题出在String类的substring方法上? 
 
 
分析问题
其实String的split方法上调用了substring方法,先来看看split的源码实现吧 
 
 
[java] 
public String[] split(String regex) {  
    return split(regex, 0);  
}  
public String[] split(String regex, int limit) {  
        return Pattern.compile(regex).split(this, limit);  
}  
  
 public String[] split(CharSequence input, int limit) {  
        ArrayList<String> matchList = new ArrayList<String>();  
        Matcher m = matcher(input);  
  
        // Add segments before each match found   
        while(m.find()) {  
            if (!matchLimited || matchList.size() < limit - 1) {  
                String match = input.subSequence(index, m.start()).toString();  
                matchList.add(match);  
                index = m.end();  
            } else if (matchList.size() == limit - 1) { // last one   
                String match = input.subSequence(index,  
                                                 input.length()).toString();  
                matchList.add(match);  
                index = m.end();  
            }  
        }  
}     
  
public CharSequence subSequence(int beginIndex, int endIndex) {  
    return this.substring(beginIndex, endIndex);  
}  
 
public String[] split(String regex) {
    return split(regex, 0);
}
public String[] split(String regex, int limit) {
return Pattern.compile(regex).split(this, limit);
}
 
 public String[] split(CharSequence input, int limit) {
        ArrayList<String> matchList = new ArrayList<String>();
        Matcher m = matcher(input);
 
        // Add segments before each match found
        while(m.find()) {
            if (!matchLimited || matchList.size() < limit - 1) {
                String match = input.subSequence(index, m.start()).toString();
                matchList.add(match);
                index = m.end();
            } else if (matchList.size() == limit - 1) { // last one
                String match = input.subSequence(index,
                                                 input.length()).toString();
                matchList.add(match);
                index = m.end();
            }
        }
}   
 
public CharSequence subSequence(int beginIndex, int endIndex) {
    return this.substring(beginIndex, endIndex);
}String.split->Pattern.split->subSequence->substring 
 
从以上代码可以看出String类的split方法确实调用了substring方法
下面来看看substring方法源码:
 
 
[java] 
private final char value[];  
  
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);  
}  
 
private final char value[];
 
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这个char数组,存放的是String每个字符的内容,substring直接依赖了这个数组。如果substri
补充:软件开发 , Java ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,