2010-04-02 33 views
12

Tôi đang sử dụng Delphi 2009 có trình quản lý bộ nhớ FastMM4 được tích hợp sẵn trong đó.Tại sao bộ nhớ chương trình Delphi của tôi tiếp tục phát triển?

Chương trình của tôi đọc và xử lý tập dữ liệu lớn. Tất cả bộ nhớ được giải phóng một cách chính xác bất cứ khi nào tôi xóa bộ dữ liệu hoặc thoát khỏi chương trình. Nó không có rò rỉ bộ nhớ nào cả.

Sử dụng CurrentMemoryTùy dụng sử dụng được đưa ra trong câu trả lời của spenwarr cho: How to get the Memory Used by a Delphi Program, tôi đã hiển thị bộ nhớ được FastMM4 sử dụng trong khi xử lý.

Điều gì dường như đang xảy ra là bộ nhớ được sử dụng đang tăng lên sau mỗi chu kỳ và chu kỳ phát hành. ví dụ:

1,456 KB được sử dụng sau khi bắt đầu chương trình của tôi mà không có tập dữ liệu.

218,455 KB được sử dụng sau khi tải tập dữ liệu lớn.

71,994 KB sau khi xóa hoàn toàn tập dữ liệu. Nếu tôi thoát ra tại thời điểm này (hoặc bất kỳ điểm nào trong ví dụ của tôi), không có rò rỉ bộ nhớ nào được báo cáo.

271,905 KB được sử dụng sau khi tải lại cùng một tập dữ liệu.

125,443 KB sau khi xóa hoàn toàn tập dữ liệu.

325,519 KB được sử dụng sau khi tải lại cùng một tập dữ liệu.

179,059 KB sau khi xóa hoàn toàn tập dữ liệu.

378,752 KB được sử dụng sau khi tải lại cùng một tập dữ liệu.

Dường như việc sử dụng bộ nhớ của chương trình của tôi đang tăng khoảng 53,400 KB theo mỗi chu kỳ tải/xóa. Trình quản lý tác vụ xác nhận rằng điều này thực sự đang diễn ra.

Tôi đã nghe nói rằng FastMM4 không phải lúc nào cũng giải phóng toàn bộ bộ nhớ của chương trình về hệ điều hành khi các đối tượng được giải phóng để nó có thể giữ bộ nhớ xung quanh khi cần. Nhưng điều này liên tục phát triển làm phiền tôi. Vì không có rò rỉ bộ nhớ nào được báo cáo, tôi không thể xác định được sự cố.

Có ai biết tại sao điều này xảy ra, nếu điều đó xấu, và nếu có bất cứ điều gì tôi có thể hoặc nên làm gì?


Cảm ơn dthorpe và Mason cho câu trả lời của bạn. Bạn đã cho tôi suy nghĩ và thử những điều khiến tôi nhận ra mình đang thiếu một cái gì đó. Quá trình gỡ lỗi chi tiết là bắt buộc.

Khi nó quay ra, tất cả các cấu trúc của tôi đã được giải phóng thích hợp khi thoát. Nhưng việc phát hành bộ nhớ sau mỗi chu kỳ trong thời gian chạy là không. Nó đang tích lũy các khối bộ nhớ mà thông thường sẽ gây ra một rò rỉ có thể phát hiện được khi thoát ra nếu việc dọn dẹp của tôi không chính xác - nhưng đúng vậy.

Có một số StringLists và các cấu trúc khác mà tôi cần để xóa giữa các chu kỳ. Tôi vẫn không chắc chắn làm thế nào chương trình của tôi làm việc một cách chính xác với các dữ liệu bổ sung vẫn còn đó từ các chu kỳ trước đó nhưng nó đã làm. Tôi có lẽ sẽ nghiên cứu thêm.

Câu hỏi này đã được trả lời. Cảm ơn bạn đã giúp đỡ.

+0

IMHO, bạn nên thêm câu trả lời và chấp nhận thay vì chỉnh sửa câu hỏi. Sẽ dễ dàng hơn nếu ai đó tìm kiếm câu hỏi, câu trả lời và học hỏi từ kinh nghiệm của bạn. – EMBarbosa

Trả lời

25

