2012-05-26 28 views
11

Theo FastMM4, chương trình Delphi tôi đang làm việc vào lúc này đang bị rò rỉ nhiều chuỗi. AnsiStrings để được chính xác:Ứng dụng Delphi bị rò rỉ AnsiStrings

enter image description here

Đơn đề nghị (http://sourceforge.net/projects/orwelldevcpp/) được sử dụng để rò rỉ rất nhiều kiểu dữ liệu khác, nhưng FastMM4 có thể báo cáo nơi dụ đã được tạo ra, vì vậy tôi cố gắng khắc phục điều đó. Điều kỳ lạ là FastMM4 không báo cáo vị trí của những rò rỉ này.

Chỉnh sửa: có vẻ như sau cùng, hãy xem câu trả lời cho bản sửa lỗi. Dù sao thì, câu hỏi vẫn còn là: thế nào tôi đang rò rỉ những thứ này?

Vì vậy, ehm, thật không may, tôi không biết nên tìm gì. Ý tôi là, nếu những thứ này vượt ra khỏi phạm vi, chúng sẽ tự động được giải phóng ngay (mặc dù chúng nằm trên đống)?

Tôi đã quản lý theo dõi một vài rò rỉ bằng cách nhận xét ngẫu nhiên và xem điều gì sẽ xảy ra với số lượng. Dưới đây là một ví dụ:

// simply passing it a constant creates a leak... 
MainForm.UpdateSplash('Creating extra dialogs...'); 

procedure TMainForm.UpdateSplash(const text : AnsiString); 
begin 
    if not devData.NoSplashScreen then // even if this branch is NOT taken 
    SplashForm.Statusbar.SimpleText := 'blablabla' + text; 
end; 

// And even if the function call itself is placed within a NOT taken branch! 

Dưới đây là một ví dụ về một sự rò rỉ:

// Passing this constants produces leaks... 
procedure TCodeInsList.AddItemByValues(const a, b, c: AnsiString;...); 
var 
    assembleditem : PCodeIns; 
begin 
    new(assembleditem); 
    assembleditem^.Caption:=a; 
    assembleditem^.Line:=b; 
    assembleditem^.Desc:=c; 
    ... 
    fList.Add(assembleditem); 
end; 

// ... even when calling this on WM_DESTROY! 
destructor TCodeInsList.Destroy; 
var 
    I: integer; 
begin 
    for I := 0 to fList.Count - 1 do 
    Dispose(fList[I]); 
    fList.Free; 
    inherited Destroy; 
end; 

// produces leaks!? 

Có khá một loạt các câu hỏi chuỗi rò rỉ ở đây, nhưng không ai thực sự làm rõ những gì mô hình ta nên tìm kiếm. Google cũng không cung cấp.

Chỉnh sửa: vì vậy, tôi phải tìm các hằng số được chuyển. Nhưng tại sao?

Vì vậy, ehm, có ý tưởng nào không?

+0

Tôi không thể tải dự án sourceforge vào lúc này. Có khả năng rằng hình thức chính không bị phá hủy đúng cách và do đó để lại chuỗi lủng lẳng? Điều đó có làm được không? –

+0

delphi phiên bản?Nếu bạn có thể, kiểm tra với aqtime nó sẽ cho bạn biết chính xác nơi rò rỉ là. –

+0

@RichardA: Như bạn có thể thấy trong nguồn \ devcpp.dpr, hình dạng giật gân được giải phóng bằng cách sử dụng 'Miễn phí'. Sẽ thử thêm caFree vào sự kiện OnClose. @ Warren: Tôi rất nghi ngờ aqtime sẽ có thể cho tôi biết nhiều hơn FastMM4, gpProfiler và MemCheck. Và tôi cần phải nâng cấp lên XE quá cho aqtime (bằng cách sử dụng D7 bây giờ). Trường đại học của tôi có một giấy phép D2009 nổi xung quanh một nơi nào đó (không phải cho các folks EE như tôi mặc dù), nhưng aqtime thậm chí không hỗ trợ rằng nó có vẻ. – Orwell

Trả lời

14

Bạn không cần phân bổ chuỗi rõ ràng. Ngoài việc mangling với số lượng tham chiếu, các trường chuỗi của đối tượng hoặc hồ sơ cũng có thể bị rò rỉ. Ví dụ:

type 
    PRecord = ^TRecord; 
    TRecord = record 
    S: string; 
    end; 

procedure TForm1.Button4Click(Sender: TObject); 
var 
    r: PRecord; 
begin 
    GetMem(r, SizeOf(r^)); 
    Initialize(r^); 
    r.S := ' '; 
    FreeMem(r); 

Trong ví dụ trên, vì bộ nhớ của bản ghi được giải phóng, FastMM sẽ chỉ báo cáo chuỗi bị rò rỉ.


Trong mọi trường hợp, FastMM không hiển thị dấu vết ngăn xếp trong hộp thoại không có nghĩa là thiếu thông tin đó. Đảm bảo có FullDebugMode, LogMemoryLeakDetailToFileLogErrorsToFile được xác định trong 'FastMM4Options.inc'. Sau đó tìm tệp '[ExecutableName] _MemoryManager_EventLog.txt' trong thư mục của tệp thực thi.

Đối với ví dụ trên, FastMM sản xuất các tập tin sau đây:

 
--------------------------------2012/5/27 4:34:46-------------------------------- 
A memory block has been leaked. The size is: 12 

Stack trace of when this block was allocated (return addresses): 
40305E 
404B5D 
404AF0 
45C47B 
43D726 
42B0C3 
42B1C1 
43D21E 
76C4702C [GetWindowLongW] 
77AE3CC3 [Unknown function at RtlImageNtHeader] 

The block is currently used for an object of class: Unknown 

The allocation number is: 484 

Current memory dump of 256 bytes starting at pointer address 7EF8DEF8: 
01 00 00 ... 
... 

Bây giờ bạn có thể chạy các ứng dụng, tạm dừng nó và sau đó tìm kiếm các địa chỉ. Đối với các bản ghi ở trên và ứng dụng thử nghiệm, các địa chỉ giải quyết đến:

 
Stack trace of when this block was allocated (return addresses): 
40305E -> _GetMem 
404B5D -> _NewAnsiString 
404AF0 -> _LStrAsg 
45C47B -> TForm1.Button4Click (on FreeMem line) 
43D726 -> TControl.Click 
... 


chỉnh sửa: Thay vì bằng tay nhìn lên địa chỉ, tạo ra một tập tin bản đồ chi tiết qua các tùy chọn liên kết và FastMM sẽ làm điều đó (nhờ Nhận xét của Mason).


Chỉnh sửa của bạn trên câu hỏi phản ánh một sự rò rỉ tương tự như ví dụ trên. Nếu 'fList' là số TList thông thường, nó chỉ giữ con trỏ và không có kiến ​​thức về những gì con trỏ trỏ đến. Do đó khi bạn vứt bỏ con trỏ, chỉ bộ nhớ được cấp phát cho con trỏ chính nó được giải phóng, không phải là các trường của bản ghi. Vì vậy, các rò rỉ có gì để làm hằng thông qua chức năng nhưng cũng giống như mô hình dưới đây:

Đối với hồ sơ đăng ký bán, mã nên định kiểu con trỏ đến kiểu của nó:

Dispose(PCodeIns(p)); 

Vì vậy, 'TCodeInsList.Destroy' của bạn nên là:

destructor TCodeInsList.Destroy; 
var 
    I: integer; 
begin 
    for I := 0 to fList.Count - 1 do 
    Dispose(PCodeIns(fList[I])); 
    fList.Free; 
    inherited Destroy; 
end; 


cuối cùng, mô hình bạn đang tìm kiếm dường như tìm kiếm những nơi mà ý đồ mã để reco miễn phí rds (ít đối tượng) có các trường chuỗi. Tìm kiếm Dispose, ít khả năng là FreeMem, thậm chí ít khả năng là FreeInstance để giải phóng bộ nhớ của các đối tượng/bản ghi mà FastMM hiển thị khi bộ nhớ được cấp phát bị rò rỉ có thể giúp ích.

+0

Hmm, tôi phải bỏ qua nó khi tôi đang tìm kiếm các rò rỉ khác, nhưng có, bạn nói đúng, FastMM4 hiển thị một số thông tin: 102DF8 [SynEditKeyCmds] [SynEditKeyCmds] [@ GetMem]. Nhân với 40000, bên trong một tệp văn bản 150MiB để có được ý tưởng. Cảm ơn, sẽ nhìn vào nó. – Orwell

+1

BTW rằng công việc sẽ dễ dàng hơn rất nhiều nếu bạn yêu cầu trình liên kết tạo một tệp bản đồ chi tiết. Sau đó FastMM có thể thực hiện tra cứu cho bạn. –

+0

@Mason - Cảm ơn, tôi đã tự hỏi những gì tôi đã mất tích trong ứng dụng thử nghiệm. :) –

