Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用java加载大型文本文件的最佳方法_Java_String_Memory - Fatal编程技术网

用java加载大型文本文件的最佳方法

用java加载大型文本文件的最佳方法,java,string,memory,Java,String,Memory,我有一个文本文件,每行有一个整数序列: 47202 1457 51821 59788 49330 98706 36031 16399 1465 ... 该文件有300万行这种格式。我必须将这个文件加载到内存中,从中提取5克,并对其进行一些统计。我有内存限制(8GB RAM)。我尽量减少我创建的对象的数量(只有一个类有6个浮点变量,还有一些方法)。该文件的每一行基本上都会生成该类对象的数量(与该行的大小成正比,以#of words为单位)。当C++出现时,我开始觉得java不是一个好的方法。

我有一个文本文件,每行有一个整数序列:

47202 1457 51821 59788 
49330 98706 36031 16399 1465
...
该文件有300万行这种格式。我必须将这个文件加载到内存中,从中提取5克,并对其进行一些统计。我有内存限制(8GB RAM)。我尽量减少我创建的对象的数量(只有一个类有6个浮点变量,还有一些方法)。该文件的每一行基本上都会生成该类对象的数量(与该行的大小成正比,以#of words为单位)。当C++出现时,我开始觉得java不是一个好的方法。

编辑:
假设每行生成该类的(n-1)个对象。其中n是该行中由空格分隔的令牌数(即1457)。因此,考虑到每行10个单词的平均大小,每行平均映射到9个对象。因此,将有9*3*10^6个对象。因此,所需的内存是:9*3*10^6*(8字节obj头+6 x 4字节浮点)+(一个映射(字符串,对象)和另一个映射(整数,数组列表(对象)))。我需要将所有内容都保存在内存中,因为之后会发生一些数学优化。

读取/解析文件

用任何语言处理大型文件的最佳方法是尝试而不是将它们加载到内存中

在java中,看一看。它允许您将文件映射到进程内存并访问其内容,而无需将整个内容加载到堆中

您还可以尝试逐行读取文件,并在读取后丢弃每一行,以避免一次将整个文件保存在内存中

处理结果对象

要处理解析时生成的对象,有几个选项:

  • 与文件本身一样-如果您可以执行您想要执行的任何操作,而不必将所有操作都保存在内存中(同时“流式传输”文件)-这是最好的解决方案。你没有描述你要解决的问题,所以我不知道这是否可能

  • 压缩一些排序-从包装器对象(Float)切换到原语(Float),使用类似的方法将数据存储在巨型Float[]数组中,并且只构造短期对象来访问它,在数据中找到一些模式,允许您更紧凑地存储它

  • 缓存/卸载-如果您的数据仍然不适合内存,请将其“分页”到磁盘。这可以是简单的,或者引入一个或多个类似的库

  • 特别是关于java集合和地图的说明


    对于小型对象,java集合和映射尤其会导致大量内存损失(主要是因为所有内容都被包装为对象,并且存在Map.Entry内部类实例)。以稍微不那么优雅的API为代价,如果内存消耗是一个问题,那么您可能应该考虑集合。

    最好只保存整数和行尾

    为此,一种方法是:将文件转换为两个文件:

    • 一个二进制整数文件(4字节)
    • 一个带有索引的二进制文件,下一行将从其中开始
    为此,可以使用扫描仪进行读取,使用DataOutputStream+BufferedOutputStream进行写入

    然后,您可以在基元类型的数组中加载这两个文件:

    int[] integers = new int[(int)integersFile.length() / 4];
    int[] lineEnds = new int[(int)lineEndsFile.length() / 4];
    

    可以使用MappedByteBuffer.toIntBuffer()进行读取。(那时你甚至不需要数组,但它会变得有点像COBOL一样冗长。)

    使用
    BufferedReader
    你的问题是什么?300万。线?那要多少MB?100? 那不是很大。顺便说一句,“只有一个类”对您实际创建的对象的数量没有影响。(10个单词x 5个字符+9个空格+1个行尾)x 2字节*3密耳行=~630MB的原始文本。10个对象x(8个标题+24个字段)*3 mil=~915 MB对象。有关更节省空间的地图,请查看trove-。将文件解析为对象后,就不需要它了。即使使用一种非常简单的方法,您仍然应该适合2GB以下的容量。您不能再访问(=没有指针)的任何内容都可以用于GC。因此,一个简单的readLine()循环从BufferedReader中将一行作为字符串读取,并输出10个对象,它将生成大量短期字符串,然后进行GC’ed。您不需要显式销毁任何内容,只需不保留对不再需要的内容的引用。虽然这是事实,但在这种情况下可能并不需要。该文件有300万行,两个示例行中较长的一行长度为28个字符。假设每行平均大约30个字符,而Java的
    char
    是两个字节,那么只有大约180MB的RAM。@Wyzard-我是按他的话来算的。如果他说不合适。。。我还扩展了我的答案,涵盖了其他可能会吃掉他的垃圾堆的东西。他从来没有说过它不合适。我想知道这是否只是一个过早优化的例子。是的,但如果OP以某种方式使180MB的数据占用8GB,那么他/她的问题就不会真正通过使用
    MappedByteBuffer
    ,或者通过在读取时丢弃文本行来解决。如果OP确实耗尽了堆空间,那么他/她需要与此不同的帮助。180MB用于原始文件+300万x(8字节obj头+6 x 4字节浮动)用于对象+(12字节数组头+3 mil x 8字节指针)用于数组保存所有对象=180+114 MB=~300MB用于整个事件。我想我也不知道。