Phân mảnh bộ nhớ là gì?


203

Tôi đã nghe thuật ngữ "phân mảnh bộ nhớ" được sử dụng một vài lần trong bối cảnh phân bổ bộ nhớ động C ++. Tôi đã tìm thấy một số câu hỏi về cách xử lý phân mảnh bộ nhớ, nhưng không thể tìm thấy câu hỏi trực tiếp liên quan đến chính nó. Vì thế:

  • Phân mảnh bộ nhớ là gì?
  • Làm thế nào tôi có thể biết nếu phân mảnh bộ nhớ là một vấn đề cho ứng dụng của tôi? Loại chương trình nào có khả năng bị ảnh hưởng nhất?
  • Những cách phổ biến tốt để đối phó với sự phân mảnh bộ nhớ là gì?

Cũng thế:

  • Tôi đã nghe nói sử dụng phân bổ động rất nhiều có thể làm tăng sự phân mảnh bộ nhớ. Điều này có đúng không? Trong ngữ cảnh của C ++, tôi hiểu tất cả các bộ chứa tiêu chuẩn (std :: string, std :: vector, v.v.) sử dụng cấp phát bộ nhớ động. Nếu chúng được sử dụng trong suốt chương trình (đặc biệt là std :: string), thì việc phân mảnh bộ nhớ có nhiều khả năng là một vấn đề không?
  • Làm thế nào phân mảnh bộ nhớ có thể được xử lý trong một ứng dụng nặng STL?

1
Rất nhiều câu trả lời tuyệt vời, cảm ơn tất cả mọi người!
AshleyBrain

4
Đã có rất nhiều câu trả lời hay, nhưng đây là một số hình ảnh từ một ứng dụng thực tế (Firefox) trong đó sự phân mảnh bộ nhớ là một vấn đề lớn: blog.pavlov.net/2007/11/10/memory-frag sắc
Marius Gedminas

2
@MariusGedminas liên kết không hoạt động nữa, đó là lý do tại sao điều quan trọng là cung cấp một bản tóm tắt ngắn gọn cùng với liên kết hoặc trả lời câu hỏi với bản tóm tắt với liên kết
katta

Chắc chắn nhưng đã hơn nửa thập kỷ
rsethc

3
Dưới đây là một vị trí được cập nhật cho các liên kết được đăng bởi Marius: pavlovdotnet.wordpress.com/2007/11/10/memory-fragmented
TheGameiswar

Câu trả lời:


312

Hãy tưởng tượng rằng bạn có bộ nhớ trống "lớn" (32 byte):

----------------------------------
|                                |
----------------------------------

Bây giờ, phân bổ một số trong đó (5 phân bổ):

----------------------------------
|aaaabbccccccddeeee              |
----------------------------------

Bây giờ, miễn phí bốn phân bổ đầu tiên nhưng không phải thứ năm:

----------------------------------
|              eeee              |
----------------------------------

Bây giờ, hãy thử phân bổ 16 byte. Rất tiếc, tôi không thể, mặc dù có gần gấp đôi số đó miễn phí.

Trên các hệ thống có bộ nhớ ảo, sự phân mảnh ít gặp vấn đề hơn bạn nghĩ, bởi vì phân bổ lớn chỉ cần được đặt liền kề trong không gian địa chỉ ảo , không phải trong không gian địa chỉ vật lý . Vì vậy, trong ví dụ của tôi, nếu tôi có bộ nhớ ảo với kích thước trang là 2 byte thì tôi có thể thực hiện phân bổ 16 byte của mình mà không gặp vấn đề gì. Bộ nhớ vật lý sẽ trông như thế này:

----------------------------------
|ffffffffffffffeeeeff            |
----------------------------------

trong khi bộ nhớ ảo (lớn hơn nhiều) có thể trông như thế này:

------------------------------------------------------...
|              eeeeffffffffffffffff                   
------------------------------------------------------...

Triệu chứng kinh điển của sự phân mảnh bộ nhớ là bạn cố gắng phân bổ một khối lớn và bạn không thể, mặc dù bạn dường như có đủ bộ nhớ trống. Một hậu quả khác có thể xảy ra là quá trình không thể giải phóng bộ nhớ trở lại HĐH (vì có một số đối tượng vẫn được sử dụng trong tất cả các khối được phân bổ từ HĐH, mặc dù các khối đó hiện không được sử dụng).

