2012-05-03 39 views
6

Tôi có nhiều tệp gzip lớn (khoảng 10MB - 200MB) mà tôi đã tải xuống từ ftp để được giải nén.Giải nén GZIP C# OutOfMemory

Vì vậy, tôi đã cố gắng tìm kiếm một số giải pháp để giải nén gzip.

static byte[] Decompress(byte[] gzip) 
    { 
     using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress)) 
     { 
      const int size = 4096; 
      byte[] buffer = new byte[size]; 
      using (MemoryStream memory = new MemoryStream()) 
      { 
       int count = 0; 
       do 
       { 
        count = stream.Read(buffer, 0, size); 
        if (count > 0) 
        { 
         memory.Write(buffer, 0, count); 
        } 
       } 
       while (count > 0); 
       return memory.ToArray(); 
      } 
     } 
    } 

nó hoạt động tốt cho bất kỳ tệp nào dưới 50mb nhưng một khi tôi đã nhập hơn 50mb, tôi có hệ thống hết ngoại lệ bộ nhớ. Vị trí cuối cùng và độ dài của bộ nhớ trước khi ngoại lệ là 134217728. Tôi không nghĩ rằng nó có quan hệ với bộ nhớ vật lý của tôi, tôi hiểu rằng tôi không thể có đối tượng nhiều hơn 2GB kể từ khi tôi sử dụng 32-bit.

Tôi cũng cần xử lý dữ liệu sau khi giải nén các tệp. Tôi không chắc chắn nếu dòng bộ nhớ là cách tiếp cận tốt nhất ở đây nhưng tôi không thực sự thích viết vào tập tin và sau đó đọc các tập tin một lần nữa.

Câu hỏi của tôi

  • tại sao tôi nhận được System.OutMemoryException?
  • giải pháp tốt nhất có thể để giải nén các tệp gzip và thực hiện một số xử lý văn bản sau đó là gì?
+0

Bạn đang tải toàn bộ nội dung của luồng vào bộ nhớ và trả về dưới dạng mảng byte. Bạn sẽ mong đợi điều gì khác * khác * hơn một ngoại lệ bộ nhớ? Bạn không nên tải tất cả vào bộ nhớ như thế này - cuối cùng bạn định làm gì với mảng? Viết nó vào một tập tin? Dù bạn có ý định gì, nó cũng phải dựa trên luồng chứ không phải dựa trên mảng. –

+0

cũng .. Ngoại lệ xảy ra trên memory.write và bị kẹt ở đó trong 134217728 .. Tôi không quen thuộc với quản lý bộ nhớ, vì vậy hãy chịu với tôi. Sau đó tôi sẽ lưu tất cả các tệp đã xử lý vào cơ sở dữ liệu, tệp bên trong tệp được nén là tệp csv –

+3

Chắc chắn, nhưng thiết kế của bạn sẽ tốt hơn nếu bạn xử lý tệp * trong khi * bạn đang giải nén. Bằng cách đó bạn sẽ không phải phân bổ một bộ nhớ khổng lồ để xử lý nó. (ví dụ, bằng cách ném luồng gzip của bạn trực tiếp vào một 'StreamReader') –

Trả lời

3

Memory chiến lược phân bổ cho MemoryStream không thân thiện với lượng dữ liệu khổng lồ.

Kể từ khi hợp đồng cho MemoryStream là có mảng liền kề như lưu trữ cơ bản, nó phải sắp xếp lại mảng thường đủ cho luồng lớn (thường là log2 (size_of_stream)). Tác dụng phụ của việc tái phân bổ như vậy là

  • dài trì hoãn bản sao trên phân bổ lại
  • mảng mới phải phù hợp trong không gian địa chỉ miễn phí đã được rất nhiều phân mảnh của phân bổ trước
  • mảng mới sẽ được LOH đống có quirks của nó (không nén chặt, thu thập trên GC2).

Khi xử lý luồng lớn (100Mb +) qua MemoryStream có thể sẽ không có ngoại lệ bộ nhớ trên hệ thống x86. Ngoài ra hầu hết các mô hình phổ biến để trả về dữ liệu là gọi GetArray như bạn làm mà bổ sung yêu cầu về cùng một lượng không gian như bộ đệm mảng cuối cùng được sử dụng cho MemoryStream.

phương pháp tiếp cận để giải quyết:

  • Cách rẻ nhất là trước phát triển MemoryStream để xấp xỉ kích thước bạn cần (tốt nhất là hơi lớn). Bạn có thể tính toán kích thước trước được yêu cầu bằng cách đọc luồng giả mạo không lưu trữ bất kỳ thứ gì (lãng phí tài nguyên CPU, nhưng bạn sẽ có thể đọc được). Xem xét cũng trở lại dòng thay vì mảng byte (hoặc mảng byte trả về của MemoryStream đệm cùng với chiều dài).
  • Một tùy chọn khác để xử lý nếu bạn cần toàn bộ luồng hoặc mảng byte là sử dụng luồng tệp tạm thời thay vì MemoryStream để lưu trữ lượng lớn dữ liệu.
  • Cách tiếp cận phức tạp hơn là triển khai luồng dữ liệu cơ bản trong khối nhỏ hơn (tức là 64K) để tránh phân bổ trên LOH và sao chép dữ liệu khi luồng cần phát triển.
