2010-03-31 4 views
3

주 스레드는 GUI 스레드이고 다른 스레드는 작업자입니다.
앱에는 일부 컨트롤이있는 WPF 양식이 하나 있습니다. 디렉토리를 선택할 수있는 버튼이 있습니다. 디렉토리를 선택한 후 응용 프로그램은 해당 디렉토리에서 .jpg 파일을 검색하고 축소판이 해시 테이블에 있는지 확인합니다. 그들이 있다면, 그것은 아무것도하지 않습니다. 그렇지 않으면 작업자의 전체 파일 이름을 대기열에 추가합니다.
Worker는이 대기열에서 WPF의 JpegBitmapDecoder 및 BitmapFrame을 사용하여 JPEG 이미지를로드하고 (WPF의 TransformedBitmap을 사용하여) 축소판을 만들고 해시 테이블에 추가하여 파일 이름을 가져옵니다.
큰 이미지 (예 : 5000x5000 픽셀)의 축소판을 만들 때이 응용 프로그램의 메모리 사용을 제외한 모든 기능이 정상적으로 작동합니다. 내 양식에 메모리 사용량 (GC.GetTotalMemory() 및 Process.GetCurrentProcess(). PrivateMemorySize64)을 표시하기 위해 텍스트 상자를 추가했으며 매우 놀랐습니다. cuz GC.GetTotalMemory()는 1 - 2MB에 가깝고 개인 메모리 크기 지속적으로, 특히 새로운 이미지를로드 할 때 증가합니다 (이미지 당 ~ 100MB).
모든 이미지를로드하고 이미지 축소판을 만들고 원본 이미지를 비운 후에도 개인 메모리 크기는 ~ 700-800Mbyte에 머물러 있습니다. 내 VirtualBox는 512MB의 실제 메모리로 제한되며 VirtualBox의 Windows는이 거대한 메모리 소비를 처리하기 위해 많은 양을 스왑하기 시작합니다. 내가 뭔가 잘못하고있는 것 같아요,하지만이 문제를 조사하는 방법, GC에 따라 사촌, 할당 된 메모리 크기가 매우 낮습니다.GC.GetTotalMemory()가 너무 낮을 때 C# WPF 응용 프로그램이 너무 많은 메모리를 사용합니다.

썸네일 로더 클래스의 코드를 첨부 : 나는 영상과 관련이있다 생각

class ThumbnailLoader 
{ 
    Hashtable thumbnails; 
    Queue<string> taskqueue; 
    EventWaitHandle wh; 
    Thread[] workers; 
    bool stop; 
    object locker; 
    int width, height, processed, added; 

    public ThumbnailLoader() 
    { 
     int workercount,i; 
     wh = new AutoResetEvent(false); 
     thumbnails = new Hashtable(); 
     taskqueue = new Queue<string>(); 
     stop = false; 
     locker = new object(); 
     width = height = 64; 
     processed = added = 0; 
     workercount = Environment.ProcessorCount; 
     workers=new Thread[workercount]; 
     for (i = 0; i < workercount; i++) { 
      workers[i] = new Thread(Worker); 
      workers[i].IsBackground = true; 
      workers[i].Priority = ThreadPriority.Highest; 
      workers[i].Start(); 
     } 
    } 

    public void SetThumbnailSize(int twidth, int theight) 
    { 
     width = twidth; 
     height = theight; 
     if (thumbnails.Count!=0) AddTask("#resethash"); 
    } 

    public void GetProgress(out int Added, out int Processed) 
    { 
     Added = added; 
     Processed = processed; 
    } 

    private void AddTask(string filename) 
    { 
     lock(locker) { 
      taskqueue.Enqueue(filename); 
      wh.Set(); 
      added++; 
     } 
    } 

    private string NextTask() 
    { 
     lock(locker) { 
      if (taskqueue.Count == 0) return null; 
      else { 
       processed++; 
       return taskqueue.Dequeue(); 
      } 
     } 
    } 

    public static string FileNameToHash(string s) 
    { 
     return FormsAuthentication.HashPasswordForStoringInConfigFile(s, "MD5"); 
    } 

    public bool GetThumbnail(string filename,out BitmapFrame thumbnail) 
    { 
     string hash; 
     hash = FileNameToHash(filename); 
     if (thumbnails.ContainsKey(hash)) { 
      thumbnail=(BitmapFrame)thumbnails[hash]; 
      return true; 
     } 
     AddTask(filename); 
     thumbnail = null; 
     return false; 
    } 