Chiến thuật để ngăn chặn sự phân mảnh bộ nhớ trong C ++ hoạt động bằng cách phân bổ các đối tượng từ các khu vực khác nhau theo kích thước và / hoặc tuổi thọ dự kiến ​​của chúng. Vì vậy, nếu bạn sẽ tạo ra nhiều đối tượng và phá hủy tất cả chúng sau này, hãy phân bổ chúng từ một nhóm bộ nhớ. Bất kỳ phân bổ nào khác mà bạn thực hiện ở giữa chúng sẽ không nằm trong nhóm, do đó sẽ không được đặt ở giữa chúng trong bộ nhớ, do đó, bộ nhớ sẽ không bị phân mảnh.

Nói chung, bạn không cần phải lo lắng về điều đó, trừ khi chương trình của bạn hoạt động lâu dài và thực hiện nhiều phân bổ và giải phóng. Đó là khi bạn có hỗn hợp các vật thể tồn tại trong thời gian ngắn và tồn tại lâu nhất mà bạn có nguy cơ cao nhất, nhưng thậm chí sau đó mallocsẽ cố gắng hết sức để giúp đỡ. Về cơ bản, bỏ qua nó cho đến khi chương trình của bạn bị lỗi phân bổ hoặc bất ngờ khiến hệ thống bị thiếu bộ nhớ (hãy kiểm tra điều này để ưu tiên!).

Các thư viện tiêu chuẩn không tệ hơn bất kỳ thứ gì khác phân bổ bộ nhớ và các thùng chứa tiêu chuẩn đều có một Alloctham số mẫu mà bạn có thể sử dụng để tinh chỉnh chiến lược phân bổ của chúng nếu thực sự cần thiết.


1
Vậy mỗi ký tự là một byte? Điều này sẽ làm cho "khoảng rộng lớn" của bạn == 32 byte (tôi đoán - không được tính) :) Ví dụ hay, nhưng đề cập đến các đơn vị trước dòng cuối cùng sẽ hữu ích. :)
jalf

1
@jalf: Đúng. Tôi sẽ không đề cập đến tất cả các đơn vị, sau đó nhận ra cuối cùng tôi đã phải. Đã làm việc trên nó trong khi bạn đang bình luận.
Steve Jessop

Thật khó để chọn một "câu trả lời" - rất nhiều câu trả lời hay ở đây và tôi khuyến khích bất cứ ai quan tâm đọc tất cả chúng. Tuy nhiên, tôi nghĩ rằng bạn bao gồm tất cả các điểm quan trọng ở đây.
AshleyBrain

1
"Các thư viện tiêu chuẩn không tệ hơn bất cứ thứ gì phân bổ bộ nhớ". Điều đó sẽ tốt nếu đúng, nhưng việc triển khai các mẫu C ++ tiêu chuẩn như chuỗi & vector có thể có một số hành vi không mong muốn khi chúng thay đổi kích thước. Ví dụ, trong các phiên bản cũ của studio hình ảnh, chuỗi std :: về cơ bản thay đổi kích thước bằng realloc 1.5 * current_size (đến 8 byte gần nhất). Vì vậy, nếu bạn tiếp tục nối vào một chuỗi, bạn có thể tạo ra heap rất dễ dàng, đặc biệt là trên các hệ thống nhúng. Cách phòng thủ tốt nhất là dự trữ lượng không gian bạn dự đoán sử dụng để tránh các reallocs bị ẩn.
locka

1
@ du369: Bộ nhớ ảo không bị phân mảnh bất cứ thứ gì tệ như vật lý. fffffffffffffffflà một phân bổ liền kề trong bộ nhớ ảo, nhưng không có phân bổ liền kề như vậy có thể tồn tại trong bộ nhớ vật lý. Nếu bạn thích nhìn vào đó rằng chúng bị phân mảnh bằng nhau, nhưng không gian ảo lớn hơn nhiều, thì hãy thoải mái nhìn nó theo cách đó. Điểm thực tế quan trọng là việc sử dụng không gian địa chỉ ảo rộng lớn thường đủ để có thể bỏ qua phân mảnh, vì vậy nó giúp bất cứ khi nào nó cho phép tôi thực hiện phân bổ 16 byte của mình.
Steve Jessop

