2010-02-01 6 views
0

우리는 대부분 데이터 읽기를 위해 열고 닫아야하는 많은 파일을 처리하고 있습니다. temp hashtable 또는 다른 객체에있는 각 파일의 memorystream을 캐시하는 것이 좋은가요?캐시에서 MemoryStream을 사용할 때 메모리 부족 예외가 발생했습니다.

100MB를 초과하는 파일을 열 때 메모리 부족 오류가 발생하는 것을 확인했습니다. 우리는 wpf 앱을 사용하고 있습니다.

파일을 성공적으로 열 수 있습니다. 1 ~ 2 번은 3 ~ 4 번 실행하지만 이후에는 예외가 발생합니다.

답변

0

MemoryStream과 관련된 한 가지 문제점은 용량을 늘려야 할 때마다 내부 버퍼가 두 배로 크기 때문입니다. MemoryStream이 100MB이고 파일이 101MB 인 경우에도 마지막 1MB를 MemoryStream에 쓰 자마자 MemoryStream의 내부 버퍼가 두 배가되어 200MB가됩니다. 메모리 버퍼에 파일 용량과 동일한 용량을 줄 경우이 값을 줄일 수 있습니다. 그러나 이것은 파일이 일부 메모리가 모두로드 된 후 모든 메모리를 사용하고 새로운 할당을 중지하도록 허용합니다. WeakReference 오브젝트 내부의 도움말 인 캐시 오브젝트를 작성하면 필요에 따라 가비지 콜렉터가 캐시 된 파일 중 일부를 버리도록 허용 할 수 있습니다. 그러나 필요할 때 손실 된 캐시를 다시 작성하기 위해 코드를 추가해야 할 필요가 있음을 잊지 마십시오.

public class CacheStore<TKey, TCache> 
{ 
    private static object _lockStore = new object(); 
    private static CacheStore<TKey, TCache> _store; 
    private static object _lockCache = new object(); 
    private static Dictionary<TKey, TCache> _cache = 
              new Dictionary<TKey, TCache>(); 

    public TCache this[TKey index] 
    { 
     get 
     { 
      lock (_lockCache) 
      { 
       if (_cache.ContainsKey(index)) 
        return _cache[index]; 
       return default(TCache); 
      } 
     } 
     set 
     { 
      lock (_lockCache) 
      { 
       if (_cache.ContainsKey(index)) 
        _cache.Remove(index); 
       _cache.Add(index, value); 
      } 
     } 
    } 

    public static CacheStore<TKey, TCache> Instance 
    { 
     get 
     { 
      lock (_lockStore) 
      { 
       if (_store == null) 
        _store = new CacheStore<TKey, TCache>(); 
       return _store; 
      } 
     } 
    } 
} 
public class FileCache 
{ 
    private WeakReference _cache; 
    public FileCache(string fileLocation) 
    { 
     if (!File.Exists(fileLocation)) 
      throw new FileNotFoundException("fileLocation", fileLocation); 
     this.FileLocation = fileLocation; 
    } 
    private MemoryStream GetStream() 
    { 
     if (!File.Exists(this.FileLocation)) 
      throw new FileNotFoundException("fileLocation", FileLocation); 
     return new MemoryStream(File.ReadAllBytes(this.FileLocation)); 
    } 

    public string FileLocation { get; private set; } 
    public MemoryStream Data 
    { 
     get 
     { 
      if (_cache == null) 
       _cache = new WeakReference(GetStream(), false); 

      var ret = _cache.Target as MemoryStream; 
      if (ret == null) 
      { 
       Recreated++; 
       ret = GetStream(); 
       _cache.Target = ret; 
      } 
      return ret; 
     } 
    } 

