Що таке витоки пам’яті та як їх виправити?

Подібно до того, як мозок має вирішальне значення для життя людини, пам’ять не менш важлива для комп’ютерів. Ваша система не може виконувати завдання, якщо їй недостатньо оперативної пам’яті.

Брак оперативної пам’яті та деякі інші проблеми з пам’яттю можуть виникнути через витік пам’яті. Отже, ми покажемо, як можна виявити витоки пам’яті та виправити їх.

Але перед цим давайте розберемося більше про витоки пам’яті та про те, навіщо їх виправляти.

Що таке витік пам’яті?

Уявіть собі автостоянку біля торгового центру, де припарковані всі машини, незалежно від того, закінчили покупці свої покупки чи ні. З часом не залишиться місця для паркування нових транспортних засобів, що призведе до блокування руху та зниження загальної ефективності торгового центру.

Джерело зображення: prateeknima.medium.com

Те саме відбувається і з комп’ютерами!

Комп’ютерні програми, подібні до автомобілів на парковці, можуть забути звільнити використану пам’ять, коли вона більше не потрібна. Це створює навантаження на пам’ять і не залишає місця для безперебійного виконання нових завдань, що призводить до типової помилки пам’яті, яка називається витоком пам’яті.

Приклад коду для демонстрації витоку пам’яті:

void memory_allocation() {
    int *ptr = (int*)malloc(sizeof(int));
}

Наведений вище фрагмент коду C виділяє деяку пам’ять для цілочисельної змінної та призначає її розташування в пам’яті покажчику «ptr». Але немає коду, написаного для звільнення пам’яті, що призводить до витоку пам’яті.

def infinite_rec():
    return infinite_rec()

У наведеному вище коді Python немає базового випадку для завершення функції. Отже, наведений вище код призводить до переповнення стеку та витоку пам’яті.

Поширені причини витоку пам’яті

Недбалість програміста

Перша причина витоку пам’яті – це недбалість програміста.

Програмісти часто розміщують дані в пам’яті, але іноді забувають звільнити їх, коли вони більше не потрібні. У якийсь момент це тримає всю пам’ять зайнятою та не дає місця для майбутніх завдань, що призводить до того, що ми називаємо помилкою «витоку пам’яті».

Мови програмування

Використання мов програмування без вбудованої системи керування пам’яттю може спричинити витік пам’яті.

Такі мови програмування, як Java, мають вбудовані збирачі сміття, які автоматично піклуються про керування пам’яттю.

Але, наприклад, C++ не має вбудованого збирача сміття. Ви, програміст, повинні вручну обробляти пам’ять тут, що призводить до витоку пам’яті кожного разу, коли ви забуваєте очистити пам’ять вручну.

  Microsoft замінить Cortana на ChatGPT і Predibase збирає 12,2 мільйона доларів

Інтенсивне використання кешу

Завдання, дані або програми, які часто використовуються, зберігаються в кеші для швидшого доступу.

Це може призвести до помилки витоку пам’яті, якщо елементи кешуються, але не очищаються, хоча вони застарілі або більше не відповідають вашим поточним моделям використання.

Використання глобальних змінних

Глобальні змінні зберігають виділені дані протягом усього життя програми. Отже, використання більш глобальних змінних використовує багато пам’яті протягом більш тривалого часу та спричиняє витоки пам’яті.

Неефективні структури даних

Розробники часто створюють власні структури даних для реалізації власних функцій. Однак помилки витоку пам’яті з’являються, коли ці структури даних не можуть звільнити використану пам’ять.

Незамкнуті з’єднання

Незакриття файлів, баз даних, мережевих з’єднань тощо після використання також може призвести до помилок витоку пам’яті.

Наслідки витоку пам’яті

Низька продуктивність – Ви спостерігатимете поступове зниження продуктивності програми або системи в міру накопичення витоків пам’яті. Це пов’язано з тим, що не буде доступної пам’яті для виконання завдань, що сповільнить роботу програми.