73

Phân mảnh bộ nhớ là gì?

Phân mảnh bộ nhớ là khi hầu hết bộ nhớ của bạn được phân bổ trong một số lượng lớn các khối không liền kề hoặc khối - để lại một tỷ lệ phần trăm tốt trong tổng số bộ nhớ của bạn không được phân bổ, nhưng không thể sử dụng cho hầu hết các tình huống điển hình. Điều này dẫn đến ngoại lệ bộ nhớ hoặc lỗi phân bổ (ví dụ malloc trả về null).

Cách dễ nhất để nghĩ về điều này là tưởng tượng bạn có một bức tường trống lớn mà bạn cần đặt những bức tranh với kích cỡ khác nhau . Mỗi bức ảnh chiếm một kích thước nhất định và rõ ràng bạn không thể chia nó thành những mảnh nhỏ hơn để làm cho nó phù hợp. Bạn cần một chỗ trống trên tường, kích thước của bức tranh, hoặc nếu không bạn không thể đặt nó lên. Bây giờ, nếu bạn bắt đầu treo tranh trên tường và không cẩn thận về cách bạn sắp xếp chúng, bạn sẽ sớm kết thúc với một bức tường phủ một phần hình ảnh và mặc dù bạn có thể có những điểm trống hầu hết các hình ảnh mới sẽ không phù hợp bởi vì chúng lớn hơn những điểm có sẵn Bạn vẫn có thể treo những bức tranh thực sự nhỏ, nhưng hầu hết những bức ảnh đều không phù hợp. Vì vậy, bạn sẽ phải sắp xếp lại (nhỏ gọn) những cái đã có trên tường để có chỗ cho nhiều thứ hơn ..

Bây giờ, hãy tưởng tượng rằng bức tường là bộ nhớ (đống) của bạn và hình ảnh là các đối tượng .. Đó là sự phân mảnh bộ nhớ ..

Làm thế nào tôi có thể biết nếu phân mảnh bộ nhớ là một vấn đề cho ứng dụng của tôi? Loại chương trình nào có khả năng bị ảnh hưởng nhất?

Một dấu hiệu nhận biết rằng bạn có thể đang xử lý sự phân mảnh bộ nhớ là nếu bạn gặp nhiều lỗi phân bổ, đặc biệt là khi tỷ lệ bộ nhớ đã sử dụng cao - nhưng bạn chưa sử dụng hết bộ nhớ - vì vậy về mặt kỹ thuật bạn nên có nhiều dung lượng cho các đối tượng bạn đang cố gắng phân bổ.

Khi bộ nhớ bị phân mảnh nhiều, việc phân bổ bộ nhớ có thể sẽ mất nhiều thời gian hơn vì bộ cấp phát bộ nhớ phải thực hiện nhiều công việc hơn để tìm một không gian phù hợp cho đối tượng mới. Nếu lần lượt bạn có nhiều phân bổ bộ nhớ (mà bạn có thể làm kể từ khi bạn kết thúc với phân mảnh bộ nhớ), thời gian phân bổ thậm chí có thể gây ra sự chậm trễ đáng chú ý.

Những cách phổ biến tốt để đối phó với sự phân mảnh bộ nhớ là gì?

Sử dụng một thuật toán tốt để phân bổ bộ nhớ. Thay vì phân bổ bộ nhớ cho nhiều đối tượng nhỏ, hãy phân bổ trước bộ nhớ cho một mảng liền kề của các đối tượng nhỏ hơn đó. Đôi khi hơi lãng phí khi phân bổ bộ nhớ có thể đi đôi với hiệu năng và có thể giúp bạn tránh khỏi những rắc rối khi phải đối phó với sự phân mảnh bộ nhớ.


10
+1. Tôi vừa xóa câu trả lời được đề xuất của tôi vì ẩn dụ "hình ảnh trên tường" của bạn thực sự, thực sự là một câu trả lời hay, rõ ràng.
ctacke

Tôi muốn nó nhiều hơn nếu bạn nhấn mạnh thực tế là các hình ảnh phải có kích cỡ khác nhau. Nếu không, không có sự phân mảnh sẽ xảy ra.
Bjorn Pollex

