2012-07-24 2 views
3

파일 (약 100 MB)에서 많은 주식 데이터 기록을로드하는 Stock 클래스가 있습니다. 나는 두 개의 Stock 오브젝트를 취하고이 둘 사이의 통계적 관계를 계산 한 후 파일에 결과를 쓰는 Pair 클래스를 가지고 있습니다.감지 할 수없는 메모리 누수

내 주요 방법에는 주식 쌍 (약 500) 목록을 통과하는 루프가 있습니다. 두 개의 주식 오브젝트를 생성 한 다음 두 개의 주식 오브젝트를 생성합니다. 이 시점에서 쌍 계산은 파일에 쓰여지고 객체로 끝납니다. 다음 계산으로 계속 진행할 수 있도록 메모리를 확보해야합니다.

내가 루프의 끝에 다음 두 줄을 추가 한 null로 3 개체를 설정하는 나는 또한

:

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

이 끝난 스테핑이 두 줄을 논제 단지 중 단지 무료 50 MB의 것 루프 반복마다 할당되는 200-300MB (작업 관리자에서 표시).

시스템에 메모리 부족 예외가 발생하기 전에 약 8 ~ 10 쌍의 프로그램이 수행됩니다. 메모리 사용량은 약 1.5GB에서 충돌 할 때까지 꾸준히 증가합니다. (이것은 Win7 Ultimate를 실행하는 8 기가 바이트 기계입니다)

가비지 수집에 대한 많은 경험이 없습니다. 내가 뭔가 잘못하고 있는거야?

다음은 내 코드입니다. (참고 : 프로그램에는 두 가지 모드가 있습니다 .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(); 
} 

내가 요청에 따라 더 많은 코드를 추가하고 : StockPairTimeSeries의 서브 클래스는이, 어떤 공통 기능을 가지고있다. ity

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; 
    } 
} 

답변

3

메모리 누수가 여기에 있습니다 : 그것은은 FileSystemWatcher의 이벤트에 응답하기 때문에

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

주식 개체의 인스턴스가, 동안에는은 FileSystemWatcher이 살아로 메모리에 저장됩니다.

난 당신이 다른 곳에서 해당 이벤트를 구현하거나 원하는, 또는 코드의 다른 지점에서 추가 생각 :

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

하면 해당 가능할 수있는 코드를 작성하는 방식을 감안할 때 재고 객체 대량 처리가 수행되는 경우 FileSystemWatcher없이 호출 할 수 있습니다.

게시 한 원본 코드에서 FileSystemWatcher를 사용하여 Stock 클래스의 생성자를 호출하고있었습니다. 당신은 지금 그것을 바꿨습니다. 이제는 FileSystemWatcher가 null 인 경우 kill을 제거 할 수 있으며 더 이상 fsw.Changed를 듣지 않아 누수가 발생하지 않습니다.

+0

필자는 fsw를 null로 설정했습니다. 추가 작업이 필요하지 않았기 때문에 kill 메소드가 트릭을 수행하고 있다고 생각했습니다. 모든 도움을 주셔서 다시 한 번 감사드립니다. – bkarj

0

Stock 클래스는 대용량 파일에서 많은 주식 데이터 기록을로드합니다. 맞습니까? 따라서 코드에서 해당 파일을 어딘가에 열어야했습니다. 그 파일들을 적절하게 처리 했습니까? 그렇지 않다면, 그것들은 열린 채로 남아있어 Unmanaged Resource이 당신의 기억을 앗아간 다. 열려있는 파일을 올바르게 처리하려면 using 블록을 사용하십시오. This은 적절한 처분 패턴을 구현하는 데 도움이 될 수 있습니다.

+0

사용 블록을 사용하지 않더라도 GC는 참조를 보유하지 않는 한 어느 시점에서 인스턴스를 해제합니다. 그런 다음 finalizer가 dispose 파트를 호출합니다. 이런 종류의 문제는 사용하는 블록이 없기 때문에 발생하는 것이 아니라 인스턴스에 대한 참조를 유지하는 것으로 발생합니다. – Neil

+0

@Nero 각 주식의 데이터는 매월 하나씩 많은 파일로 나뉩니다. 새로운 streamreader를 사용하여 한 번에 한 줄씩 읽는 각 파일을 읽고 Stock 클래스의 List 에 줄을 추가합니다. 나는 이것이 문제가되지 않는다. – bkarj