2012-01-03 48 views
6

Tôi có một vấn đề đã làm phiền tôi bây giờ trong một vài ngày. Tôi đã cố gắng để Google vấn đề, nhưng cho đến nay đã không thể tìm thấy bất kỳ giải pháp, thậm chí không phải là một người duy nhất với cùng một vấn đề.C# System.Buffer.BlockCopy Memory Issue?

Dường như phương pháp C# System.Buffer.BlockCopy để lại cho bạn một số loại bóng ma bộ nhớ. Ví dụ: tôi có ví dụ:

private float[,] readFloatArray2 (byte[] b) { 
    int floatSize = sizeof(float); 
    float[,] v = new float[2, (b.Length/2)/floatSize]; 
    System.Buffer.BlockCopy(b, 0, v, 0, b.Length); 

    return v; 
} 

để chuyển mảng byte sang mảng nổi 2D. Dữ liệu trước đây được đọc từ một luồng. Tôi đã đặt vấn đề là phương thức System.Buffer.BlockCopy.

Nếu tôi xóa lệnh BlockCopy, bộ nhớ được ứng dụng sử dụng sẽ bằng một nửa kích thước. điều này có nghĩa rằng không phải lỗi của tôi mà mảng byte vẫn còn sống. bởi vì không có lệnh BlockCopy, mảng byte chết đúng cách. mảng float được tạo ra dù sao (có hoặc không có thông tin được sao chép).

Tôi không hoàn toàn chắc chắn nếu đây là một vấn đề của lệnh BlockCopy hoặc GC vì tôi cũng đã cố gắng gọi System.GC.Collect(); sau khi BlockCopy và sau đó nó cũng hoạt động hoàn hảo (tôi biết bạn không nên làm điều này ... đó là lý do tại sao tôi yêu cầu tư vấn ở đây).

Tôi cũng sẽ không bận tâm hỏi, nhưng vấn đề liên quan đến vài trăm meg.

Bên cạnh các vấn đề về bộ nhớ, phương thức hoạt động hoàn toàn tốt. Có ai biết điều gì gây ra vấn đề bộ nhớ?

lời chào và cảm ơn trước oli

ps: Tôi đang sử dụng .NET4.0 với Visual Studio 2010 PRO và WIN7 ... không biết liệu điều này có liên quan hay không.

+2

Nếu bạn nói bộ nhớ được thu thập đúng cách bởi một 'GC.Collect' sau đó mọi thứ đều tốt. 'b' * sẽ * cuối cùng được thu thập bởi một GC bình thường khi thời gian đến. –

+0

Nếu bạn đang làm việc trên dữ liệu âm thanh, tôi muốn sử dụng một mảng lởm chởm của dạng 'float [len] [channelCount]'. Bằng cách đó bạn có thể xử lý các kênh riêng biệt, điều này đôi khi hữu ích. – CodesInChaos

+1

"mảng float được tạo anyway" Chỉ một nửa sự thật. Nó không nhất thiết cần bộ nhớ vật lý. Các trang bộ nhớ là tất cả 0 và chưa bao giờ được ghi vào, được tối ưu hóa bởi trình quản lý bộ nhớ cửa sổ. – CodesInChaos

Trả lời

1

BlockCopy không được triển khai .NET được quản lý. Bên trong, nó gọi api giành chiến thắng bên ngoài.

[SecuritySafeCritical] 
public static extern void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count); 
+0

ok ... và cách xử lý vấn đề này? bạn có đề nghị gì? –

+0

Không có gì. GC sẽ chăm sóc nó cuối cùng khi nó cần thêm bộ nhớ. –

1

Buffer.BlockCopy là dựa trên byte chứ không phải chỉ mục. Tôi sẽ đề nghị bạn sử dụng Array.Copy mà về cơ bản làm điều tương tự. BlockCopy chỉ nhanh hơn một chút.

bạn cần phải chuyển đổi các byte [] để float [] first.Have nhìn vào dưới đây để biết rằng

static float[] ConvertByteArrayToFloat(byte[] bytes) 
{ 
    if(bytes == null) 
     throw new ArgumentNullException("bytes"); 

    if(bytes.Length % 4 != 0) 
     throw new ArgumentException 
       ("bytes does not represent a sequence of floats"); 

    return Enumerable.Range(0, bytes.Length/4) 
        .Select(i => BitConverter.ToSingle(bytes, i * 4)) 
        .ToArray(); 
} 
+0

Nhưng Array.Copy không làm việc với kiểu dữ liệu khác nhau. Tôi không thể sử dụng nó để sao chép từ byte [] sang float [,]. –

+0

Tôi đã chỉnh sửa câu trả lời của tôi –

+0