1
Thật thú vị, cơ sở dữ liệu bộ nhớ chính đang trở nên hơi thực tế trong những ngày này (với rất nhiều bộ nhớ có sẵn). Trong bối cảnh này, điều đáng chú ý là, đối với các ổ cứng, việc đọc các dòng liên tục từ RAM nhanh hơn nhiều so với khi dữ liệu bị phân mảnh.
Bjorn Pollex

1
Tương tự hình ảnh đẹp với hình ảnh trên tường, nhưng bộ nhớ chính không phải là hai chiều! Tuy nhiên, câu trả lời tốt đẹp, cảm ơn.
AshleyBrain

24

Phân mảnh bộ nhớ là khái niệm tương tự như phân mảnh đĩa: nó đề cập đến không gian bị lãng phí vì các khu vực sử dụng không được đóng gói đủ chặt chẽ với nhau.

Giả sử với một ví dụ đồ chơi đơn giản rằng bạn có mười byte bộ nhớ:

 |   |   |   |   |   |   |   |   |   |   |
   0   1   2   3   4   5   6   7   8   9

Bây giờ, hãy phân bổ ba khối ba byte, tên A, B và C:

 | A | A | A | B | B | B | C | C | C |   |
   0   1   2   3   4   5   6   7   8   9

Bây giờ giải quyết khối B:

 | A | A | A |   |   |   | C | C | C |   |
   0   1   2   3   4   5   6   7   8   9

Bây giờ điều gì xảy ra nếu chúng ta cố gắng phân bổ một khối D bốn byte? Chà, chúng tôi có bốn byte bộ nhớ miễn phí, nhưng chúng tôi không có bốn byte bộ nhớ liền kề , vì vậy chúng tôi không thể phân bổ D! Đây là việc sử dụng bộ nhớ không hiệu quả, vì chúng tôi đã có thể lưu trữ D, nhưng chúng tôi không thể. Và chúng tôi không thể di chuyển C để tạo khoảng trống, vì rất có thể một số biến trong chương trình của chúng tôi đang trỏ đến C và chúng tôi không thể tự động tìm và thay đổi tất cả các giá trị này.

Làm thế nào để bạn biết đó là một vấn đề? Chà, dấu hiệu lớn nhất là kích thước bộ nhớ ảo của chương trình của bạn lớn hơn đáng kể so với dung lượng bộ nhớ bạn thực sự sử dụng. Trong một ví dụ trong thế giới thực, bạn sẽ có nhiều hơn mười byte bộ nhớ, do đó, D sẽ chỉ được cấp phát bắt đầu một byte 9 và các byte 3-5 sẽ không được sử dụng trừ khi sau đó bạn đã phân bổ thứ gì đó dài hơn hoặc nhỏ hơn ba byte.

Trong ví dụ này, 3 byte không phải là một sự lãng phí hoàn toàn, nhưng hãy xem xét một trường hợp bệnh lý hơn trong đó hai phân bổ của một vài byte, ví dụ, cách nhau mười megabyte trong bộ nhớ và bạn cần phân bổ một khối có kích thước 10 megabyte + 1 byte. Bạn phải yêu cầu HĐH cho bộ nhớ ảo hơn mười megabyte để làm điều đó, mặc dù bạn chỉ cần một byte để có đủ dung lượng.

Bạn phòng tránh bằng cách nào? Các trường hợp xấu nhất có xu hướng phát sinh khi bạn thường xuyên tạo và phá hủy các vật thể nhỏ, vì điều đó có xu hướng tạo ra hiệu ứng "pho mát Thụy Sĩ" với nhiều vật thể nhỏ cách nhau bởi nhiều lỗ nhỏ, khiến chúng không thể phân bổ các vật thể lớn hơn trong các lỗ đó. Khi bạn biết bạn sẽ thực hiện việc này, một chiến lược hiệu quả là phân bổ trước một khối bộ nhớ lớn làm nhóm cho các đối tượng nhỏ của bạn, sau đó quản lý thủ công việc tạo các đối tượng nhỏ trong khối đó, thay vì cho phép bộ cấp phát mặc định xử lý nó.