    public int Recreated { get; private set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var cache = CacheStore<string, FileCache>.Instance; 

     var fileName = @"c:\boot.ini"; 
     cache[fileName] = new FileCache(fileName); 

     var ret = cache[fileName].Data.ToArray(); 
     Console.WriteLine("Recreated {0}", cache[fileName].Recreated); 
     Console.WriteLine(Encoding.ASCII.GetString(ret)); 
     GC.Collect(); 
     var ret2 = cache[fileName].Data.ToArray(); 
     Console.WriteLine("Recreated {0}", cache[fileName].Recreated); 
     Console.WriteLine(Encoding.ASCII.GetString(ret2)); 
     GC.Collect(); 
     var ret3 = cache[fileName].Data.ToArray(); 
     Console.WriteLine("Recreated {0}", cache[fileName].Recreated); 
     Console.WriteLine(Encoding.ASCII.GetString(ret3)); 
     Console.Read(); 
    } 
} 
4

이 파일을 현재 캐시하고있는 경우 메모리가 매우 빨리 소모 될 것으로 예상됩니다.

아직 캐싱하지 않는 경우 더 심하게 만들 것이므로 사용하지 마십시오. 아마도 당신은 메모리 누수가 있습니까? 일단 memorystream을 사용했다면 폐기하겠습니까?

큰 파일을 처리하는 가장 좋은 방법은

0

그것은 매우 dificutl의 ... 당신이 한 번에 메모리에 전체 파일을 할 필요가 없도록, (FileStreams 사용) 아웃 데이터를 스트리밍하는 것입니다 일반적인 경우에 파일 내용을 캐싱하고 /하거나 정보가 거의없는 경우 "예"또는 "아니오"라고 말하십시오. 그러나 한정된 리소스는 실제 세계의 상태이며, 개발자 (개발자)는이를 고려해야합니다. 캐시를 원하면 자동 언로드 데이터에 대한 메커니즘을 사용해야합니다. .NET 프레임 워크에서 WeakReference 클래스를 사용할 수 있습니다.이 클래스는 대상 객체를 언로드합니다 (바이트 배열과 메모리 스트림도 객체 임).

HW를 제어하고 64 비트를 사용할 수 있고 매우 큰 RAM을 사용할 수있는 경우 대용량 파일을 캐시 할 수 있습니다.

그러나 리소스 (CPU, RAM)는 겸손해야하며 "저렴한"구현 방법을 사용해야합니다.

0

나는이 문제가 완료된 후에 파일이 immediatly로 처리되지 않는다고 생각하고 다음 GC주기를 기다리고있다.

스트림은 IDisposable이며 whice는 사용 블록을 사용할 수 있고 사용해야 함을 의미합니다. 스트림이 처리되면 끝내 버립니다.

+0

그냥 명확히하기 위해, 당신은에서 OutOfMemory 예외를 얻을 수 없다 * 단지 * 가비지 컬렉터가 잡았되지 않았기 때문에 - 오해 이런 종류의 잘못을 뿌리는 사람에 이르게'GC.Collect를()'생각 자신의 코드 . 일부 메모리를 할당하라는 요청이 있고 충분하지 않은 경우 GC는 3 세대 모두 호출됩니다. 그러나 MemoryStream이 메모리를 모두 해제하기 위해서는 Dispose() (아마도'using()'을 통해)를 호출해야한다고 동의합니다. –

+0

Dispose 메모리를 해제하지 마십시오. GC만이 그렇게 할 수 있습니다. – adrianm

0

메모리 오버 플로우가 발생하지 않더라도 그러한 양의 데이터를 캐싱하는 것이 좋은 해결책이라고 생각하지 않습니다. 메모리 매핑 파일 솔루션을 확인하십시오. 즉, 파일이 파일 시스템에 저장되지만 읽기 속도는 메모리에있는 것과 거의 같습니다 (확실히 오버 헤드가 있음). 이 링크를 확인하십시오. MemoryMappedFiles

P. Ther는 인터넷에있는이 화제 arround에 아주 좋은 기사 및보기이다. 행운을 비네.

관련 문제