Фрагментація пам’яті - це те саме поняття, що і фрагментація диска: це стосується витраченого простору, оскільки використовувані області недостатньо упаковані разом.
Припустимо для простого іграшкового прикладу, що у вас є десять байт пам'яті:
| | | | | | | | | | |
0 1 2 3 4 5 6 7 8 9
Тепер виділимо три трибайтові блоки, назви A, B і C:
| A | A | A | B | B | B | C | C | C | |
0 1 2 3 4 5 6 7 8 9
Тепер розмістіть блок B:
| A | A | A | | | | C | C | C | |
0 1 2 3 4 5 6 7 8 9
Тепер що станеться, якщо ми спробуємо виділити чотирибайтовий блок D? Ну, у нас немає чотирьох байтів пам'яті, але у нас немає чотирьох суміжних байтів пам'яті, тому ми не можемо виділити D! Це неефективне використання пам’яті, оскільки ми повинні були вміти зберігати D, але цього нам не вдалося. І ми не можемо перемістити C, щоб звільнити місце, тому що, швидше за все, деякі змінні в нашій програмі вказують на C, і ми не можемо автоматично знайти та змінити всі ці значення.
Звідки ти знаєш, що це проблема? Ну, найбільша ознака полягає в тому, що розмір віртуальної пам’яті вашої програми значно перевищує об’єм пам’яті, який ви насправді використовуєте. У прикладі реального світу у вас буде набагато більше десяти байт пам'яті, тож D просто виділиться, починаючи байт 9, а байти 3-5 залишаться невикористаними, якщо пізніше ви не виділили щось три байти довгими або меншими.
У цьому прикладі 3 байти - це не багато марна трата, але розглянемо більш патологічний випадок, коли два виділення пару байтів, наприклад, на десять мегабайт в пам'яті, і вам потрібно виділити блок розміром 10 мегабайт + 1 байт. Для цього вам потрібно попросити ОС на понад десять мегабайт більше віртуальної пам’яті, навіть якщо ви лише один байт соромляться того, що вже вистачає місця.
Як вам запобігти? Найгірші випадки, як правило, виникають, коли ви часто створюєте та знищуєте дрібні предмети, оскільки це, як правило, створює ефект "швейцарського сиру" з багатьма дрібними предметами, розділеними багатьма маленькими отворами, що робить неможливим виділення більших предметів у цих отворах. Коли ви знаєте, що ви будете робити це, ефективна стратегія полягає в тому, щоб заздалегідь виділити великий блок пам'яті як пул для своїх маленьких об’єктів, а потім вручну керувати створенням малих об'єктів у цьому блоці, а не дозволяти алокатор за замовчуванням обробляє його.
Загалом, чим менше виділень, тим менше ймовірність, що пам'ять буде фрагментованою. Однак STL займається цим досить ефективно. Якщо у вас є рядок, який використовує всю її поточну розподіл, і ви додаєте до неї один символ, він не просто переділяє його на поточну довжину плюс один, він подвоює його довжину. Це зміна стратегії "пул для частих невеликих виділень". Рядок захоплює великий шматок пам'яті, щоб він міг ефективно справлятися з повторними невеликими збільшеннями розміру, не роблячи повторних невеликих перерозподілів. Усі контейнери STL насправді роблять подібне, тому, як правило, вам не потрібно буде надто турбуватися про фрагментацію, викликану автоматичним перерозподілом контейнерів STL.
Хоча, звичайно, контейнери STL не об'єднують пам'ять між собою, тому якщо ви збираєтеся створити багато маленьких контейнерів (а не кілька контейнерів, які часто змінюють розмір), можливо, вам доведеться потурбуватися про те, щоб запобігти фрагментації таким же чином, як і ви буде для будь-яких часто створюваних невеликих об'єктів, STL чи ні.