Nói chung, bạn càng phân bổ ít, bộ nhớ càng ít bị phân mảnh. Tuy nhiên, STL giải quyết vấn đề này khá hiệu quả. Nếu bạn có một chuỗi sử dụng toàn bộ phân bổ hiện tại của nó và bạn nối thêm một ký tự cho chuỗi đó, thì nó không chỉ phân bổ lại cho độ dài hiện tại của nó cộng với một chuỗi, nó nhân đôi độ dài của nó. Đây là một biến thể của chiến lược "nhóm phân bổ nhỏ thường xuyên". Chuỗi đang chiếm một phần lớn bộ nhớ để có thể xử lý hiệu quả với các mức tăng nhỏ lặp đi lặp lại mà không thực hiện các phân bổ nhỏ lặp đi lặp lại. Trên thực tế, tất cả các container STL đều làm điều này, vì vậy nhìn chung bạn sẽ không cần phải lo lắng quá nhiều về sự phân mảnh gây ra bởi các container STL tự động phân bổ lại.

Mặc dù tất nhiên STL container không hồ bơi bộ nhớ giữa mỗi khác, vì vậy nếu bạn đang đi để tạo ra nhiều container nhỏ (chứ không phải một vài container mà có được thay đổi kích cỡ thường xuyên), bạn có thể phải quan tâm mình với việc ngăn ngừa sự phân mảnh trong cùng một cách bạn sẽ cho bất kỳ đối tượng nhỏ thường xuyên tạo, STL hay không.


14
  • Phân mảnh bộ nhớ là gì?

Phân mảnh bộ nhớ là vấn đề bộ nhớ trở nên không sử dụng được mặc dù về mặt lý thuyết là có sẵn. Có hai loại phân mảnh: phân mảnh nội bộ là bộ nhớ được phân bổ nhưng không thể được sử dụng (ví dụ: khi bộ nhớ được phân bổ trong các khối 8 byte nhưng chương trình liên tục thực hiện các chỉ định duy nhất khi chỉ cần 4 byte). phân mảnh bên ngoài là vấn đề bộ nhớ trống bị chia thành nhiều phần nhỏ để các yêu cầu phân bổ lớn không thể được đáp ứng mặc dù có đủ bộ nhớ trống tổng thể.

  • Làm thế nào tôi có thể biết nếu phân mảnh bộ nhớ là một vấn đề cho ứng dụng của tôi? Loại chương trình nào có khả năng bị ảnh hưởng nhất?

phân mảnh bộ nhớ là một vấn đề nếu chương trình của bạn sử dụng nhiều bộ nhớ hệ thống hơn dữ liệu paylod thực tế của nó sẽ yêu cầu (và bạn đã loại trừ rò rỉ bộ nhớ).

  • Những cách phổ biến tốt để đối phó với sự phân mảnh bộ nhớ là gì?

Sử dụng một bộ phân bổ bộ nhớ tốt. IIRC, những người sử dụng chiến lược "phù hợp nhất" thường vượt trội hơn nhiều trong việc tránh phân mảnh, nếu chậm hơn một chút. Tuy nhiên, nó cũng đã được chứng minh rằng đối với bất kỳ chiến lược phân bổ nào, đều có những trường hợp xấu nhất bệnh lý. May mắn thay, các mẫu phân bổ điển hình của hầu hết các ứng dụng thực sự tương đối lành tính để các bộ cấp phát xử lý. Có rất nhiều giấy tờ ngoài kia nếu bạn quan tâm đến các chi tiết:

  • Paul R. Wilson, Mark S. Johnstone, Michael Neely và David Boles. Phân bổ lưu trữ động: Một khảo sát và đánh giá quan trọng. Trong Kỷ yếu của Hội thảo quốc tế về Quản lý bộ nhớ năm 1995, Springer Verlag LNCS, 1995
  • Mark S.Jstonestone, Paul R. Wilson. Vấn đề phân mảnh bộ nhớ: Giải quyết? Trong Thông báo ACM SIG-PLAN, tập 34 số 3, trang 26-36, 1999
  • MR Garey, RL Graham và JD Ullman. Phân tích trường hợp xấu nhất của các thuật toán phân bổ bộ nhớ. Trong Hội nghị chuyên đề ACM hàng năm lần thứ tư về lý thuyết tính toán, năm 1972

9

Cập nhật:
Google TCMalloc: Mall-Cache-Threading
Người ta thấy rằng nó khá tốt trong việc xử lý phân mảnh trong một quy trình dài.


