首页 > 程序员 > java中String.split导致的内存泄露

java中String.split导致的内存泄露

2010年1月29日 闫鹏 发表评论 阅读评论

有的时候,由于我们队Java api理解上面的错误,可能会导致出现一些意外的问题,比如内存泄露。认为虚拟机会为你创建一个新的对象,引用了不同的内存区域,结果却和想象中的不一样,导致服务宕机。

今天在查网关的内存溢出问题时,发现问题出现在String.split函数上面。大家都认为Java中的String是只读的,new新对象的时候一定是分配了新的内存,但虚拟机内部却不一定如此。sun的hotspot1.5版本在实现java的类时做了优化,为了减少内存拷贝,一个String对象可能会引用另外一个String对象的内存。查看jdk的源代码可知,String内部是用一个名为value的char数组存储内容的,但同时另外也有两个int类型的变量:offset与count。

查看String.split()函数的源代码,随后调用的是Pattern.split(),随后调用String.subSequence(),随后调用String.substring()。String的substring代码如下

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);
}

如果看到最后return的是new String,以为就是一个新的String就真的囧了,这不是一个向外开放的构造函数,它创建了一个新的对象,却是用的原来String对象中的内存

// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}

本来这样进行优化无可厚非,但在网关的应用情况下就出现问题了:
一次性查询得到一个String,里面记载了1000条数据结果,将这1000条数据结果拆分,封装成1000个对象,存在缓存里面。缓存是用LinkedHashMap,当Entry达到一定数量时就进行淘汰。这样就导致这1000个对象都淘汰了,才会释放存储了1000个数据的String,这个String可是相当的大啊。
又因为查询的时间段是有重叠的,导致每次查询的String都记载了很多重复的内容,虽然缓存里面只对String不同的地方保持了一次引用,但是却无意间存储了大量的无用数据。这就导致了jvm的内存溢出。

好多年没搞java了,结合以前上学时候查java内存泄露的一点经验,说明了:

1.出现内存泄露,通常都是有容器保持了大量的引用,查问题的时候,可以从容器优先开始

2.查看jdk的源代码,是很有必要的。

您可能会喜欢:

  1. 关于数钱费
分类: 程序员 标签:

  1. 2010年2月1日11:59 | #1

    这个ms很囧啊。只在频繁需要引用split出来的各个部分的时候这个优化才有用。如果我只用其中一个,这个优化完全垃圾啊。

  2. 2010年2月1日12:13 | #2

    避免了内存拷贝,能够在一定程度上提高性能的。90%的情况下应该都是好的,只是正好赶上网关这样了

  3. 2010年2月1日13:04 | #3

    但是空间确实浪费了。呵呵。

  4. 2010年2月1日13:09 | #4

    空间换时间啊

  1. 本文目前尚无任何 trackbacks 和 pingbacks.