如何处理大字符串和有限的内存
Posted
技术标签:
【中文标题】如何处理大字符串和有限的内存【英文标题】:How to deal with big strings and limited memory 【发布时间】:2011-01-10 01:00:17 【问题描述】:我有一个从中读取数据的文件。 该文件中的所有文本都存储在一个字符串变量(一个非常大的变量)中。 然后在我的应用程序的另一部分,我想逐步浏览这个字符串并提取有用的信息(解析字符串)。
与此同时,我的内存已满,OutOfMemory 异常使我无法进行进一步处理。 我认为在从文件中读取输入流时直接处理数据会更好。但出于组织目的,我想将字符串传递给我的应用程序的另一部分。
如何防止内存溢出?
【问题讨论】:
你不能用其中一个阅读器(例如 BufferedReader)逐位解析文件吗? 【参考方案1】:您应该使用BufferedInputReader 而不是将这些全部存储到一个大字符串中。
如果您要解析的内容恰好在同一行,那么StringTokenizer 会很好地工作,否则您必须设计一种方法从文件中读取您想要的内容以解析出语句,然后将 StringTokenizer 应用于每个语句。
【讨论】:
+1。 Anthony:一般的想法是你通过 CURSORS(就像在 DB 中一样)。它们可以是文本情况下的阅读器,字节情况下的流,项目序列情况下的迭代器,或其他任何东西。您可以将一种类型转换为另一种类型(将序列中的每个项目,例如,将文件中的一行转换为某个域对象)但是应用程序的一个区域提供给另一个区域的是光标,因此它是一个句柄来使用一次输入一个步骤,而无需了解读取文件或您在中间实现的任何转换。 您提供的BufferedInputReader
和StringTokenizer
的链接不可用。【参考方案2】:
如果您可以稍微放宽您的要求,您可以实现由您的文件支持的java.lang.CharSequence。
支持 CharSequence many places in the JDK (A String is a CharSequence) 。因此,这是基于 Reader 实现的一个很好的替代方案。
【讨论】:
【参考方案3】:其他人建议一次读取和处理文件的某些部分。如果可能的话,其中一种方法会更好。
但是,如果这是不可能的,并且您可以按照您的指示最初将String
加载到内存中,但稍后解析该字符串会产生问题,您可以使用子字符串。在 Java 中,子字符串映射到原始 char
数组的顶部,并且只占用基本 Object
的内存,然后是 start 和 length int 指针。
因此,当您找到要单独保留的字符串的一部分时,请使用以下内容:
String piece = largeString.substring(foundStart, foundEnd);
如果您改为使用这个或内部执行此操作的代码,那么内存使用将急剧增加:
new String(largeString.substring(foundStart, foundEnd));
请注意,出于这个原因,您必须小心使用String.substring()
。您可以有一个非常大的字符串,从中取出一个子字符串,然后丢弃对原始字符串的引用。问题是子字符串仍然引用原始的大 char
数组。在子字符串也被删除之前,GC 不会释放它。在这种情况下,实际使用new String(...)
来确保GC 丢弃未使用的大数组很有用(这是您应该使用new String(...)
的少数情况之一)。
如果您希望有很多小字符串并且这些字符串可能具有相同的值,但来自外部源(如文件),另一种技术是在创建新字符串后使用.intern()
。
注意:这确实取决于 String
的实现,您确实不必了解这一点,但实际上对于大型应用程序,有时您确实必须依赖这些知识。请注意,Java 的未来版本可能会改变这一点(尽管不太可能)。
【讨论】:
【参考方案4】:您必须检查处理大数据的算法。您必须逐块处理此数据,或者使用随机文件访问而不将数据存储在内存中。例如,您可以像 @Zombies 所说的那样使用 StringTokenizer 或 StreamTokenizer。 您可以看到 parser-lexer 技术:当 parser 解析某个表达式时,它要求 lexer 读取下一个 lexem(tokens),但不会一次读取整个输入流。
【讨论】:
以上是关于如何处理大字符串和有限的内存的主要内容,如果未能解决你的问题,请参考以下文章