4

我的应用程序中有以下代码,它做了两件事:

解析具有“n”个数据的文件。

对于文件中的每个数据,将有两个 Web 服务调用。

 public static List<String> parseFile(String fileName) {
   List<String> idList = new ArrayList<String>();
   try {
     BufferedReader cfgFile = new BufferedReader(new FileReader(new File(fileName)));
     String line = null;
     cfgFile.readLine();
     while ((line = cfgFile.readLine()) != null) {
       if (!line.trim().equals("")) {
         String [] fields = line.split("\\|"); 
         idList.add(fields[0]);
       } 
     } 
     cfgFile.close();
   } catch (IOException e) {
     System.out.println(e+" Unexpected File IO Error.");
   }
 return idList;
}

当我尝试解析具有 100 万行记录的文件时,java 进程在处理一定数量的数据后失败。我有java.lang.OutOfMemoryError: Java heap space错误。我可以部分地弄清楚由于提供了如此庞大的数据,java进程停止了。请建议我如何处理这些庞大的数据。

编辑:这部分代码是否会new BufferedReader(new FileReader(new File(fileName)));解析整个文件并受到文件大小的影响。

4

3 回答 3

3

您遇到的问题是您正在累积列表中的所有数据。解决此问题的最佳方法是以流媒体方式进行。这意味着不要累积列表中的所有 id,而是在每一行上调用您的 Web 服务或累积一个较小的缓冲区,然后进行调用。

打开文件并创建 BufferedReader 不会影响内存消耗,因为文件中的字节将被逐行读取(或多或少)。问题在于代码中的这一点,idList.add(fields[0]);当您不断将所有文件数据累积到其中时,列表将与文件一样大。

您的代码应该执行以下操作:

 while ((line = cfgFile.readLine()) != null) {
   if (!line.trim().equals("")) {
     String [] fields = line.split("\\|"); 
     callToRemoteWebService(fields[0]);
   } 
 } 
于 2012-09-28T14:26:47.023 回答
2

使用 -Xms 和 -Xmx 选项增加 Java 堆内存大小。如果未明确设置,jvm 会将堆大小设置为符合人体工程学的默认值,在您的情况下这还不够。阅读本文以了解有关在 jvm 中调整内存的更多信息:http ://www.oracle.com/technetwork/java/javase/tech/memorymanagement-whitepaper-1-150020.pdf

编辑:以生产者 - 消费者方式执行此操作以利用并行处理的替代方法。一般的想法是创建一个生产者线程来读取文件并将任务排入队列以进行处理,并创建一个消费它们的消费者线程。一个非常一般的想法(用于说明目的)如下:

// blocking queue holding the tasks to be executed
final SynchronousQueue<Callable<String[]> queue = // ...

// reads the file and submit tasks for processing
final Runnable producer = new Runnable() {
  public void run() {
     BufferedReader in = null;
     try {
         in = new BufferedReader(new FileReader(new File(fileName)));
         String line = null;
         while ((line = file.readLine()) != null) {
             if (!line.trim().equals("")) {
                 String[] fields = line.split("\\|"); 
                 // this will block if there are not available consumer threads to process it...
                 queue.put(new Callable<Void>() {
                     public Void call() {
                         process(fields);
                     }
                  });
              } 
          }
     } catch (InterruptedException e) {
         Thread.currentThread().interrupt());
     } finally {
         // close the buffered reader here...
     }
  }
}

// Consumes the tasks submitted from the producer. Consumers can be pooled
// for parallel processing.
final Runnable consumer = new Runnable() {
  public void run() {
    try {
        while (true) {
            // this method blocks if there are no items left for processing in the queue...
            Callable<Void> task = queue.take();
            taks.call();
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
  }
}

当然,您必须编写代码来管理消费者和生产者线程的生命周期。正确的方法是使用 Executor 来实现它。

于 2012-09-28T14:36:14.793 回答
1

当您想使用大数据时,您有 2 个选择:

  1. 使用足够大的堆来容纳所有数据。这将“工作”一段时间,但如果你的数据大小是无限的,它最终会失败。
  2. 以增量方式处理数据。任何时候仅将部分数据(有限大小)保留在内存中。这是理想的解决方案,因为它可以扩展到任何数量的数据。
于 2012-09-28T14:26:32.863 回答