3

Stockファイル(約100MB)から大量の株式データ履歴をロードするクラスがあります。2つのオブジェクトを受け取り、2つの間の統計的関係を計算して、結果をファイルに書き込むクラスPairがあります。Stock

私の主な方法では、株のペアのリスト(約500)を通過するループがあります。2つのストックオブジェクトを作成し、次に2つのオブジェクトからペアオブジェクトを作成します。この時点で、ペアの計算がファイルに書き込まれ、オブジェクトの処理が完了しました。次の計算に進むことができるように、メモリを解放する必要があります。

3つのオブジェクトをnullに設定することに加えて、ループの最後に次の2行を追加しました。

GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();

これらの2行をステップオーバーすると、ループの反復ごとに割り当てられる200〜300MBから50MBが解放されるように見えます(タスクマネージャーから表示)。

プログラムは、システムのメモリ不足例外を発生させる前に、約8または10ペアを実行します。メモリ使用量は、約1.5GBでクラッシュするまで着実に増加します。(これはWin7Ultimateを実行している8GBのマシンです)

ガベージコレクションの経験はあまりありません。私は何か間違ったことをしていますか?

あなたが尋ねたので私のコードはここにあります:(注:プログラムには2つのモードがあります、1>新しいペアがシステムに追加される追加モード。2>イベントに基づいてペアファイルをリアルタイムで更新する通常モードfilesystemwatcher。株式データは外部アプリによって更新されますQCollectorと呼ばれます。)

MainFormこれは、追加モードで実行されるセグメントです。

foreach (string line in PairList)
{
    string[] tokens = line.Split(',');

    stockA = new Stock(QCollectorPath, tokens[0].ToUpper()); 
    stockB = new Stock(QCollectorPath, tokens[1].ToUpper()); 

    double ratio = double.Parse(tokens[2]);
    Pair p = new Pair(QCollectorPath, stockA, stockB, ratio);

    // at this point the pair is written to file (constructor handles this)        

    // commenting out the following lines of code since they don't fix the problem
    // stockA = null;
    // stockB = null;
    // p = null;

    // refraining from forced collection since that's not the problem
    // GC.Collect(GC.MaxGeneration);
    // GC.WaitForPendingFinalizers();

    // so far this is the only way i can fix the problem by setting the pair classes
    // references to StockA and StockB to null
    p.Kill();
}

リクエストに応じてコードを追加しています:StockPairはのサブクラスでTimeSeriesあり、共通の機能を備えています

public abstract class TimeSeries {
     protected List<string> data;

     // following create class must be implemented by subclasses (stock, pair, etc...)
     // as each class is created differently, although their data formatting is identical
     protected void List<string> Create();

     // . . . 

     public void LoadFromFile()
     {
          data = new List<string>();

          List<StreamReader> srs = GetAllFiles();

          foreach (StreamReader sr in srs)
          {
               List<string> temp = new List<string>();
               temp = TurnFileIntoListString(sr);
               data = new List<string>(temp.Concat(data));
               sr.Close()
          }
     }

     // uses directory naming scheme (according to data month/year) to find files of a symbol
     protected List<StreamReader> GetAllFiles()...

     public static List<string> TurnFileIntoListString(StreamReader sr)
     {
          List<string> list = new List<string>();
          string line;
          while ((line = sr.ReadLine()) != null)
               list.Add(line);
          return list;
     }

     // this is the only mean to access a TimeSeries object's data
     // this is to prevent deadlocks by time consuming methods such as pair's Create

     public string[] GetListCopy()
     {
          lock (data)
          {
               string[] listCopy = new string[data.count];
               data.CopyTo(listCopy);
               return listCopy();
          }
     }
}

public class Stock : TimeSeries
{
     public Stock(string dataFilePath, string symbol, FileSystemWatcher fsw = null)
     {
          DataFilePath = dataFilePath;
          Name = symbol.ToUpper();
          LoadFromFile();
          // to update stock data when external app updates the files
          if (fsw != null) fsw.Changed += FileSystemEventHandler(fsw_Changed);
     }

     protected void List<string> Create()
     {
          // stock files created by external application
     }


     // . . . 
}

public class Pair : TimeSeries {
     public Pair(string dataFilePath, Stock stockA, Stock stockB, double ratio)
     {
          // assign parameters to local members
          // ...         

          if (FileExists())
               LoadFromFile();
          else
             Create();
     }

     protected override List<string> Create()
     {
          // since stock can get updated by fileSystemWatcher's event handler
          // a copy is obtained from the stock object's data 
          string[] listA = StockA.GetListCopy();
          string[] listB = StockB.GetListCopy();
          List<string> listP = new List<string>();

          int i, j;
          i = GetFirstValidBar(listA);
          j = GetFirstValidBar(listB);
          DateTime dtA, dtB;

          dtA = GetDateTime(listA[i]);
          dtB = GetDateTime(listB[j]);

          // this hidden segment adjusts i and j until they are starting at same datetime
          // since stocks can have different amount of data

          while (i < listA.Count() && j < listB.Count)
          {
              double priceA = GetPrice(listA[i]);
              double priceB = GetPrice(listB[j]);
              double priceP = priceA * ratio - priceB;
              listP.Add(String.Format("{0},{1:0.00},{2:0.00},{3:0.00}"
                   , dtA
                   , priceP
                   , priceA
                   , priceB
              );
              if (i < j)
                   i++;
              else if (j < i)
                   j++;
              else
              {
                   i++; 
                   j++;
              }
          }
     }

     public void Kill()
     {
         data = null;
         stockA = null;
         stockB = null;
     }
 }
4

1 に答える 1

3

あなたのメモリリークはここにあります:

if (fsw != null) fsw.Changed += FileSystemEventHandler(fsw_Changed);

ストック オブジェクトのインスタンスは、FileSystemWatcher のイベントに応答しているため、FileSystemWatcher が動作している限りメモリに保持されます。

そのイベントを別の場所に実装するか、コードの別の場所に次を追加する必要があると思います。

if (fsw != null) fsw.Changed -= fsw_Changed;

コードの書き方によっては、一括処理が行われる場合に FileSystemWatcher なしでストック オブジェクトを呼び出すことを意図している可能性があります。

あなたが投稿した元のコードでは、Stock クラスのコンストラクターが FileSystemWatcher で呼び出されていました。あなたは今それを変えました。null FileSystemWatcher を使用すると、キルを削除でき、fsw.Changed を聞いていないため、リークが発生しないことがわかると思います。

于 2012-07-25T01:02:53.713 に答える