yeah ... ok ... thats mát mẻ nhưng tôi có một mảng byte 1D đến một mảng float 2D (float [,]). Nhưng đề nghị của bạn là mát mẻ. Tôi không biết điều này. (Mã hóa trong C# trong vòng 4 tháng nay) –

2

tôi đã định vị được vấn đề là phương pháp System.Buffer.BlockCopy. Nếu tôi xóa lệnh BlockCopy, bộ nhớ được ứng dụng sử dụng sẽ bằng một nửa kích thước. điều này có nghĩa rằng không phải lỗi của tôi mà mảng byte vẫn còn sống. Vì không có lệnh BlockCopy, mảng byte sẽ chết đúng cách.

Tôi không đồng ý với kết luận đó. Tôi thấy một số giai đoạn:

  1. Byte mảng tồn tại và được làm đầy với dữ liệu
  2. Bạn phân bổ các mảng float, nhưng nó không được làm đầy với dữ liệu được nêu ra.
  3. Bạn điền vào các mảng float với dữ liệu
  4. Mảng byte không được tham chiếu nữa, nhưng đã không được thu thập
  5. Mảng byte đã được thu thập.

Phần trực tiếp của mảng byte không bị ảnh hưởng bởi BlockCopy.

Bước 2 đặt trước và cam kết bộ nhớ ảo. Vì vậy, kích thước cam kết phát triển trong bước này. Nhưng vì nội dung của mảng chưa bao giờ được viết tới và bao gồm hoàn toàn là 00 byte, trình quản lý bộ nhớ cửa sổ không phân bổ bất kỳ bộ nhớ vật lý nào cho nó. Nó chỉ lưu ý rằng các trang này bao gồm hoàn toàn là 00 s.

Bộ nhớ vật lý cho mảng phao chỉ được cấp phát trong bước 3. Bạn sẽ nhận được hiệu ứng tương tự nếu bạn thêm vòng lặp khởi tạo mọi trường trong mảng.


Ngoài các vấn đề thực tế, tôi cũng có một số gợi ý thiết kế:

  1. bộ đệm tái sử dụng. GC là tốt cho các đối tượng shortlived nhỏ, nhưng rất xấu cho các đối tượng shortlived lớn. Điều này có nghĩa là bạn không nên sử dụng các hàm phân bổ và trả về các mảng lớn. Thay vào đó lấy chúng như một tham số, do đó, một mảng hiện có có thể được tái sử dụng.
  2. Nếu bạn đang sử dụng làm việc với dữ liệu âm thanh (Dường như có khả năng), tôi sẽ không sử dụng một mảng 2D vững chắc. Thay vào đó, tôi sử dụng một mảng các mảng. Trong đó mảng bên trong đại diện cho các mẫu trong một bộ đệm duy nhất, và mảng bên ngoài đại diện cho các bộ đệm. Điều này có hai ưu điểm:

    • Bạn có thể dễ dàng viết mã chỉ hoạt động trên một kênh.
    • Mảng 2D 2D chậm để lập chỉ mục, vì vậy nó thường nhanh hơn.
  3. Bạn có thực sự muốn đọc tất cả dữ liệu cùng một lúc không? Tôi đã đọc trong một vài kilobyte.
+0

Tôi là một bioinformatition như vậy, không tôi không làm việc với các tập tin âm thanh. Giống như với phổ MS. Tôi cần những dữ liệu WHOLE để chế biến và do đó gợi ý thiết kế 1. và 2. Không có tùy chọn (bước 1 là không có lựa chọn vì độ dài của các byte readed khác nhau đáng kể (từ 32 byte đến> 10.000.000)). Thiết kế gợi ý 3 cũng không phải là một lựa chọn bởi vì tôi phải xử lý các phần khác nhau của dữ liệu nhiều hơn một lần để làm nổi bật khía cạnh khác nhau. việc tải lại tất cả mọi thứ trên bay là không thể vì tôi cần toàn bộ thông tin cho đến khi kết thúc quá trình. nhưng nhờ anyway :) –

+0

Oh ... và đề nghị 3, tôi không đọc TẤT CẢ cùng một lúc. Tôi đọc quang phổ. Và một kích thước phổ có thể khác nhau từ 32B đến> 10MB. Và tôi có khoảng 80.000 quang phổ để đọc. Mỗi đơn trong số đó là cần thiết cho việc xử lý. –

1

Tôi không chắc chắn nếu đây là vấn đề của lệnh BlockCopy hoặc GC vì tôi cũng đã cố gọi System.GC.Collect(); sau khi BlockCopy và sau đó nó cũng hoạt động hoàn hảo (tôi biết bạn không nên làm điều này ... đó là lý do tại sao tôi yêu cầu tư vấn ở đây). Tôi cũng sẽ không bận tâm hỏi nếu nó không phải là khoảng vài hundret MB chúng tôi đang nói về.

Bộ sưu tập rác chạy khi cần bộ nhớ nhiều hơn cho thế hệ cụ thể hoặc từ LOH. Như một quy tắc Garbage Collection sẽ không chạy chỉ vì có rác để thu thập, và như một quy luật này là một điều tốt (nó thực sự không chi phí cho chúng tôi bất cứ điều gì để có gigabyte bộ nhớ chính thức "sử dụng" mà chúng tôi không sử dụng miễn là GC có thể nhận được nó khi chúng ta cần nó).

Có lần khi gọi GC.Collect() có ý nghĩa trong một chương trình thực, và điều này cũng có thể là một trong số đó, vì vậy nếu làm như vậy "hoạt động hoàn hảo", thì tôi sẽ không lo lắng quá nhiều về nó thực hành cho 99,9% mã. Lý do là "thực hành tốt nhất" thay vì quy tắc cứng nhắc và nhanh chóng là đôi khi chúng ta đang ở trong trường hợp 0,1% và thực hành tốt nhất thông thường không còn là phương pháp hay nhất.

Ngoài ra, nếu bạn có thể dự đoán trước thời hạn kích thước tối đa của mảng (hoặc không rằng, chỉ các mảng nguồn byte), sau đó tiếp cận đầu tiên CodeInChaos' có thể làm việc. Nó không thực sự làm tổn thương để sử dụng 10.000.000 byte để xử lý 32 chừng một lúc nào đó bạn sẽ thực sự được sử dụng mà 10.000.000. Tái sử dụng 10.000.000 đó làm cho một sự tiết kiệm rất thực tế trong suốt vòng đời của quá trình.