论坛首页 Java企业应用论坛

优化变成了忧患:String类的split方法引起的内存泄漏

浏览 53058 次
该帖已经被评为精华帖
作者 正文
   发表时间:2010-03-30   最后修改:2010-03-30
应该回收没回收也叫泄露
楼主的系统中有一个阀值可以干掉全局的hashMAP
但是在这个阀值不是以字节数进行统计的。
所以。。。。算是泄露不是内存溢出

PS:刚刚的回贴被干掉了。。。。
楼下你不认为这样的回复也会被干掉么
0 请登录后投票
   发表时间:2010-03-30  
有点类似于Buffer中的slice()方法。被分割出来的buffer和原来的buffer还共享同样的一个内部数组。


ByteBuffer bf = ByteBuffer.allocate(10) ;
		
for(int i=0;i<bf.capacity() ;i++)
{
         bf.put((byte)i) ;
}
		
bf.position(3) ;
bf.limit(7) ;
		
ByteBuffer bf2 = bf.slice() ;

for(int i=0 ; i<bf2.capacity() ;i++)
{
	byte b = bf2.get(i) ;
	b *= 11 ;
	bf2.put(i,b) ;
}
		
bf.position(0) ;
bf.limit(bf.capacity()) ;
		
while(bf.remaining() > 0)
{
	System.out.println(bf.get()) ;
}



输出结果:
0
1
2
33
44
55
66
7
8
9
0 请登录后投票
   发表时间:2010-03-30  
skydream 写道
kimmking 写道
我也没看懂,hold的是引用,又不是char,怎么会有问题。


他并不是说所有split的小字符串都是从同一个大字符串下split出来的,不同的请求大字符串的来源不同,这样慢慢攒下来的。



还是你的表达能力 强啊。
0 请登录后投票
   发表时间:2010-03-30  
查了一下javadoc,ByteBuffer明确了内容是同一个数组。String没有提示这点。
0 请登录后投票
   发表时间:2010-03-30  
回去看了一下API
发现楼主用错API了
明明应当使用
streamTokenizer

非要用string这瘸子来搬砖头。。。。
0 请登录后投票
   发表时间:2010-03-30  
抛出异常的爱 写道
回去看了一下API
发现楼主用错API了
明明应当使用
streamTokenizer

非要用string这瘸子来搬砖头。。。。


StringTokenizer也是有问题的哦,最终还是调用了subString方法。
    public String nextToken() {
	/* 
	 * If next position already computed in hasMoreElements() and
	 * delimiters have changed between the computation and this invocation,
	 * then use the computed value.
	 */

	currentPosition = (newPosition >= 0 && !delimsChanged) ?  
	    newPosition : skipDelimiters(currentPosition);

	/* Reset these anyway */
	delimsChanged = false;
	newPosition = -1;

	if (currentPosition >= maxPosition)
	    throw new NoSuchElementException();
	int start = currentPosition;
	currentPosition = scanToken(currentPosition);
	return str.substring(start, currentPosition);
    }

注意最后一行对subString的调用。

我觉得,这个问题本质上不在于调用哪个API。而是在于String类在性能和安全上的权衡。如果要优化性能,split和subString那样的优化是很自然的,也无可厚非。只是,JavaDoc中应该明确说明。
0 请登录后投票
   发表时间:2010-03-30  
lz最好在帖子上加上StringBuilder,StringBuffer就不会出这问题吧。要是很长的String,用这两个SB也是有优势的。StringBuilder,StringBuffer在j2me可惜了,要么没有这类,要么没substring这个方法。
0 请登录后投票
   发表时间:2010-03-30  
鸟哥哥 写道
lz最好在帖子上加上StringBuilder,StringBuffer就不会出这问题吧。要是很长的String,用这两个SB也是有优势的。StringBuilder,StringBuffer在j2me可惜了,要么没有这类,要么没substring这个方法。


StringBuilder和StringBuffer的subString方法是确实安全的。

可问题是,得到的是一个大String对象。如果基于此对象构造一个StringBuilder,那么所有的字符都首先被拷贝一次。然后subString每次都会拷贝一部分字符。结果就是,所有的字符都被拷贝两次。

如果使用new String()的方式,所有字符只会被拷贝一次。
0 请登录后投票
   发表时间:2010-03-30  
jarfield 写道
鸟哥哥 写道
lz最好在帖子上加上StringBuilder,StringBuffer就不会出这问题吧。要是很长的String,用这两个SB也是有优势的。StringBuilder,StringBuffer在j2me可惜了,要么没有这类,要么没substring这个方法。