Tôi đã phát triển một ứng dụng máy chủ có vấn đề về phân mảnh bộ nhớ trên HP-UX 11,23 / 11,31 ia64.

Nó trông như thế này. Có một quá trình thực hiện phân bổ bộ nhớ và giải quyết và chạy trong nhiều ngày. Và mặc dù không có rò rỉ bộ nhớ, mức tiêu thụ bộ nhớ của quá trình tiếp tục tăng.

Về kinh nghiệm của tôi. Trên HP-UX, rất dễ tìm thấy phân mảnh bộ nhớ bằng gdb HP-UX. Bạn đặt điểm dừng và khi bạn nhấn nó, bạn chạy lệnh này: info heapvà xem tất cả các cấp phát bộ nhớ cho quy trình và tổng kích thước của heap. Sau đó, bạn tiếp tục chương trình của bạn và sau đó một thời gian sau đó bạn lại đạt điểm dừng. Bạn làm lại info heap. Nếu tổng kích thước của heap lớn hơn nhưng số lượng và kích thước phân bổ riêng biệt là như nhau thì có khả năng bạn có vấn đề về cấp phát bộ nhớ. Nếu cần thiết làm điều này kiểm tra vài lần trước.

Cách cải thiện tình hình của tôi là thế này. Sau khi tôi thực hiện một số phân tích với HP-UX gdb, tôi thấy rằng các vấn đề về bộ nhớ là do thực tế là tôi đã sử dụng std::vectorđể lưu trữ một số loại thông tin từ cơ sở dữ liệu. std::vectoryêu cầu dữ liệu của nó phải được giữ trong một khối. Tôi đã có một vài container dựa trên std::vector. Những container này thường xuyên được tái tạo. Thường có các tình huống khi các bản ghi mới được thêm vào cơ sở dữ liệu và sau đó các thùng chứa được tạo lại. Và vì các thùng chứa được tạo lại lớn hơn nên chúng không vừa với các khối bộ nhớ trống có sẵn và thời gian chạy yêu cầu một khối mới lớn hơn từ HĐH. Kết quả là mặc dù không có rò rỉ bộ nhớ, mức tiêu thụ bộ nhớ của quá trình tăng lên. Tôi đã cải thiện tình hình khi tôi thay đổi các container. Thay vì std::vectortôi bắt đầu sử dụngstd::deque trong đó có một cách phân bổ bộ nhớ cho dữ liệu.

Tôi biết rằng một trong những cách để tránh phân mảnh bộ nhớ trên HP-UX là sử dụng Bộ phân bổ khối nhỏ hoặc sử dụng MallocNextGen. Trên RedHat Linux, bộ cấp phát mặc định dường như xử lý việc phân bổ khá tốt cho rất nhiều khối nhỏ. Trên Windows có Low-fragmentation Heapvà nó giải quyết vấn đề về số lượng lớn phân bổ nhỏ.

Hiểu biết của tôi là trong một ứng dụng nặng STL, trước tiên bạn phải xác định vấn đề. Bộ cấp phát bộ nhớ (như trong libc) thực sự xử lý vấn đề của rất nhiều phân bổ nhỏ, điển hình cho std::string(ví dụ trong ứng dụng máy chủ của tôi có rất nhiều chuỗi STL nhưng như tôi thấy khi chạy info heapchúng không gây ra vấn đề gì). Ấn tượng của tôi là bạn cần tránh phân bổ lớn thường xuyên. Thật không may, có những tình huống khi bạn không thể tránh chúng và phải thay đổi mã của mình. Như tôi nói trong trường hợp của tôi, tôi đã cải thiện tình hình khi chuyển sang std::deque. Nếu bạn xác định phân đoạn bộ nhớ của mình, có thể nói về nó chính xác hơn.


6

Sự phân mảnh bộ nhớ rất có thể xảy ra khi bạn phân bổ và phân bổ nhiều đối tượng có kích cỡ khác nhau. Giả sử bạn có bố cục sau trong bộ nhớ:

obj1 (10kb) | obj2(20kb) | obj3(5kb) | unused space (100kb)

Bây giờ, khi obj2được phát hành, bạn có 120kb bộ nhớ không sử dụng, nhưng bạn không thể phân bổ một khối đầy đủ 120kb, vì bộ nhớ bị phân mảnh.