+0

Vâng, cảm ơn vì đã làm rõ điều này với tôi. Giờ tôi đã hiểu, luồng bộ nhớ không phải là bạn tốt cho tôi trong trường hợp này. Tôi nghĩ rằng nó có thể giúp nhanh hơn hiệu suất nhưng thay vào đó nó làm tôi đau đầu hơn. Cảm ơn –

0

Tôi hiểu rằng tôi không thể có đối tượng hơn 2GB kể từ khi tôi sử dụng 32-bit

Đó là không chính xác. Bạn có thể có nhiều bộ nhớ tùy thích. Giới hạn 32 bit có nghĩa là bạn chỉ có thể có 4GB (Hệ điều hành chiếm một nửa) của không gian địa chỉ ảo. Không gian địa chỉ ảo không phải là bộ nhớ. Here là thiết bị đọc đẹp.

tại sao tôi nhận được System.OutMemoryException?

Vì người cấp phát không thể tìm thấy không gian địa chỉ liền kề cho đối tượng của bạn hoặc nó diễn ra quá nhanh và bị khóa. (Nhiều khả năng là trang đầu tiên)

giải pháp tốt nhất có thể để giải nén tệp gzip và làm một số xử lý văn bản sau đó là gì?

Viết tập lệnh tải xuống tệp, sau đó sử dụng các công cụ như gzip hoặc 7zip để giải nén và sau đó xử lý tệp. Tùy thuộc vào loại xử lý, số lượng tệp và tổng kích thước bạn sẽ phải lưu chúng tại một số điểm để tránh các vấn đề về bộ nhớ kiểu này. Lưu chúng sau khi giải nén và xử lý 1MB cùng một lúc.

+5

[OP là chính xác về giới hạn kích thước 2GB * mảng *] (http: // stackoverflow.com/questions/1087982/single-objects-still-limited-to-2-gb-in-size-in-clr-4-0). Ngoài ra, tôi nghĩ rằng đề xuất một công cụ bên ngoài như 7-zip hoàn toàn bỏ lỡ tinh thần của queston này. –

1

Bạn có thể thử một thử nghiệm như sau để có được một cảm giác về bao nhiêu bạn có thể viết thư cho MemoryStream trước khi nhận được một OutOfMemoryException:

 const int bufferSize = 4096; 
     byte[] buffer = new byte[bufferSize]; 

     int fileSize = 1000 * 1024 * 1024; 

     int total = 0; 

     try 
     { 
      using (MemoryStream memory = new MemoryStream()) 
      { 
       while (total < fileSize) 
       { 
        memory.Write(buffer, 0, bufferSize); 
        total += bufferSize; 
       } 

      } 

      MessageBox.Show("No errors"); 

     } 
     catch (OutOfMemoryException) 
     { 
      MessageBox.Show("OutOfMemory around size : " + (total/(1024m * 1024.0m)) + "MB"); 
     } 

Bạn có thể phải giải nén vào một tập tin vật lý tạm thời đầu tiên và tái đọc nó theo từng phần nhỏ và xử lý khi bạn đi.

Side Point: thú vị, trên một PC chạy Windows XP, các mã trên cho: "OutOfMemory xung quanh kích thước 256MB" khi chỉ tiêu mã NET 2.0, và "OutOfMemory xung quanh kích thước 512MB" trên .net 4.

+1

Tôi đã chỉ định ở trên. Nó đã bị mắc kẹt trên 134217728 khoảng khoảng 128MB nếu tôi chính xác. Tôi không chắc tại sao điều này xảy ra quá sớm nhưng tôi đoán chọn luồng bộ nhớ là sai lầm đầu tiên của tôi .. Cảm ơn câu trả lời của bạn –

+0

Có thể xác nhận tôi đã đạt đến giới hạn CHÍNH XÁC. – Kris

1

Bạn có đang xử lý tệp trong nhiều chuỗi không? Điều đó sẽ tiêu tốn một lượng lớn không gian địa chỉ của bạn. Lỗi OutOfMemory thường không liên quan đến bộ nhớ vật lý và do đó MemoryStream có thể chạy sớm hơn bạn mong đợi. Kiểm tra cuộc thảo luận này http://social.msdn.microsoft.com/Forums/en-AU/csharpgeneral/thread/1af59645-cdef-46a9-9eb1-616661babf90. Nếu bạn chuyển sang quy trình 64 bit, có thể bạn sẽ không hài lòng với kích thước tệp mà bạn đang xử lý.

Trong trường hợp hiện tại của bạn, bạn có thể làm việc với các tệp ánh xạ bộ nhớ để nhận được bất kỳ giới hạn kích thước địa chỉ nào. Nếu bạn đang sử dụng .NET 4.0, nó cung cấp một trình bao bọc gốc cho các chức năng của Windows http://msdn.microsoft.com/en-us/library/dd267535.aspx.

+0

Có, tôi đã thấy liên kết đó trước khi tôi hỏi trong SO. Tôi chỉ muốn biết những lựa chọn khác mà tôi có. Cảm ơn bạn đã trả lời –