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树结构提供了排序和顺序输出的功能。
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("\t");
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("\t");
}
/*for(int j=0;j<row.size();j++){
List tempList=(List)row.get(j);
bfw.write((String)tempList.get(i));
bfw.write("\t");
} */
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. 若减少要合并的离散数据文件个数,上面的程序是可以成功运行的。为方便读者,还是把数据源和代码分别列出如下:
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("\t");
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("\t");
}
/*for(int j=0;j<row.size();j++){
List tempList=(List)row.get(j);
bfw.write((String)tempList.get(i));
bfw.write("\t");
} */
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个文件要合并时则出现内存泄漏。
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("\t");
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("\t");
}
//以下这种应用.size()的遍历方法在大循环的时候效率要略低于上面的.iterator()方法
/*for(int j=0;j<row.size();j++){
List tempList=(List)row.get(j);
bfw.write((String)tempList.get(i));
bfw.write("\t");
} */
sb.append("\n");
}
bfw.write(sb.toString());
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
耗费时间: 15155 ms
4. 下面的方法一样的使用经典固定长度数组,以及在重新写入到单个合并文件时使用了StringBuilder (其实因为BufferedWriter也是缓存,所以有无SringBuilder效率一样)。 经测试,效率比CombinedTextSpectres(第一版) 和CombinedTextSpectres_2(第二版)都要高出10倍!。
但是依然对于大数据如超过2000个文件要合并时则出现内存泄漏。
package com.han;
import java.io.*;
/**
* 此方法一样的使用经典固定长度数组,以及在重新写入到单个合并文件时使用了StringBuilder
* (其实因为BufferedWriter也是缓存,所以有无SringBuilder效率一样)。
* 经测试,效率比CombinedTextSpectres(第一版)
* 和CombinedTextSpectres_2(第二版)都要高出10倍!。
* <p>
* 但是依然对于大数据如超过2000个文件要合并时则出现内存泄漏。
* @author han
*
*/
public class CombinedTextSpectres_3 {
//此方法使用自动探测单个文件的行数(即spectrometre的采样点数),无SringBuilder
static final int N=6;//N是保存的文件名称的长度
/***貌似由于.split()函数的影响,内存泄漏在文件数超过1800时***/
static final int M=357;//M是保存的文件数目
/****************************************************/
public static void main(String[] args) {
// TODO Auto-generated method stub
long startTime=System.currentTimeMillis();
try {
//定义存放离散采样数据文件的文件夹
String dirPath="/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/10_10-14_10/spectre sans pola";
File file_temp=new File(dirPath,"000000.txt");
FileReader fr_temp=new FileReader(file_temp);
BufferedReader bfr_temp=new BufferedReader(fr_temp);
int NLine = 0;//自动探测单个文件的行数(即spectrometre的采样点数)
@SuppressWarnings("unused")
String s_temp=null;
while((s_temp=bfr_temp.readLine())!=null){
NLine++;
}
bfr_temp.close();
fr_temp.close();
String[][] CombinedArray=new String[NLine][M+1];
File new_file=new File("/home/han/Desktop","combinedFile.txt");
//若是当前class类文件目录则用下面的getClass().getRessource()代码
//File new_file=new File(new File(new test().getClass().getResource("").toURI()),"combinedFile.txt");
FileWriter fw = new FileWriter(new_file);
BufferedWriter bfw=new BufferedWriter(fw);
for(int i=0;i<M;i++){
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(dirPath,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;
int num_line = 0;
while((s=bfr.readLine())!=null){
s=s.replace(',', '.');
String[] sArray=s.split("\t");
if(i==0){
CombinedArray[num_line][0]=sArray[0];
CombinedArray[num_line][1]=sArray[1];
}else{
CombinedArray[num_line][i+1]=sArray[1];
}
num_line++;
}
bfr.close();
fr.close();
}
System.out.println("Files are all viewed");
for(int i=0;i<NLine;i++){
for(int j=0;j<M+1;j++){
bfw.write(CombinedArray[i][j]);
bfw.write("\t");
}
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
耗费时间: 1802 ms
5. 利用了递归的手段,将5000左右txt文档分批调入内存进行处理, 处理一部分就释放一部分资源。这样不管是5000个数据文件还是7000,9000都没问题。 试验测得我的情况是如果不进行分批处理而是一次性调入内存在合并文档的话, 那最大文件数只能是2000,就是超过2000个数据文件就会出现 java.lang.OutOfMemoryError: Java heap space这样的错误。
代码中level变量就是每次分批处理的数目,本程序设为1000。
此方法使用自动探测单个文件的行数(即spectrometre的采样点数), 因为BufferedWriter也是缓存,所以有无SringBuilder效率差不多, 本程序因此写入到单个合并文件时没有使用StringBuilder。 但是写的时候也向系统申请了String[][] CombinedArray=new String[2048][Column]; 其中Column为批处理次数,比如3或4。由此可见虽然载入的还是“整个数据”, 但申请的String二维数组只有String[2048][4],系统处理速度和内存占用却大大改善!
package com.han;
import java.io.*;
/**
* 利用了递归的手段,将5000左右txt文档分批调入内存进行处理,
* 处理一部分就释放一部分资源。这样不管是5000个数据文件还是7000,9000都没问题。
* 试验测得我的情况是如果不进行分批处理而是一次性调入内存在合并文档的话,
* 那最大文件数只能是2000,就是超过2000个数据文件就会出现
* java.lang.OutOfMemoryError: Java heap space这样的错误。
* <p>
* 代码中level变量就是每次分批处理的数目,本程序设为1000。
* <p>
* 此方法使用自动探测单个文件的行数(即spectrometre的采样点数),
* 因为BufferedWriter也是缓存,所以有无SringBuilder效率差不多,
* 本程序因此写入到单个合并文件时没有使用StringBuilder。
* 但是写的时候也向系统申请了String[][] CombinedArray=new String[2048][Column];
* 其中Column为批处理次数,比如3或4。由此可见虽然载入的还是“整个数据”,
* 但申请的String二维数组只有String[2048][4],系统处理速度和内存占用却大大改善!
* @author han
*
*/
public class CombinedTextSpectres_4 {
static int N=6;//N是保存的文件名称的长度
static int M=4520;//M是保存的文件数目
int level=1000;//每次分批处理的数目
int filenumBegin;//每批处理时的第一个文件名
int filenumEnd;//每批处理时的最后一个文件名
String dirPath="/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola";//定义存放离散采样数据文件的文件夹
static File tempFile; //定义临时文件夹来存放每批合并后的结果文件
static String destDirPath="/home/han/Desktop";//定义存放最终结果文件的文件夹
/**
* the construct function
*/
public CombinedTextSpectres_4(){
try {
combine(M,0);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @param num 剩余待处理的文件数
* @param loop 递归的次数
* @throws IOException
*/
void combine(int num, int loop) throws IOException{
if(num>level){
//如果剩余的文件数还是大于设定的level=1000的话,则继续往下递归
combine(num-level,loop+1);
}
filenumBegin=loop*level;
if(num<=level){
filenumEnd=filenumBegin+num;
}else{
filenumEnd=filenumBegin+level;
}
//定义经典固定长度数组,因为比集合等动态数组效率要高。
//并且此时也可以保证String[][] CombinedArray不会
//超出内存,因为每次处理的不会超过level=1000
String[][] CombinedArray=new String[2048][filenumEnd-filenumBegin];
for(int i=filenumBegin;i<filenumEnd;i++){
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(dirPath,filename);
FileReader fr=new FileReader(file);
BufferedReader bfr=new BufferedReader(fr);
int num_line = 0;
String s=null;
while((s=bfr.readLine())!=null){
if(i==0){
CombinedArray[num_line][0]=s.replace(',', '.');
}else{
String[] sArray=s.split("\t");
CombinedArray[num_line][i-filenumBegin]=sArray[1].replace(',', '.');
}
num_line++;
}
bfr.close();
fr.close();
}
//System.out.println("Files are all viewed");
/*建立临时文件夹存放每批合并后的结果文件*/
String filename=loop+".txt";
tempFile=new File(dirPath,"temp");
if(!tempFile.exists()){
tempFile.mkdir();
}
File new_file=new File(tempFile,filename);
FileWriter fw = new FileWriter(new_file);
BufferedWriter bfw=new BufferedWriter(fw);
for(int i=0;i<2048;i++){
for(int j=0;j<filenumEnd-filenumBegin;j++){
bfw.write(CombinedArray[i][j]);
bfw.write("\t");
}
bfw.newLine();
}
CombinedArray=null;//让JAVA垃圾自动回收装置认为此CombinedArray变量可以回收
bfw.close();
fw.close();
System.out.println(new_file);
System.out.println("OK");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
long startTime=System.currentTimeMillis();
new CombinedTextSpectres_4();
/*再把中间临时文件都合并成最终的单个文件*/
File[] FileArray=tempFile.listFiles();
int Column=FileArray.length;
String[][] CombinedArray=new String[2048][Column];
try {
for(int i=0;i<Column;i++){
FileReader fr=new FileReader(FileArray[i]);
BufferedReader bfr=new BufferedReader(fr);
int num_line = 0;
String s=null;
while((s=bfr.readLine())!=null){
CombinedArray[num_line][i]=s;
num_line++;
}
bfr.close();
fr.close();
}
File new_file=new File(destDirPath,"CombinedSpectres.txt");
FileWriter fw = new FileWriter(new_file);
BufferedWriter bfw=new BufferedWriter(fw);
/***************加入实验条件**********************/
String ExperienceCondition="Integrated time is 1 ms, with the HR Spectrometer, with polarizorH, using the syringe Rh6G+Glycol and the other is Huile";
bfw.write(ExperienceCondition);
bfw.newLine();
/***************加入每列数值代表的物理含义***********/
bfw.write("Wavelength (nm)\t");
for(int i=0;i<M;i++){
String filename="00"+i;//"00"是保存的文件的前缀
int temLength=filename.length();
for(int j=0;j<N-temLength;j++){
filename="0"+filename;
}
filename=filename+".txt";
bfw.write(filename);
bfw.write("\t");
}
bfw.newLine();
/*****************写入全部数据********************/
for(int i=0;i<2048;i++){
for(int j=0;j<Column;j++){
bfw.write(CombinedArray[i][j]);
//bfw.write("\t");
}
bfw.newLine();
}
CombinedArray=null;
bfw.close();
fw.close();
System.out.println(new_file);
System.out.println("OK");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long endTime=System.currentTimeMillis();
System.out.println("耗费时间: "+(endTime-startTime)+" ms");
}
}
运行结果:/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola/temp/4.txt
OK
/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola/temp/3.txt
OK
/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola/temp/2.txt
OK
/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola/temp/1.txt
OK
/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola/temp/0.txt
OK
/home/han/Desktop/CombinedSpectres.txt
OK
耗费时间: 30842 ms
6. 但是考虑到上个程序最后写的时候也向系统申请了 String[][] CombinedArray=new String[2048][Column]; 其中Column为批处理次数,比如3或4。由此可见载入的还是“整个数据”, 虽然试验证明了系统处理速度和内存占用大大改善了,是可行的。但是, 在本程序中还是又采用了另一种方法:同时开通4个输入流分别指向4个临时文件, 写入最后的单个结果文件。效果证明也非常不错!
package com.han;
import java.io.*;
/**
* 利用了递归的手段,将5000左右txt文档分批调入内存进行处理,
* 处理一部分就释放一部分资源。这样不管是5000个数据文件还是7000,9000都没问题。
* 试验测得我的情况是如果不进行分批处理而是一次性调入内存在合并文档的话,
* 那最大文件数只能是2000,就是超过2000个数据文件就会出现
* java.lang.OutOfMemoryError: Java heap space这样的错误。
* <p>
* 代码中level变量就是每次分批处理的数目,本程序设为1000。
* <p>
* 此方法使用自动探测单个文件的行数(即spectrometre的采样点数),
* 因为BufferedWriter也是缓存,所以有无SringBuilder效率差不多,
* 本程序因此写入到单个合并文件时没有使用StringBuilder。
* 但是考虑到最后写的时候也向系统申请了
* String[][] CombinedArray=new String[2048][Column];
* 其中Column为批处理次数,比如3或4。由此可见载入的还是“整个数据”,
* 虽然试验证明了系统处理速度和内存占用大大改善了,是可行的。但是,
* 在本程序中还是又采用了另一种方法:同时开通4个输入流分别指向4个临时文件,
* 写入最后的单个结果文件。效果证明也非常不错!
* @author han
*
*/
public class CombinedTextSpectres_5 {
static int N=6;//N是保存的文件名称的长度
static int M=4520;//M是保存的文件数目
int level=1000;//每次分批处理的数目
int filenumBegin;//每批处理时的第一个文件名
int filenumEnd;//每批处理时的最后一个文件名
String dirPath="/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola";//定义存放离散采样数据文件的文件夹
static File tempFile; //定义临时文件夹来存放每批合并后的结果文件
static String destDirPath="/home/han/Desktop";//定义存放最终结果文件的文件夹
/**
* the construct function
*/
public CombinedTextSpectres_5(){
try {
combine(M,0);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @param num 剩余待处理的文件数
* @param loop 递归的次数
* @throws IOException
*/
void combine(int num, int loop) throws IOException{
if(num>level){
//如果剩余的文件数还是大于设定的level=1000的话,则继续往下递归
combine(num-level,loop+1);
}
filenumBegin=loop*level;
if(num<=level){
filenumEnd=filenumBegin+num;
}else{
filenumEnd=filenumBegin+level;
}
//定义经典固定长度数组,因为比集合等动态数组效率要高。
//并且此时也可以保证String[][] CombinedArray不会
//超出内存,因为每次处理的不会超过level=1000
String[][] CombinedArray=new String[2048][filenumEnd-filenumBegin];
for(int i=filenumBegin;i<filenumEnd;i++){
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(dirPath,filename);
FileReader fr=new FileReader(file);
BufferedReader bfr=new BufferedReader(fr);
int num_line = 0;
String s=null;
while((s=bfr.readLine())!=null){
if(i==0){
CombinedArray[num_line][0]=s.replace(',', '.');
}else{
String[] sArray=s.split("\t");
CombinedArray[num_line][i-filenumBegin]=sArray[1].replace(',', '.');
}
num_line++;
}
bfr.close();
fr.close();
}
//System.out.println("Files are all viewed");
//建立临时文件夹存放每批合并后的结果文件
String filename=loop+".txt";
tempFile=new File(dirPath,"temp");
if(!tempFile.exists()){
tempFile.mkdir();
}
File new_file=new File(tempFile,filename);
FileWriter fw = new FileWriter(new_file);
BufferedWriter bfw=new BufferedWriter(fw);
for(int i=0;i<2048;i++){
for(int j=0;j<filenumEnd-filenumBegin;j++){
bfw.write(CombinedArray[i][j]);
bfw.write("\t");
}
bfw.newLine();
}
CombinedArray=null;//让JAVA垃圾自动回收装置认为此CombinedArray变量可以回收
bfw.close();
fw.close();
System.out.println(new_file);
System.out.println("OK");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
long startTime=System.currentTimeMillis();
new CombinedTextSpectres_5();
try{
File new_file=new File(destDirPath,"CombinedSpectres.txt");
FileWriter fw = new FileWriter(new_file);
BufferedWriter bfw=new BufferedWriter(fw);
/***************加入实验条件**********************/
String ExperienceCondition="Integrated time is 1 ms, with the HR Spectrometer, with polarizorH, using the syringe Rh6G+Glycol and the other is Huile";
bfw.write(ExperienceCondition);
bfw.newLine();
/***************加入每列数值代表的物理含义***********/
bfw.write("Wavelength (nm)\t");
for(int i=0;i<M;i++){
String filename="00"+i;//"00"是保存的文件的前缀
int temLength=filename.length();
for(int j=0;j<N-temLength;j++){
filename="0"+filename;
}
filename=filename+".txt";
bfw.write(filename);
bfw.write("\t");
}
bfw.newLine();
/*****************写入全部数据********************/
//再把中间临时文件都合并成最终的单个文件
File[] FileArray=tempFile.listFiles();
int Column=FileArray.length;
FileReader[] fr=new FileReader[Column];
BufferedReader[] bfr=new BufferedReader[Column];
/*for(int i=0;i<Column;i++){
System.out.println(FileArray[i]);
}*/
for(int i=0;i<Column;i++){
fr[i]=new FileReader(FileArray[i]);
bfr[i]=new BufferedReader(fr[i]);
}
String s;
while((s=bfr[0].readLine())!=null){
for(int i=0;i<Column;i++){
if(i==0){
bfw.write(s);
}else{
bfw.write(bfr[i].readLine());
}
}
bfw.newLine();
}
for(int i=0;i<Column;i++){
bfr[i].close();
fr[i].close();
}
bfw.close();
fw.close();
System.out.println(new_file);
System.out.println("OK");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long endTime=System.currentTimeMillis();
System.out.println("耗费时间: "+(endTime-startTime)+" ms");
}
}
运行结果: /media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola/temp/4.txt
OK
/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola/temp/3.txt
OK
/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola/temp/2.txt
OK
/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola/temp/1.txt
OK
/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola/temp/0.txt
OK
/home/han/Desktop/CombinedSpectres.txt
OK
耗费时间: 28230 ms
到此,实验室中频谱数据处理就告一段落了。总结就是:改变算法非常重要。遇到问题,找到根源后,总会有改良的算法来解决它的。
******************************************************************************************************************************************************************************
不过,随着继续经验的积累,我发现全部使用流操作是可以的,即:用约5000个输入流同时分别指向每一个要合并的文件,然后用一个输出流指向合并结果文件。
虽然也许会遇到同时打开的流数限制(发现在Linux下是这样的,Windows尚未发现),但是可以再通过分批处理的思想来解决的。
(
)在本程序中又采用了另一种方法:同时开通N个输入流分别指向N个要合并的数据文件, 写入最后的单个结果文件。效果证明运行时间差不多,但是占用内存更少。
PS: 但是发现了一个Linux下特有的问题(因为此问题在Windows下不存在): 当文件读到大约第4092个时,抛出了“文件找不到“的异常。 所以断定同时打开输入流的数目在Linux下此时被限制为4092。 所以此时必须分批来处理,每批处理数目自动设为能够同时打开的最大流数目 (实际发现是输入流和输出流的总数,而此数字可以通过抛出的异常信息获得)。
总结:这样此程序即可以在Linux中运行,又可以在Windows中运行,就是做到了真正的跨平台!
代码如下:
package com.han;
import java.io.*;
/**
* 在本程序中又采用了另一种方法:同时开通N个输入流分别指向N个要合并的数据文件,
* 写入最后的单个结果文件。效果证明运行时间差不多,但是占用内存更少。
* <p>
* PS: 但是发现了一个Linux下特有的问题(因为此问题在Windows下不存在):
* 当文件读到大约第4092个时,抛出了“文件找不到“的异常。
* 所以断定同时打开输入流的数目在Linux下此时被限制为4092。
* 所以此时必须分批来处理,每批处理数目自动设为能够同时打开的最大流数目
* (实际发现是输入流和输出流的总数,而此数字可以通过抛出的异常信息获得)。
* <p>
* 总结:这样此程序即可以在Linux中运行,又可以在Windows中运行,就是做到了真正的跨平台!
* @author han
*
*/
public class CombinedTextSpectres6 {
private final int N=6;//N是保存的文件名称的长度
private final int M=4520;//M是保存的文件数目
String ExperienceCondition="Integrated time is 1 ms, with the HR Spectrometer, with polarizorH, using the syringe Rh6G+Glycol and the other is Huile";
String dirPath="/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/17_10-21_10/apresmidi sans pola";//定义存放离散采样数据文件的文件夹
// String dirPath="/media/96D0265ED0264539/Users/HAN/Documents/Thèse ISMO/DonneesLabo/10_10-14_10/spectre sans pola";
static String destDirPath="/home/han/Desktop";//定义存放最终结果文件的文件夹
File new_file;
File[] FileArray;
int Column;
FileReader[] fr;
BufferedReader[] bfr;
FileWriter fw;
BufferedWriter bfw;
/**
* the construct function
* @throws IOException
*/
public CombinedTextSpectres6() throws IOException{
long startTime=System.currentTimeMillis();
try{
/*****************写入全部数据********************/
FileArray=new File(dirPath).listFiles();
Column=FileArray.length;// 4000 limit
fr=new FileReader[Column];//声明
bfr=new BufferedReader[Column];
/*for(int i=0;i<Column;i++){
System.out.println(FileArray[i]);
}*/
for(int i=0;i<Column;i++){//初始化
fr[i]=new FileReader(FileArray[i]);
bfr[i]=new BufferedReader(fr[i]);
}
new_file=new File(destDirPath,"CombinedSpectres.txt");
fw = new FileWriter(new_file);
bfw=new BufferedWriter(fw);
/***************加入实验条件**********************/
bfw.write(ExperienceCondition);
bfw.newLine();
/***************加入每列数值代表的物理含义***********/
bfw.write("Wavelength (nm)\t");
for(int i=0;i<M;i++){
String filename="00"+i;//"00"是保存的文件的前缀
int temLength=filename.length();
for(int j=0;j<N-temLength;j++){
filename="0"+filename;
}
filename=filename+".txt";
bfw.write(filename);
bfw.write("\t");
}
bfw.newLine();
String s;
while((s=bfr[0].readLine())!=null){
for(int i=0;i<Column;i++){
if(i==0){
bfw.write(s.replace(',', '.'));
bfw.write("\t");
}else{
bfw.write(bfr[i].readLine().split("\t")[1].replace(',', '.'));
bfw.write("\t");
}
}
bfw.newLine();
}
for(int i=0;i<Column;i++){
bfr[i].close();
fr[i].close();
}
bfw.close();
fw.close();
System.out.println(new_file);
System.out.println("OK");
} catch (Exception e) {
// TODO Auto-generated catch block
String strFile=e.getMessage();
String fileName=strFile.substring(dirPath.length()+1, dirPath.length()+7);
int maxNumFile=Integer.parseInt(fileName);
maxNumFile=maxNumFile-6;//for "bfr[i].close();" successful
/*System.out.println(fileName);
System.out.println(maxNumFile);*/
for(int i=0;i<maxNumFile;i++){ //close all input streams opened for the aims of releasing the resources.
bfr[i].close();
fr[i].close();
}
System.out.printf("The maximum opened file number limited by the current computer environment is : %d\n",maxNumFile-1);
System.out.println("So we have to retreat the problem by considering the limitation of the computer resources.");
new LinuxLimit(maxNumFile-1).run();
}
long endTime=System.currentTimeMillis();
System.out.println("耗费时间: "+(endTime-startTime)+" ms");
}
class LinuxLimit{
private final int maxNumFile;
File tempDir=new File(destDirPath,"temp");
LinuxLimit(int maxNumFile){
this.maxNumFile=maxNumFile;
}
void run(){
int fileStart=0;
int fileEnd=0;
int num=0;
String filename=num+".txt";
if(!tempDir.exists()){
tempDir.mkdir();
}
Column=maxNumFile;
try{
while(Column>=maxNumFile){
fileEnd=fileStart+maxNumFile;
fw=new FileWriter(new File(tempDir,filename));
bfw=new BufferedWriter(fw);
System.out.println(fileStart);
System.out.println(fileEnd);
for(int i=fileStart;i<fileEnd;i++){//初始化
fr[i-fileStart]=new FileReader(FileArray[i]);
bfr[i-fileStart]=new BufferedReader(fr[i-fileStart]);
}
String s;
while((s=bfr[0].readLine())!=null){
for(int i=0;i<maxNumFile;i++){
if(i==0 && num==0){
bfw.write(s.replace(',', '.'));
bfw.write("\t");
}else if(i==0 && num!=0){
bfw.write(s.split("\t")[1].replace(',', '.'));
bfw.write("\t");
}else if(i!=0){
bfw.write(bfr[i].readLine().split("\t")[1].replace(',', '.'));
bfw.write("\t");
}
}
bfw.newLine();
}
for(int i=0;i<maxNumFile;i++){
bfr[i].close();
fr[i].close();
}
bfw.close();
fw.close();
fileStart=fileEnd;
Column=M-fileEnd;
num++;
filename=num+".txt";
}
if(Column!=0){
fw=new FileWriter(new File(tempDir,filename));
bfw=new BufferedWriter(fw);
for(int i=fileStart;i<M;i++){//初始化 M=fileStart+Column
fr[i-fileStart]=new FileReader(FileArray[i]);
bfr[i-fileStart]=new BufferedReader(fr[i-fileStart]);
}
String s;
while((s=bfr[0].readLine())!=null){
for(int i=0;i<Column;i++){
if(i==0){
bfw.write(s.split("\t")[1].replace(',', '.'));
bfw.write("\t");
}else{
bfw.write(bfr[i].readLine().split("\t")[1].replace(',', '.'));
bfw.write("\t");
}
}
bfw.newLine();
}
for(int i=0;i<Column;i++){
bfr[i].close();
fr[i].close();
}
bfw.close();
fw.close();
}
/*****************************/
FileArray=tempDir.listFiles();
Column=FileArray.length;
fr=new FileReader[Column];//声明
bfr=new BufferedReader[Column];
new_file=new File(destDirPath,"CombinedSpectres.txt");
fw = new FileWriter(new_file);
bfw=new BufferedWriter(fw);
for(int i=0;i<Column;i++){
System.out.println(FileArray[i]);
}
for(int i=0;i<Column;i++){//初始化
fr[i]=new FileReader(FileArray[i]);
bfr[i]=new BufferedReader(fr[i]);
}
/***************加入实验条件**********************/
bfw.write(ExperienceCondition);
bfw.newLine();
/***************加入每列数值代表的物理含义***********/
bfw.write("Wavelength (nm)\t");
for(int i=0;i<M;i++){
String fname="00"+i;//"00"是保存的文件的前缀
int temLength=fname.length();
for(int j=0;j<N-temLength;j++){
fname="0"+fname;
}
fname=fname+".txt";
bfw.write(fname);
bfw.write("\t");
}
bfw.newLine();
String s;
while((s=bfr[0].readLine())!=null){
for(int i=0;i<Column;i++){
if(i==0){
bfw.write(s);
bfw.write("\t");
}else{
bfw.write(bfr[i].readLine());
bfw.write("\t");
}
}
bfw.newLine();
}
for(int i=0;i<Column;i++){
bfr[i].close();
fr[i].close();
}
bfw.close();
fw.close();
for(File e:FileArray){
e.delete();
}
tempDir.delete();
System.out.println("The temp folder has been deleted.");
}catch(Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
new CombinedTextSpectres6();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:The maximum opened file number limited by the current computer environment is : 4091
So we have to retreat the problem by considering the limitation of the computer resources.
0
4091
/home/han/Desktop/temp/0.txt
/home/han/Desktop/temp/1.txt
The temp folder has been deleted.
耗费时间: 17582 ms
程序中所要用到的数据文件以及合并后的结果文件都和前面是相同的。