    private BitmapFrame LoadThumbnail(string filename) 
    { 
     FileStream fs; 
     JpegBitmapDecoder bd; 
     BitmapFrame oldbf, bf; 
     TransformedBitmap tb; 
     double scale, dx, dy; 
     fs = new FileStream(filename, FileMode.Open); 
     bd = new JpegBitmapDecoder(fs, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); 
     oldbf = bd.Frames[0]; 
     dx = (double)oldbf.Width/width; 
     dy = (double)oldbf.Height/height; 
     if (dx > dy) scale = 1/dx; 
     else scale = 1/dy; 
     tb = new TransformedBitmap(oldbf, new ScaleTransform(scale, scale)); 
     bf = BitmapFrame.Create(tb); 
     fs.Close(); 
     oldbf = null; 
     bd = null; 
     GC.Collect(); 
     return bf; 
    } 

    public void Dispose() 
    { 
     lock(locker) { 
      stop = true; 
     } 
     AddTask(null); 
     foreach (Thread worker in workers) { 
      worker.Join(); 
     } 
     wh.Close(); 
    } 

    private void Worker() 
    { 
     string curtask,hash; 
     while (!stop) { 
      curtask = NextTask(); 
      if (curtask == null) wh.WaitOne(); 
      else { 
       if (curtask == "#resethash") thumbnails.Clear(); 
       else { 
        hash = FileNameToHash(curtask); 
        try { 
         thumbnails[hash] = LoadThumbnail(curtask); 
        } 
        catch { 
         thumbnails[hash] = null; 
        } 
       } 
      } 
     } 
    } 
} 
+0

큰 개체 힙이있는 것과 같은 소리가납니다 ... 이미지 처리 과정에서 시간이 지남에 따라 성능 카운터를 얻을 수 있습니까? LOH는 표준 메모리와 같은 방식으로 수집하지 않으며 (정확하게 기억한다면) 조각화에 훨씬 더 민감합니다 ... – LorenVS

+1

이것은 관리되지 않는 메모리에 대해 여러 가지 알려진 문제점이있는 WPF의 버그 일 수 있습니다. Connect (http://connect.microsoft.com/)에서이 사실을 알려주기를 권합니다. 메모리 누수가 응용 프로그램에 문제가 발생하여 수정 사항을 얻을 수없는 경우 코드의 중요 부분을 다른 프로세스 나 AppDomain으로 옮길 수 있습니다. –

답변

3

문제가 해결되었습니다.
그냥 BitmapCacheOption.None :)와 BitmapCacheOption.OnLoad를 교체했다

+2

@Dmitry : 답변을 허용 된 답변으로 표시해야합니다. 이것이 Nymaen의 대답에 대한 "답장"이라면, 그 답을 수락 된 것으로 표시하고 (수표를 클릭하십시오), 별도의 대답이 아닌 Comment로 포함 시키십시오 ... –

+0

Nymaen의 대답에 응답 할 수 없습니다 왜냐하면 나는 그의 대답을 쓴 것보다 일찍 이것을 썼기 때문이다. – mephisto123

2

- 이미지 클래스의 기본 객체가 GC 카운터에 포함되어 있지 않기 때문에 그들에 의해 소비되는이 메모리의 비 관리입니다.

또한 처리 방법에 대한 추가 관리가 필요합니다. 관리되는 메모리 사용량이 매우 적으므로 GC가 실제로주의를 기울이지는 않지만 분리되지 않은 메모리는 볼 수 있습니다.

결론 - 범위를 벗어나는 데 충분하지 않으므로 작업이 끝나면 명시 적으로 처리를 호출해야합니다.

+1

BitmapImage 클래스에는 Dispose() 메서드가 없습니다. – mephisto123

10

내가 BitmapCacheOption.OnLoad이 프레임 워크의 메모리 공간에 이미지를 추가하는 것으로 의심,하지만 당신은 이미지 캐시에있는 객체를 소유하고 있지 않기 때문에, 그들은 표시되지 않습니다 GC 메서드 호출의 결과입니다. 대신 BitmapCacheOption.None을 사용하여 메모리 문제가 해결되는지 확인하십시오. 참고 : 이렇게하면 성능에 큰 영향을 미칩니다.

관련 문제