Програми, що вийшли з ладу – програми не вистачають пам’яті, оскільки витік пам’яті зростає. У якийсь момент, коли пам’ять не доступна, програма аварійно завершує роботу, що призводить до втрати даних і збою програми.

Уразливості системи безпеки – неправильне очищення конфіденційних даних, як-от паролі, особисті дані або конфіденційна інформація, з пам’яті після використання робить ці дані доступними для зловмисників під час витоку пам’яті.

Вичерпання ресурсів. Програми займають більше місця в оперативній пам’яті, коли їм не вистачає пам’яті через витік пам’яті. Це збільшує споживання ресурсів і знижує загальну продуктивність системи.

Як виявити витоки пам’яті?

Ручна перевірка коду

Перегляньте вихідний код, щоб знайти випадки, коли пам’ять виділяється, але не очищається після використання. Шукайте в коді змінні та об’єкти, які використовують пам’ять, але не звільняють її, коли вона більше не потрібна.

Також слідкуйте за основними джерелами зберігання даних, тобто переконайтеся, що структури даних добре керують виділеною пам’яттю.

Статичний аналіз коду

Різноманітні добре розроблені інструменти статичного аналізу аналізують вихідний код вашого компілятора та виявляють випадки витоку пам’яті.

Іноді вони відстежують загальні шаблони, правила та помилки у вашому коді, щоб заздалегідь вгадати витоки пам’яті, ще до того, як вони виникнуть.

Інструменти динамічного аналізу

Ці інструменти використовують динамічний підхід для аналізу коду під час виконання та виявлення витоків пам’яті.

  Криптоіндексні фонди та ETF: що це?

Інструменти динамічного аналізу досліджують поведінку об’єктів, функцій під час виконання та використання ними пам’яті. Ось чому ці інструменти мають високу точність у виявленні витоків пам’яті.

Інструменти профілювання

Інструменти профілювання дають змогу зрозуміти, як програма використовує пам’ять.

Ви, як розробник, можете використовувати цю інформацію, щоб проаналізувати використання пам’яті програмою та оптимізувати методи керування пам’яттю, щоб запобігти збоям програми та проблемам із погіршенням пам’яті.

Бібліотеки виявлення витоку пам’яті

Деякі мови програмування пропонують вбудовані або сторонні бібліотеки для виявлення витоків пам’яті у вашій програмі.

Наприклад, Java має збирач сміття для обробки пам’яті, а C++ пропонує CrtDbg для керування пам’яттю.

Крім того, спеціалізовані бібліотеки, такі як LeakCanary, Valgrind, YourKit тощо, усувають витоки пам’яті в різних типах програм.

Як виправити витік пам’яті?

Визначте витоки пам’яті

Щоб усунути витоки пам’яті, спочатку потрібно їх ідентифікувати.

Ви можете провести перевірку вручну або скористатися якимось автоматизованим інструментом, щоб виявити, чи програма втрачає пам’ять. Ви можете спробувати інші методи виявлення витоків пам’яті, згадані вище, щоб виявити витоки.

Визначте об’єкти, що викликають витік

Після того, як ви підтвердите, що програма втрачає пам’ять на описаному вище кроці, вам слід шукати об’єкти та структури даних, які викликають витік. Зрозумійте, як їм розподіляється пам’ять і де вони повинні звільнити пам’ять.

Створення тестових випадків

Тепер ви звузили точне місце витоку пам’яті. Отже, створіть тестовий приклад, щоб переконатися, що ви правильно визначили джерело витоку пам’яті, і підтвердити, що витік зникає, коли ви виправите ці конкретні об’єкти.

Виправте код

Додайте код звільнення пам’яті, щоб звільнити пам’ять, заблоковану ідентифікованими несправними об’єктами. Якщо код уже існує, оновіть його, щоб переконатися, що він належним чином звільняє використану пам’ять.

Перевірте ще раз

Знову ж таки, використовуйте засоби виявлення витоків або автоматизовані тести, щоб перевірити, чи програма працює належним чином і чи немає блокування пам’яті.