Tiện ích CurrentMemoryUsage mà bạn đã liên kết với báo cáo kích thước bộ làm việc của ứng dụng.Bộ làm việc là tổng số trang không gian địa chỉ bộ nhớ ảo được ánh xạ tới các địa chỉ bộ nhớ vật lý. Tuy nhiên, một số hoặc nhiều trang trong số đó có thể có rất ít dữ liệu thực được lưu trữ trong chúng. Do đó, bộ làm việc là "giới hạn trên" của bộ nhớ mà quá trình của bạn đang sử dụng. Nó cho biết bao nhiêu không gian địa chỉ được dành riêng để sử dụng, nhưng nó không chỉ ra số tiền thực sự được cam kết (thực sự nằm trong bộ nhớ vật lý) hoặc số trang đã cam kết thực sự được sử dụng bởi ứng dụng của bạn.

Hãy thử điều này: sau khi bạn thấy kích thước bộ làm việc của bạn tăng lên sau một vài lần chạy thử, hãy thu nhỏ cửa sổ chính của ứng dụng. Bạn sẽ rất có thể thấy kích thước thiết lập làm việc giảm đáng kể. Tại sao? Bởi vì Windows thực hiện một cuộc gọi SetProcessWorkingSetSize (-1) khi bạn giảm thiểu một ứng dụng loại bỏ các trang không sử dụng và thu nhỏ tập làm việc ở mức tối thiểu. Hệ điều hành không làm điều này trong khi cửa sổ ứng dụng có kích thước bình thường vì việc giảm kích thước bộ làm việc quá thường xuyên có thể làm cho hiệu suất tồi tệ hơn bằng cách buộc dữ liệu phải được tải lại từ tệp hoán đổi.

Để tìm hiểu chi tiết hơn: Ứng dụng Delphi của bạn cấp phát bộ nhớ trong các khối khá nhỏ - một chuỗi ở đây, một lớp ở đó. Cấp phát bộ nhớ trung bình cho một chương trình thường nhỏ hơn vài trăm byte. Thật khó để quản lý phân bổ nhỏ như thế này một cách hiệu quả trên quy mô toàn hệ thống, do đó hệ điều hành không. Nó quản lý khối bộ nhớ lớn một cách hiệu quả, đặc biệt là kích thước trang bộ nhớ ảo 4k và phạm vi địa chỉ bộ nhớ ảo tối thiểu 64k.

Điều này thể hiện sự cố đối với các ứng dụng: các ứng dụng thường phân bổ các đoạn nhỏ, nhưng hệ điều hành phát hiện ra bộ nhớ trong các khối khá lớn. Phải làm gì? Trả lời: suballocate. Bộ quản lý bộ nhớ của thư viện thời gian chạy Delphi và trình quản lý bộ nhớ thay thế FastMM (và các thư viện thời gian chạy về mọi ngôn ngữ hoặc bộ công cụ khác trên hành tinh) đều tồn tại để thực hiện một việc: xử lý các khối bộ nhớ lớn từ hệ điều hành trở nên nhỏ hơn các khối được ứng dụng sử dụng. Theo dõi vị trí của tất cả các khối nhỏ, chúng lớn đến cỡ nào và liệu chúng có bị "rò rỉ" hay không đòi hỏi một số bộ nhớ - được gọi là chi phí.

Trong trường hợp phân bổ bộ nhớ nặng/deallocation, có thể có các tình huống trong đó bạn phân bổ 99% những gì bạn đã phân bổ, nhưng kích thước thiết lập của quy trình chỉ co lại bằng 50%. Tại sao? Thông thường, điều này là do phân mảnh heap: một khối bộ nhớ nhỏ vẫn được sử dụng trong một trong các khối lớn mà trình quản lý bộ nhớ Delphi thu được từ hệ điều hành và được chia ra trong nội bộ. Số lượng bộ nhớ nội bộ được sử dụng nhỏ (300 byte) nhưng vì nó ngăn cản trình quản lý heap phát hành khối lớn mà nó quay trở lại hệ điều hành, phần đóng góp làm việc của đoạn nhỏ 300 byte đó giống như 4k (hoặc 64k tùy thuộc vào việc đó là các trang ảo hay không gian địa chỉ ảo - tôi không thể nhớ lại). Trong một hoạt động nặng bộ nhớ liên quan đến megabyte phân bổ bộ nhớ nhỏ, phân mảnh đống là rất phổ biến - đặc biệt nếu phân bổ bộ nhớ cho những thứ không liên quan đến hoạt động chuyên sâu bộ nhớ đang diễn ra cùng lúc với công việc lớn. Ví dụ, nếu crunching thông qua hoạt động cơ sở dữ liệu 80MB của bạn cũng xuất hiện trạng thái đến một ListBox khi nó tiến triển, các chuỗi được sử dụng để báo cáo trạng thái sẽ nằm rải rác trong đống giữa các khối bộ nhớ cơ sở dữ liệu. Khi bạn giải phóng tất cả các khối bộ nhớ được sử dụng bởi tính toán cơ sở dữ liệu, các chuỗi listbox vẫn còn ở đó (đang sử dụng, không bị mất) nhưng chúng nằm rải rác khắp nơi, có khả năng chiếm toàn bộ khối lớn của hệ điều hành cho mỗi chuỗi nhỏ.