4

Bạn phải tự động xóa các chuỗi. Tôi đã nhìn thấy một vài cách để vít mà lên, mặc dù.

Đầu tiên là nếu bạn đang làm việc với cấu trúc dữ liệu chuỗi trực tiếp có thể phá vỡ số tham chiếu. Đây là khả năng nhất, với số lượng chuỗi bạn đang bị rò rỉ.

Người còn lại gọi Halt và để lại tham chiếu chuỗi trên ngăn xếp. Nhưng bạn sẽ không để lại 40.000 tham chiếu chuỗi trên ngăn xếp, vì vậy tôi sẽ tìm mã được truyền qua một chuỗi và sau đó tìm kiếm với số tham chiếu của nó.

+0

Tôi chắc chắn rằng tôi không sử dụng Halt ở bất cứ đâu. Tôi đang sử dụng Exit khá thường xuyên, nhưng điều đó không quan trọng lắm. Và uhm, nope, tôi không rối tung xung quanh với chỉ số zero'th chẳng hạn. – Orwell

+4

@Orwell 'exit' là an toàn để được sử dụng ở bất cứ nơi nào: nó sẽ đi đến khối' try..finally' ẩn được tạo cho bất kỳ chuỗi cục bộ nào và xử lý số lượng tham chiếu như mong đợi. –