Крім того, перевірте продуктивність і функції програми, щоб переконатися, що оновлення коду не впливає на інші фактори програми.

Найкращі методи запобігання витокам пам’яті

Будьте відповідальним програмістом

Ви повинні пам’ятати про звільнення використаної пам’яті або звільнення покажчиків пам’яті під час написання самого коду. Це мінімізує проблеми витоку пам’яті.

Пам’ятаєте наведений нижче код? Як згадувалося на початку статті, немає фрагмента коду звільнення пам’яті, тому це призводить до витоку пам’яті.

void memory_allocation() {
    int *ptr = (int*)malloc(sizeof(int));
}

Ось як ви, як програміст, можете звільнити пам’ять.

delete ptr;

Використовуйте обладнані мови програмування

Такі мови програмування, як Java або Python, використовують вбудовані бібліотеки керування пам’яттю, такі як збирачі сміття, для автоматичної обробки витоків пам’яті.

  8 способів виправити Для активації iPhone потрібне оновлення

Хоча ви пропускаєте кілька випадків, ці вбудовані інструменти впораються з ними, запобігаючи потенційним витокам пам’яті.

Тому я рекомендую вам використовувати такі мови програмування, у яких вбудовано керування пам’яттю.

Циркулярні посилання

Уникайте циклічних посилань у вашій програмі.

Циклічні посилання слідують за замкнутим циклом об’єктів, які посилаються один на одного. Наприклад, об’єкт a посилається на b, об’єкт b посилається на c, а об’єкт c знову посилається на a, без кінця в циклі. Таким чином, циклічні посилання призводять до нескінченного циклу, що спричиняє витік пам’яті.

Зведіть до мінімуму використання глобальних змінних

Вам слід уникати використання глобальних змінних, якщо ви стурбовані ефективністю пам’яті. Глобальні змінні використовують вашу пам’ять під час роботи програми, що є поганою практикою в управлінні пам’яттю.

Отже, перейдіть на локальні змінні. Вони ефективні для використання пам’яті, оскільки звільняють пам’ять після завершення виклику функції.

Глобальні змінні виглядають так, але використовуйте їх лише за необхідності.

int x = 5 // Global variable
void func(){
    print(x)
}

Але використовуйте локальні змінні наступним чином:

void func(){
    int x = 5 // Local variable
    print(x)
}

Обмеження кеш-пам’яті

Встановіть обмеження на пам’ять, яку може використовувати кеш. Іноді всі завдання, які ви виконуєте в системі, надсилаються в кеш-пам’ять, і це накопичене сховище кешу призводить до витоку пам’яті.

Таким чином, обмеження кешу може запобігти витокам пам’яті.

Випробуйте добре

Включіть тести на витік пам’яті на етапі тестування.

Створюйте автоматизовані тести та охоплюйте всі крайні випадки, щоб виявити витоки пам’яті перед випуском коду для виробництва.

Обладнайте засоби моніторингу

Використовуйте засоби автоматичного профілювання, щоб контролювати використання пам’яті. Регулярне відстеження використання пам’яті допоможе вам визначити потенційні витоки та завчасно їх усунути.

Visual Studio Profiler, NET Memory Profiler і JProfiler є кількома хорошими інструментами в цьому контексті.

Висновок

Ефективне керування пам’яттю необхідне для досягнення максимальної продуктивності програми, і в цьому контексті витоки пам’яті не можна ігнорувати. Для ефективного керування пам’яттю слід усунути витоки пам’яті та запобігти їх виникненню в майбутньому. Ця стаття розповідає про те, як це зробити.

Ми показали вам різні методи виявлення витоків пам’яті, перевірені кроки для їх усунення та практики, яких ви можете дотримуватися, щоб уникнути витоків пам’яті в майбутньому.

Далі ви також можете дослідити, як виправити помилку «недостатньо пам’яті» у Windows протягом 5 хвилин.