Các kỹ thuật phổ biến để tránh hiệu ứng đó bao gồm bộ đệm vòng và nhóm đối tượng . Trong ngữ cảnh của STL, các phương thức như std::vector::reserve()có thể giúp đỡ.



3

Phân mảnh bộ nhớ là gì?

Khi ứng dụng của bạn sử dụng bộ nhớ động, nó sẽ phân bổ và giải phóng các khối bộ nhớ. Ban đầu, toàn bộ không gian bộ nhớ của ứng dụng của bạn là một khối bộ nhớ trống liền kề. Tuy nhiên, khi bạn phân bổ và các khối miễn phí có kích thước khác nhau, bộ nhớ bắt đầu bị phân mảnh , tức là thay vì một khối tự do tiếp giáp lớn và một số khối được phân bổ liền kề, sẽ có một khối được phân bổ và miễn phí trộn lẫn. Vì các khối miễn phí có kích thước hạn chế, rất khó để sử dụng lại chúng. Ví dụ: bạn có thể có 1000 byte bộ nhớ trống, nhưng không thể phân bổ bộ nhớ cho khối 100 byte, bởi vì tất cả các khối miễn phí dài tối đa 50 byte.

Một cách phân chia khác, không thể tránh khỏi, nhưng ít vấn đề hơn là trong hầu hết các kiến ​​trúc, các địa chỉ bộ nhớ phải được căn chỉnh theo các ranh giới byte 2, 4, 8, v.v. (nghĩa là các địa chỉ phải là bội số của 2, 4, 8, v.v.) Điều này có nghĩa là ngay cả khi bạn có ví dụ: một cấu trúc chứa 3 chartrường, cấu trúc của bạn có thể có kích thước 12 thay vì 3, do thực tế là mỗi trường được căn chỉnh theo ranh giới 4 byte.

Làm thế nào tôi có thể biết nếu phân mảnh bộ nhớ là một vấn đề cho ứng dụng của tôi? Loại chương trình nào có khả năng bị ảnh hưởng nhất?

Câu trả lời rõ ràng là bạn nhận được một ngoại lệ bộ nhớ.

Rõ ràng không có cách di động tốt để phát hiện sự phân mảnh bộ nhớ trong các ứng dụng C ++. Xem câu trả lời này để biết thêm chi tiết.

Những cách phổ biến tốt để đối phó với sự phân mảnh bộ nhớ là gì?

Thật khó khăn trong C ++, vì bạn sử dụng địa chỉ bộ nhớ trực tiếp trong con trỏ và bạn không kiểm soát được ai tham chiếu địa chỉ bộ nhớ cụ thể. Vì vậy, sắp xếp lại các khối bộ nhớ được phân bổ (cách mà trình thu gom rác Java thực hiện) không phải là một tùy chọn.

Bộ cấp phát tùy chỉnh có thể giúp bằng cách quản lý việc phân bổ các đối tượng nhỏ trong một bộ nhớ lớn hơn và sử dụng lại các vị trí miễn phí trong khối đó.


3

Đây là một phiên bản siêu đơn giản cho người giả.

Khi các đối tượng được tạo trong bộ nhớ, chúng sẽ được thêm vào cuối phần được sử dụng trong bộ nhớ.

Nếu một đối tượng không ở cuối phần bộ nhớ đã sử dụng bị xóa, có nghĩa là đối tượng này nằm giữa 2 đối tượng khác, nó sẽ tạo ra một "lỗ hổng".

Đây là những gì được gọi là phân mảnh.


2

Khi bạn muốn thêm một mục vào heap, điều xảy ra là máy tính phải thực hiện tìm kiếm không gian để phù hợp với mục đó. Đó là lý do tại sao phân bổ động khi không được thực hiện trên nhóm bộ nhớ hoặc với phân bổ gộp có thể "làm chậm" mọi thứ. Đối với ứng dụng STL nặng nếu bạn đang thực hiện đa luồng, có bộ cấp phát Hoard hoặc phiên bản TBB Intel .

