Java heap space一直是困扰我们的一个问题。像Matlab就可以一次性读取5000*5000的数据到一个矩阵matrix中。然而Java确不行。
我遇到实验室处理一个“合并5000左右txt文档到一个txt文件中”的问题,相同的算法用Matlab就不会出现内存不足的问题,而JAVA则会报出:java.lang.OutOfMemoryError: Java heap space.从这里可以判断的说Matlab是为处理大型数据而设计(It is conceived for),所以其执行代码时对内存启动了自动的分配与管理。因为我的5000左右txt文档大约有180M,显而易见,同时读到内存肯定会出现内存空间不足的警告,所以也不怪JAVA.当然,你可以在运行JAVA代码之前提高内存分配值,但是这实际上不能从根本上解决问题。因为哪天万一还有更大的数据遇到时,你是不是还要继续增大内存?要知道现在一般电脑的内存也就2G,就算1G的也很普遍。
所以根本上我觉得是要改变你的算法。一个很自然的思路就是:数据大了就分配处理嘛。
所以我最后就是利用了递归的手段,将5000左右txt文档分批调入内存进行处理,处理一部分就释放一部分资源。这样不管是5000个数据文件还是7000,9000都没问题。试验测得我的情况是如果不进行分批处理而是一次性调入内存在合并文档,那最大文件数只能是2000,就是超过2000个数据文件就会出现java.lang.OutOfMemoryError: Java heap space这样的错误。
Matlab的缺点就是速度慢。所以处理海里数据为求速度还是可以试一下用JAVA实现的。我的实验结果表明,速度相差几十倍。
下面将一步一步介绍对这个同一问题不同的编程版本以及其结果:
1. 以我实验室的频谱测量仪(spectrometer)对激光所获的离散采样为背景。
本程序是属于数据处理程序,用JAVA所实现。
spectrometer中光栅的每次采样所获得的数据被保存在一个文本文件中, 分为2列:第一列为波长值,第二列为对应的光强值。 第1次采样文本文件被命名为“000000.txt”,第二个为“000001.txt”,依次类推。 比如若让spectrometer采样3mins,大概会出现4000次采样, 被保存的文件就为000000.txt~003999.txt
此程序使用的是二维LinkedList类型来拉取数据。 LinkedList:就是采用链表结构保存对象。 PS:JAVA中集合类分为List,Set,和Map. List又有LinkedList和ArrayList; Set分为HashSet和TreeSet; Map分为HashMap和TreeMap. 顾名思义,Hash就是由哈希表提供支持,Tree树结构提供了排序和顺序输出的功能。
=>下载本程序所用到的存放离散采样数据文件的文件夹view plain package com.han;
import java.io.*;import java.util.*;
/** * 以我实验室的频谱测量仪(spectrometer)对激光所获的离散采样为背景。
* <p> * 本程序是属于数据处理程序,用JAVA所实现。
* <p> * spectrometer中光栅的每次采样所获得的数据被保存在一个文本文件中,* 分为2列:第一列为波长值,第二列为对应的光强值。
* 第1次采样文本文件被命名为“000000.txt”,第二个为“000001.txt”,依次类推。
* 比如若让spectrometer采样3mins,大概会出现4000次采样,* 被保存的文件就为000000.txt~003999.txt * <p> * 此程序使用的是二维LinkedList类型来拉取数据。
* LinkedList:就是采用链表结构保存对象。
* PS:JAVA中集合类分为List,Set,和Map. * List又有LinkedList和ArrayList;* Set分为HashSet和TreeSet;* Map分为HashMap和TreeMap. * 顾名思义,Hash就是由哈希表提供支持,Tree树结构提供了排序和顺序输出的功能。
* @author han * */ public class CombinedTextSpectres { static final int N=6;//N是保存的文件名称的长度static final int M=4520;//M是保存的文件数目@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) { // TODO Auto-generated method stub long startTime=System.currentTimeMillis();try { //若是当前class类文件目录则用下面的getClass()。getRessource()代码//File new_file=new File(new File(new test()。getClass()。getResource("")。toURI()),"combinedFile.txt");File new_file=new File("/home/han/Desktop","combinedFile.txt");//定义合并后的文件的保存路径FileWriter fw = new FileWriter(new_file);BufferedWriter bfw=new BufferedWriter(fw);List row=new LinkedList();int num_line = 0;for(int i=0;i<M;i++){ List column=new LinkedList();/*获得每个离散采样文件的名称*/ String filename="00"+i;//"00"是保存的文件的前缀int temLength=filename.length();for(int j=0;j<N-temLength;j++){ filename="0"+filename;} filename=filename+".txt";
//定义存放离散采样数据文件的文件夹File file=new File("/home/han/Ubuntu One/apresmidi sans pola",filename);//File file=new File(new File(new test()。getClass()。getResource("")。toURI()),filename);FileReader fr=new FileReader(file);BufferedReader bfr=new BufferedReader(fr);
String s=null;while((s=bfr.readLine())!=null){ s=s.replace(,, .);if(i==0){ column.add(s);}else{ String[] sArray=s.split(" ");column.add(sArray[1]);} row.add(column);num_line=column.size();bfr.close();fr.close();} System.out.println("Files are all viewed");for(int i=0;i<num_line;i++){ Iterator it=row.iterator();while(it.hasNext()){ List tempList=(List)it.next();bfw.write((String)tempList.get(i));bfw.write(" ");} /*for(int j=0;j<row.size();j++){ List tempList=(List)row.get(j);bfw.write((String)tempList.get(i));bfw.write(" ");} */ bfw.newLine();} bfw.close();fw.close();System.out.println("OK");} catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace();} long endTime=System.currentTimeMillis();System.out.println("耗费时间: "+(endTime-startTime)+" ms");}
运行结果:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.ArrayList.subList(ArrayList.java:915)
at java.lang.String.split(String.java:2359)
at java.lang.String.split(String.java:2403)
at com.han.CombinedTextSpectres.main(CombinedTextSpectres.java:64)
2. 若减少要合并的离散数据文件个数,上面的程序是可以成功运行的。为方便读者,还是把数据源和代码分别列出如下:
=>下载本程序所用到的存放离散采样数据文件的文件夹view plain package com.han;
import java.io.*;import java.util.*;
/** * 以我实验室的频谱测量仪(spectrometer)对激光所获的离散采样为背景。
* <p> * 本程序是属于数据处理程序,用JAVA所实现。
* <p> * spectrometer中光栅的每次采样所获得的数据被保存在一个文本文件中,* 分为2列:第一列为波长值,第二列为对应的光强值。
* 第1次采样文本文件被命名为“000000.txt”,第二个为“000001.txt”,依次类推。
* 比如若让spectrometer采样3mins,大概会出现4000次采样,* 被保存的文件就为000000.txt~003999.txt * <p> * 此程序使用的是二维LinkedList类型来拉取数据。
* LinkedList:就是采用链表结构保存对象。
* PS:JAVA中集合类分为List,Set,和Map. * List又有LinkedList和ArrayList;* Set分为HashSet和TreeSet;* Map分为HashMap和TreeMap. * 顾名思义,Hash就是由哈希表提供支持,Tree树结构提供了排序和顺序输出的功能。
* @author han * */ public class CombinedTextSpectres { static final int N=6;//N是保存的文件名称的长度static final int M=357;//M是保存的文件数目@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) { // TODO Auto-generated method stub long startTime=System.currentTimeMillis();try { //若是当前class类文件目录则用下面的getClass()。getRessource()代码//File new_file=new File(new File(new test()。getClass()。getResource("")。toURI()),"combinedFile.txt");File new_file=new File("/home/han/Desktop","combinedFile.txt");//定义合并后的文件的保存路径FileWriter fw = new FileWriter(new_file);BufferedWriter bfw=new BufferedWriter(fw);List row=new LinkedList();int num_line = 0;for(int i=0;i<M;i++){ List column=new LinkedList();/*获得每个离散采样文件的名称*/ String filename="00"+i;//"00"是保存的文件的前缀int temLength=filename.length();for(int j=0;j<N-temLength;j++){ filename="0"+filename;} filename=filename+".txt";
//定义存放离散采样数据文件的文件夹File file=new File("/home/han/Ubuntu One/spectre sans pola",filename);//File file=new File(new File(new test()。getClass()。getResource("")。toURI()),filename);FileReader fr=new FileReader(file);BufferedReader bfr=new BufferedReader(fr);
String s=null;while((s=bfr.readLine())!=null){ s=s.replace(,, .);if(i==0){ column.add(s);}else{ String[] sArray=s.split(" ");column.add(sArray[1]);} row.add(column);num_line=column.size();bfr.close();fr.close();} System.out.println("Files are all viewed");for(int i=0;i<num_line;i++){ Iterator it=row.iterator();while(it.hasNext()){ List tempList=(List)it.next();bfw.write((String)tempList.get(i));bfw.write(" ");} /*for(int j=0;j<row.size();j++){ List tempList=(List)row.get(j);bfw.write((String)tempList.get(i));bfw.write(" ");} */ bfw.newLine();} bfw.close();fw.close();System.out.println("OK");} catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace();} long endTime=System.currentTimeMillis();System.out.println("耗费时间: "+(endTime-startTime)+" ms");}
运行结果:Files are all viewed OK耗费时间: 14880 ms附: 运行结果即合并后的单个文件
3. 下面的方法一样的使用二维LinkedList类型来读取数据。 但是在重新写入到单个合并文件时使用了StringBuilder, 经测试,效率和CombinedTextSpectres(第一版)差不多。
但是依然对于大数据如超过2000个文件要合并时则出现内存泄漏。
=>下载本程序所用到的存放离散采样数据文件的文件夹
view plain package com.han;
import java.io.*;import java.util.*;
/** * 此方法一样的使用二维LinkedList类型来读取数据。
* 但是在重新写入到单个合并文件时使用了StringBuilder,* 经测试,效率和CombinedTextSpectres(第一版)差不多。
* <p> * 但是依然对于大数据如超过2000个文件要合并时则出现内存泄漏。
* @author han * */ public class CombinedTextSpectres_2 { static final int N=6;//N是保存的文件名称的长度static final int M=357;//M是保存的文件数目@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) { // TODO Auto-generated method stub long startTime=System.currentTimeMillis();try { //若是当前class类文件目录则用下面的getClass()。getRessource()代码//File new_file=new File(new File(new test()。getClass()。getResource("")。toURI()),"combinedFile.txt");File new_file=new File("/home/han/Desktop","combinedFile.txt");FileWriter fw = new FileWriter(new_file);BufferedWriter bfw=new BufferedWriter(fw);List row=new LinkedList();int num_line = 0;for(int i=0;i<M;i++){ List column=new LinkedList();/*获得每个离散采样文件的名称*/ String filename="00"+i;//"00"是保存的文件的前缀int temLength=filename.length();for(int j=0;j<N-temLength;j++){ filename="0"+filename;} filename=filename+".txt";//定义存放离散采样数据文件的文件夹File file=new File("/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/10_10-14_10/spectre sans pola",filename);//File file=new File(new File(new test()。getClass()。getResource("")。toURI()),filename);FileReader fr=new FileReader(file);BufferedReader bfr=new BufferedReader(fr);
String s=null;while((s=bfr.readLine())!=null){ s=s.replace(,, .);if(i==0){ column.add(s);}else{ String[] sArray=s.split(" ");column.add(sArray[1]);} row.add(column);num_line=column.size();bfr.close();fr.close();} System.out.println("Files are all viewed");StringBuilder sb=new StringBuilder("");for(int i=0;i<num_line;i++){ Iterator it=row.iterator();while(it.hasNext()){ List tempList=(List)it.next();sb.append((String)tempList.get(i));sb.append(" ");} //以下这种应用。size()的遍历方法在大循环的