Hãy thử mẹo tối thiểu hóa cửa sổ để xem liệu điều đó có làm giảm bộ công việc của bạn hay không. Nếu có, bạn có thể giảm giá "mức độ nghiêm trọng" rõ ràng của các số được trả lại bởi bộ đếm làm việc. Bạn cũng có thể thêm một cuộc gọi đến SetProcessWorkingSetSize sau khi hoạt động tính toán lớn của bạn để dọn dẹp các trang không còn sử dụng nữa.

+0

Anh ta không nên nhìn thấy nhiều bộ nhớ bị mất để phân mảnh đống. FastMM được thiết kế đặc biệt để giữ phân mảnh đến mức tối thiểu tuyệt đối. –

+5

Vâng, tôi biết thiết kế của FastMM. Tôi đã giúp phê bình việc thực hiện và đưa nó vào sản phẩm Delphi. FastMM vẫn còn nhạy cảm với kích thước workings phóng đại. – dthorpe

+0

Cảm ơn nhưng giảm thiểu có tác dụng tối thiểu và không khắc phục được sự cố. Tôi cũng đã thử thói quen dọn dẹp bộ nhớ của Gabr tại: http://stackoverflow.com/questions/2031577/can-memory-be-cleaned-up/2033393#2033393 và điều đó cũng không giúp được gì. p.s. Câu trả lời của Barry có rất nhiều thông tin. – lkessler

1

Bạn đang sử dụng loại bộ dữ liệu nào?Nếu nó được triển khai hoàn toàn trong Delphi, (không gọi ra mã khác với một trình quản lý bộ nhớ khác, như Midas,) bạn có thể thử cố tình làm rò rỉ tập dữ liệu.

Tôi giả định rằng tập dữ liệu của bạn ở trên biểu mẫu và dữ liệu được tự động giải phóng khi biểu mẫu xóa các thành phần của nó. Thử đặt MyDataset := nil; vào OnDestroy của biểu mẫu của bạn. Điều này sẽ đảm bảo rằng tập dữ liệu bị rò rỉ và cũng có thể là mọi thứ mà tập dữ liệu sở hữu. Hãy thử rằng sau khi tải một lần và một lần nữa sau khi tải hai lần và so sánh các báo cáo rò rỉ, và xem nếu đó cung cấp cho bạn bất cứ điều gì hữu ích.

+0

@Mason: Có thể thuật ngữ "tập dữ liệu" của tôi là gây hiểu lầm. Bởi tập dữ liệu tôi có nghĩa là một tập tin đầu vào văn bản hoặc Unicode dựa trên một kích thước lớn mà tôi đang đọc như một FileStream, cũng như các cấu trúc dữ liệu nội bộ mà tôi đang tạo ra từ nó. Tôi không sử dụng cơ sở dữ liệu. Chương trình của tôi là một EXE duy nhất. Tôi không gọi bất kỳ DLL bên ngoài. Sự nghi ngờ của tôi, do kích thước lớn của bộ nhớ bị mất, rằng nó có liên quan đến bộ phim đó và có thể là bộ đệm mà tôi đang tải. Nhưng không có rò rỉ nào được báo cáo bởi EurekaLog hoặc bởi AQTime. Bây giờ tôi đang bị gỡ lỗi nặng nề. – lkessler

0

Bạn là bộ nhớ bị rò rỉ một nửa; chắc chắn. Bạn đang rò rỉ bộ nhớ trong khi chương trình đang chạy, nhưng khi bạn đóng chương trình, tập dữ liệu của bạn được giải phóng đúng cách để FastMM (đúng) không báo cáo.

Xem chi tiết này: My program never releases the memory back. Why?

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