1

Với các từ ngắn, các loại chuỗi dựng sẵn Delphi được tính tham chiếu. Việc phân bổ bộ nhớ và các phương thức xử lý không quan tâm đến việc cập nhật đếm tham chiếu, do đó trình biên dịch không biết, các chuỗi trong hồ sơ của bạn có thể được giải phóng thực sự.

Không khuyến khích xác định bản ghi với các loại chuỗi được tính tham chiếu. Tôi đã có cùng một sự nhầm lẫn trước đây. Nếu bạn nhìn vào nguồn thư viện Delphi. Bạn sẽ tìm thấy nhiều bản ghi có PChar không phải chuỗi.

Some discuss about records

1

Cách phổ biến nhất để rò rỉ dây là phải có một kỷ lục có chứa một chuỗi và một con trỏ đến kỷ lục đó. Nếu bạn chỉ cần thực hiện một Dispose() của con trỏ đó, trình biên dịch sẽ chỉ giải phóng con trỏ chứ không phải tất cả các thứ trong bản ghi lân cận. Luôn đảm bảo rằng mã vứt bỏ của bạn cho trình biên dịch biết bạn đang xử lý những gì.

Ví dụ: giả sử trong TTreeView tôi đặt PMyRecord =^MyRecord trong Node.Data. Nếu, cuối cùng bạn lặp đi lặp lại mặc dù tất cả các nút và chỉ cần bỏ đi (Node.Data) thì bất kỳ chuỗi nào trong MyRecord sẽ không được xử lý đúng cách. Trong trường hợp này, bạn cần phải Dispose (PMyRecord (Node.Data)).

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