论坛首页 Java企业应用论坛

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

浏览 52631 次
该帖已经被评为精华帖
作者 正文
   发表时间:2010-03-31  
异常的思路挺好,不过一个是Stream一个是String对于substring功能的处理方式本来就不一样,没有可比性。
0 请登录后投票
   发表时间:2010-03-31  
太感谢了 最近正在研究这类的问题
0 请登录后投票
   发表时间:2010-03-31  
这不是split方法的问题,实际上,如果你是一个大字符串,用来拆成N个小串,那内存使用没有问题;但是如果你是接收N个大字符串,然后每个大串中仅需要保留一小部分,我觉得这应该是需要从源头上去解决,split的做法还是没有错

ps: 这种问题应该在压力测试的时候就暴露出来的,对网络传输、并发都是问题
0 请登录后投票
   发表时间:2010-03-31  
iooyoo 写道
这不是split方法的问题,实际上,如果你是一个大字符串,用来拆成N个小串,那内存使用没有问题;但是如果你是接收N个大字符串,然后每个大串中仅需要保留一小部分,我觉得这应该是需要从源头上去解决,split的做法还是没有错

ps: 这种问题应该在压力测试的时候就暴露出来的,对网络传输、并发都是问题


很赞同你的说法

slite和subString的实现,我觉得很合理,只是我们使用时要充分了解。所以帖子的最后一个标题是“是否bug”。

确实没有做充分的、真实的压力测试,因此导致缓存的阈值设置过大,还没有开始淘汰,内存就超过预期了。
0 请登录后投票
   发表时间:2010-04-01  
抛出异常的爱 写道
我说的不是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 >= 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中应该明确说明。

我说的不是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();
    }
  }
}


用来统计字数的代码倒子


惭愧啊,没有仔细看你写的“StreamTokenizer”,误以为是笔误。

刚仔细看了一下,在我的应用场景中,确实很适合用StreamTokenizer。你说的这个,确实是终极解决方案!

学习了,多谢多谢!
0 请登录后投票
   发表时间:2010-04-01  
优化要建立在性能测试工具的基础上,在性能测试工具测试出哪段代码确实有性能问题才去优化性能。
0 请登录后投票
   发表时间:2010-04-01  
longhaisheng 写道
优化要建立在性能测试工具的基础上,在性能测试工具测试出哪段代码确实有性能问题才去优化性能。


赞同!好多关于性能的书上都建议:先测试,再有针对性的优化。不要过早优化、盲目优化。
0 请登录后投票
   发表时间:2010-04-02   最后修改:2010-04-02
楼主分析还是比较到位的,有研究精神,javaeye应该多些这样的帖子。
0 请登录后投票
   发表时间:2010-04-02  
应该是你引用了字符串一直没有释放吧,这样的话,就算不用split,迟早也得内存溢出
0 请登录后投票
   发表时间:2010-04-03  
mycybyb 写道
应该是你引用了字符串一直没有释放吧,这样的话,就算不用split,迟早也得内存溢出


我的本意是引用小String,数量达到阈值后自然淘汰。没想到引用的还是大String,没达到阈值,内存就超出预期了。
0 请登录后投票
论坛首页 Java企业应用版

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