StringBuilder和StringBuffer的subString方法是确实安全的。

可问题是,得到的是一个大String对象。如果基于此对象构造一个StringBuilder,那么所有的字符都首先被拷贝一次。然后subString每次都会拷贝一部分字符。结果就是,所有的字符都被拷贝两次。

如果使用new String()的方式,所有字符只会被拷贝一次。

帖子讨论的问题是内存溢出了。放SB里,String再=null,不省事,就不出溢出问题了嘛。
如果如此大的string,是否应该从char[]入手了?或者byte[]通过socket得到InputStream,一个个byte来读了?
0 请登录后投票
   发表时间:2010-03-30   最后修改:2010-03-30
我说的不是stringTokenizer
是streamTokenizer
而且对于这么大的字串
一般用流来作才更合理一些。
string不适合这样的工作。

jarfield 写道
抛出异常的爱 写道
回去看了一下API
发现楼主用错API了
明明应当使用
streamTokenizer

非要用string这瘸子来搬砖头。。。。


StringTokenizer也是有问题的哦,最终还是调用了subString方法。
    public String nextToken() {
	/* 
	 * If next position already computed in hasMoreElements() and
	 * delimiters have changed between the computation and this invocation,
	 * then use the computed value.
	 */

	currentPosition = (newPosition &gt;= 0 &amp;&amp; !delimsChanged) ?  
	    newPosition : skipDelimiters(currentPosition);

	/* Reset these anyway */
	delimsChanged = false;
	newPosition = -1;

	if (currentPosition &gt;= maxPosition)
	    throw new NoSuchElementException();
	int start = currentPosition;
	currentPosition = scanToken(currentPosition);
	return str.substring(start, currentPosition);
    }

注意最后一行对subString的调用。

我觉得,这个问题本质上不在于调用哪个API。而是在于String类在性能和安全上的权衡。如果要优化性能,split和subString那样的优化是很自然的,也无可厚非。只是,JavaDoc中应该明确说明。

我说的不是stringTokenizer


import java.io.*;
import java.util.*;

 
class Counter {
  private int i = 1;
  int read() { return i; }
  void increment() { i++; }
}
 
public class SortedWordCount {
  private FileInputStream file;
  private StreamTokenizer st;
  private Hashtable counts = new Hashtable();
  SortedWordCount(String filename)
    throws FileNotFoundException {
    try {
      file = new FileInputStream(filename);
      st = new StreamTokenizer(file);
      st.ordinaryChar('.');
      st.ordinaryChar('-');
    } catch(FileNotFoundException e) {
      System.out.println(
        "Could not open " + filename);
      throw e;
    }
  }
  void cleanup() {
    try {
      file.close();
    } catch(IOException e) {
      System.out.println(
        "file.close() unsuccessful");
    }
  }
  void countWords() {
    try {
      while(st.nextToken() !=
        StreamTokenizer.TT_EOF) {
        String s;
        switch(st.ttype) {
          case StreamTokenizer.TT_EOL:
            s = new String("EOL");
            break;
          case StreamTokenizer.TT_NUMBER:
            s = Double.toString(st.nval);
            break;
          case StreamTokenizer.TT_WORD:
            s = st.sval; // Already a String
            break;
          default: // single character in ttype
            s = String.valueOf((char)st.ttype);
        }
        if(counts.containsKey(s))
          ((Counter)counts.get(s)).increment();
        else
          counts.put(s, new Counter());
      }
    } catch(IOException e) {
      System.out.println(
        "st.nextToken() unsuccessful");
    }
  }
  Enumeration values() {
    return counts.elements();
  }
  Enumeration keys() { return counts.keys(); }
  Counter getCounter(String s) {
    return (Counter)counts.get(s);
  }
  Enumeration sortedKeys() {
    Enumeration e = counts.keys();
    StrSortVector sv = new StrSortVector();
    while(e.hasMoreElements())
      sv.addElement((String)e.nextElement());
    // This call forces a sort:
    return sv.elements();
  }
  public static void main(String[] args) {
    try {
      SortedWordCount wc =
        new SortedWordCount(args[0]);
      wc.countWords();
      Enumeration keys = wc.sortedKeys();
      while(keys.hasMoreElements()) {
        String key = (String)keys.nextElement();
        System.out.println(key 
                 + wc.getCounter(key).read());
      }
      wc.cleanup();
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
}


用来统计字数的代码倒子
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics