2012-05-27 54 views
9

Chào buổi tối!Ảnh chụp màn hình bị rò rỉ?

Trong một dự án hiện tại, tôi đang gặp phải sự rò rỉ bộ nhớ khá đáng lo ngại mà tôi dường như không thể cắm.

Tôi đã để ứng dụng chạy qua đêm với mức sử dụng tiêu chuẩn đang diễn ra và khi tôi thức dậy sau 8 giờ, nó đã tăng ~ 750MB bộ nhớ trong khi nó bắt đầu ở ~ 50MB. Trình quản lý tác vụ Windows không phù hợp để kiểm tra các rò rỉ khác ngoài việc cho phép bạn tìm ra rằng một sự cố tồn tại ngay từ đầu.

Tôi đã xóa một số rò rỉ bộ nhớ khác rồi, với lỗ hổng lớn nhất có liên quan đến số TGlowEffect của Firemonkeys. Nó không được phát hiện bởi ReportLeaksOnShutdown nhưng việc sử dụng bộ nhớ của nó trở nên cực kỳ quá mức trên đối tượng được sửa đổi động (ví dụ: xoay hoặc thay đổi tỷ lệ).

Tôi đã theo dõi nó xuống một bộ hẹn giờ (và tắt nó dừng hoàn toàn rò rỉ), và tôi yêu cầu hỗ trợ trong việc sửa chữa nó nếu có thể.

Mô tả: Mã này sử dụng hàm Firemonkey MakeScreenshot để cứu sự xuất hiện hình ảnh của một TPanel (SigPanel) đến một TMemoryStream. Dữ liệu luồng này sau đó được tải lên máy chủ FTP từ xa bằng mã tiêu chuẩn (xem bên dưới). Bên trong SigPanel, có 4 TLabel trẻ em, 1 TRectangle trẻ em và 6 TImage trẻ em.

Ghi chú: CfId là một chuỗi toàn cầu và được tạo ra dựa trên một giá trị ngẫu nhiên extended phao mà sau đó được băm cùng với DateTime ở định dạng yyyymmdd_hhnnsszzz. Thế hệ này được thực hiện khi biểu mẫu được tạo và lặp lại cho đến khi nó nhận được CfId hợp lệ (nghĩa là không chứa các ký tự bất hợp pháp để sử dụng trong tên tệp Windows). Khi nó nhận được CfId hợp lệ, nó hoàn toàn không chạy lại (vì tôi không cần phải tạo ID mới nữa). Điều này cho phép tôi gần như hoàn toàn loại bỏ cơ hội trùng lặp CfId.

Mã trong bộ hẹn giờ như sau;

var 
    i : Integer; 
    SigStream : TMemoryStream; 
begin 
    SigStream := TMemoryStream.Create; 
    SigPanel.MakeScreenshot.SaveToStream(SigStream); 
    SigPanel.MakeScreenshot.Free; 
    if VT2SigUp.Connected then 
    begin 
    VT2SigUp.Put(SigStream,'Sig_'+CfId+'.png',False); 
    end else 
    begin 
    VT2SigUp.Connect; 
    VT2SigUp.Put(SigStream,'Sig_'+CfId+'.png',False); 
    end; 
    SigStream.Free; 
end; 

Với bộ đếm thời gian KHÔNG chạy, các chức năng mã hoàn toàn không rò rỉ và ReportMemoryLeaksOnShutdown không KHÔNG tạo ra một thông điệp. Với bộ hẹn giờ được kích hoạt và được phép "chạy" ít nhất một lần, tôi nhận được rất nhiều rò rỉ làm tăng thời gian chạy bộ đếm thời gian. Các rò rỉ được báo cáo như sau;

Small Block Leaks 

1 - 12 Bytes: Unknown x 1 
13 - 20 Bytes: TList x 5, Unknown x 1 
21 - 28 Bytes: TFont x 2, TGradientPoint x 8, TGradientPoints x 4, Unknown x 4 
29 - 36 Bytes: TObjectList<FMX.Types.TCanvasSaveState> x 1, TBrushBitmap x 4, 
TBrushGrab x 4, TPosition x 24, TGradient x 4, UnicodeString x1 
37 - 44 Bytes: TBrushResource x 4 
53 - 60 Bytes: TBrush x 4 
61 - 68 Bytes: TBitmap x 5 
69 - 76 Bytes: TD2DCanvasSaveState x 1 
205 - 220 Bytes: TCanvasD2D x 1 

Sizes of Medium and Large Block Leaks 
200236 

Khi timer chạy, những giá trị được nhân n lần (n là số lần đếm thời gian đã chạy). Các khối trung bình và lớn có giá trị n năm 200236 (ví dụ: nếu bộ hẹn giờ đã chạy 3 lần, đó là 200236, 200236, 200326).

Quan tâm, nếu tôi xóa mã được liên kết với MakeScreenshot, rò rỉ không còn tồn tại và mức sử dụng bộ nhớ vẫn ở mức hơi bình thường. Bên cạnh việc sử dụng bộ nhớ thông thường, không có gì ngoài bình thường và không có rò rỉ được báo cáo. Tôi đã thử nhiều mẫu mã, cả hai đều có lưu vào luồng và tải lên từ đó hoặc lưu vào luồng> Tệp và sau đó tải lên tệp, nhưng dường như có rò rỉ bên trong chính hàm đó.Tôi thậm chí còn thêm MakeScreenshot.Free một khi tôi phát hiện ra một rò rỉ ở đây, nhưng tôi chỉ đơn giản là không thể có vẻ để cắm nó, và tất nhiên, tôi đã sử dụng try..finally trong một mã của tôi "chạy thử nghiệm".

Tôi thậm chí đã chạy mã với GDI + làm loại canvas và cùng một rò rỉ xảy ra ở đó (với thay đổi duy nhất là D2D rò rỉ tham chiếu GDI + thay thế).

Tôi rất muốn đánh giá cao mọi nghiên cứu hoặc ghi chú bất kỳ ai có về vấn đề này và hơn nữa, giải pháp cho vấn đề này.

+1

Tôi tin rằng bạn chỉ tìm thấy một rò rỉ bộ nhớ trong FM (: – ComputerSaysNo

+0

tôi tin rằng thiết 'ReportMemoryLeaksOnShutdown: = True; ' trong quá trình khởi chạy ứng dụng của bạn, hãy thực hiện mẹo để cho bạn thấy những gì đang bị rò rỉ ... –

+0

@DorinDuminica Tôi tin như vậy. Tuy nhiên, tôi tin rằng tôi đã phát hiện ra sự cố là 'Kết quả' trong' FMX.Types.MakeScreenshot' không phải là Chúng đơn giản gọi là 'Result.Canvas.EndScene' và không bao giờ giải phóng nó! @Je rryDodge Đó là những gì tôi chạy để tìm hiểu những gì đã bị rò rỉ chính xác như tôi sẽ không có thể có được một danh sách chính xác mà không có nó :) –

Trả lời

13

Bạn không giải phóng bitmap MakeScreenshot tạo.

procedure TForm1.Button1Click(Sender: TObject); 
var 
    ms: TMemoryStream; 
begin 
    ms := TMemoryStream.Create; 
    Panel1.MakeScreenshot.SaveToStream(ms); 
    ms.Free; 
end; 

Đoạn mã trên không giữ tham chiếu đến bitmap đã tạo, do đó không có cơ hội để giải phóng nó. Thay vì thay đổi thiết kế của bạn như dưới đây:

procedure TForm1.Button2Click(Sender: TObject); 
var 
    ms: TMemoryStream; 
    bmp: TBitmap; 
begin 
    ms := TMemoryStream.Create; 
    bmp := Panel1.MakeScreenshot; 
    bmp.SaveToStream(ms); 
    ms.Free; 
    bmp.Free; 
end; 


Với mã bên dưới, bạn đang ở trong thực tế tạo ra hai bitmap và giải phóng một trong số họ.

SigPanel.MakeScreenshot.SaveToStream(SigStream); 
    SigPanel.MakeScreenshot.Free; 


Cuối cùng, mã của bạn sẽ được nhiều hơn như dưới đây:

var 
    i : Integer; 
    Bmp: TBitmap; 
    SigStream : TMemoryStream; 
begin 
    SigStream := TMemoryStream.Create; 
    try 
    Bmp := SigPanel.MakeScreenshot; 
    try 
     Bmp.SaveToStream(SigStream); 
     if not VT2SigUp.Connected then 
     VT2SigUp.Connect; 
     VT2SigUp.Put(SigStream, 'Sig_'+CfId+'.png', False); 
    finally 
     Bmp.Free; 
    end; 
    finally 
    SigStream.Free; 
    end; 
end; 
+2

Bổ sung câu trả lời @Sertac không quên sử dụng thử cuối cùng để tránh lỗi khi đặt và tránh rò rỉ bộ nhớ vì nó sẽ không được gọi nếu bất kỳ Ngoại lệ nào được nâng lên. –

+0

Ah, chắc chắn là đủ rồi. Nó có vẻ giống như một con đường dài quanh co và nó trông như thể kết quả không được giải phóng trong 'FMX.Types'. Tôi giả định rằng 'MakeScreenshot' phải được gọi và đưa ra một tham chiếu (ví dụ như một dòng), nhưng nhìn thấy cách bạn đã làm nó làm cho tôi hiểu cách hàm được sử dụng đúng cách. Như tôi đã nói, tôi đã sử dụng 'try..finally' trong một trong các bản sửa lỗi của tôi, nhưng lấy nó ra để thử và đơn giản hóa mã càng nhiều càng tốt để khắc phục vấn đề. –

+2

@Scott - Bạn có thể bỏ qua việc xử lý lỗi trong khi đăng câu hỏi ở đây hoặc trong khi bạn đang thiết kế ban đầu. Tôi chỉ không muốn để nó ra khi tôi nhìn thấy nó được đề cập trên các ý kiến. –

Các vấn đề liên quan