Bây giờ, khi bộ nhớ bị phân mảnh, hai điều có thể xảy ra:

  1. Sẽ phải có nhiều tìm kiếm hơn để tìm một không gian tốt để dán các đối tượng "lớn". Đó là, với nhiều đối tượng nhỏ nằm rải rác về việc tìm kiếm một đoạn ký ức đẹp, có thể trong những điều kiện nhất định có thể khó khăn (những điều này là cực kỳ.)
  2. Bộ nhớ không phải là một số thực thể dễ đọc. Bộ xử lý được giới hạn ở mức độ chúng có thể giữ và ở đâu. Họ làm điều này bằng cách hoán đổi các trang nếu một mục họ cần là một nơi nhưng địa chỉ hiện tại là một địa điểm khác. Nếu bạn liên tục phải trao đổi các trang, quá trình xử lý có thể chậm lại (một lần nữa, các tình huống cực đoan trong đó điều này ảnh hưởng đến hiệu suất.) Xem bài đăng này trên bộ nhớ ảo .

1

Sự phân mảnh bộ nhớ xảy ra do các khối bộ nhớ có kích thước khác nhau được yêu cầu. Hãy xem xét một bộ đệm 100 byte. Bạn yêu cầu hai ký tự, sau đó một số nguyên. Bây giờ bạn giải phóng hai ký tự, sau đó yêu cầu một số nguyên mới - nhưng số nguyên đó không thể vừa với khoảng trống của hai ký tự. Bộ nhớ đó không thể được sử dụng lại vì nó không nằm trong một khối liền kề đủ lớn để phân bổ lại. Trên hết, bạn đã gọi rất nhiều chi phí phân bổ cho ký tự của mình.

Về cơ bản, bộ nhớ chỉ có các khối có kích thước nhất định trên hầu hết các hệ thống. Khi bạn tách các khối này lên, chúng không thể được nối lại cho đến khi toàn bộ khối được giải phóng. Điều này có thể dẫn đến toàn bộ các khối được sử dụng khi thực sự chỉ có một phần nhỏ của khối được sử dụng.

Cách chính để giảm phân mảnh heap là phân bổ lớn hơn, ít thường xuyên hơn. Trong trường hợp cực đoan, bạn có thể sử dụng một đống được quản lý có khả năng di chuyển các đối tượng, ít nhất là trong mã của riêng bạn. Điều này loại bỏ hoàn toàn vấn đề - dù sao, từ góc độ bộ nhớ. Rõ ràng các đối tượng di chuyển và như vậy có một chi phí. Trong thực tế, bạn chỉ thực sự có vấn đề nếu bạn thường xuyên phân bổ số tiền rất nhỏ. Sử dụng các thùng chứa liền kề (vectơ, chuỗi, v.v.) và phân bổ trên ngăn xếp càng nhiều càng tốt cho con người (luôn là một ý tưởng tốt cho hiệu suất) là cách tốt nhất để giảm bớt nó. Điều này cũng làm tăng sự gắn kết bộ nhớ cache, giúp ứng dụng của bạn chạy nhanh hơn.

Điều bạn nên nhớ là trên hệ thống máy tính để bàn 32 bit x86, bạn có toàn bộ 2GB bộ nhớ, được chia thành các "trang" 4KB (khá chắc chắn rằng kích thước trang giống nhau trên tất cả các hệ thống x86). Bạn sẽ phải gọi một số phân mảnh omgwtfbbq để có một vấn đề. Sự phân mảnh thực sự là một vấn đề của quá khứ, vì các đống hiện đại quá lớn đối với phần lớn các ứng dụng và có sự phổ biến của các hệ thống có khả năng chịu đựng được, chẳng hạn như các đống được quản lý.


0

Loại chương trình nào có khả năng bị ảnh hưởng nhất?

Một ví dụ hay (kinh khủng) cho các vấn đề liên quan đến phân mảnh bộ nhớ là việc phát triển và phát hành "Elemental: War of Magic" , một trò chơi trên máy tính của Stardock .

Trò chơi được xây dựng cho Bộ nhớ 32 bit / 2GB và phải thực hiện nhiều tối ưu hóa trong quản lý bộ nhớ để giúp trò chơi hoạt động trong phạm vi 2 GB bộ nhớ đó. Khi "tối ưu hóa" dẫn đến phân bổ và phân bổ liên tục, sự phân mảnh bộ nhớ heap theo thời gian đã xảy ra và làm cho trò chơi bị sập mỗi lần .

Có một cuộc phỏng vấn "câu chuyện chiến tranh" trên YouTube.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.