4

私のアプリケーションには、2つのことを行う次のコードがあります。

'n'個のデータを持つファイルを解析します。

ファイル内のデータごとに、2つの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

編集:並列処理を活用するための生産者/消費者の方法でこれを行う別の方法。一般的な考え方は、ファイルを読み取り、処理のためにタスクをキューに入れるプロデューサースレッドと、それらを消費するn個のコンシューマースレッドを作成することです。非常に一般的な考え方(説明のため)は次のとおりです。

// 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 に答える