Тесты и подход разработки через тестирование (Test-Driven Development, TDD) оказывают значительное влияние на структуру, организацию и качество кода. Они стимулируют разработчиков писать более чистый, модульный и тестируемый код, следуя принципам хорошего дизайна.
Как тесты влияют: Для удобства тестирования разработчики разделяют большие методы и классы на мелкие, с чёткими зонами ответственности. В результате код становится более модульным, а его компоненты проще повторно использовать и изменять.
Как тесты влияют: Для изолированного тестирования классов и методов зависимости инвертируются и предоставляются через интерфейсы или моки. Это приводит к использованию принципа инверсии зависимостей (Dependency Inversion Principle) и снижению связности между модулями.
Как тесты влияют: Если метод или класс берёт на себя слишком много обязанностей, тестировать его становится сложно. Это побуждает разработчиков разделять функциональность, соблюдая SRP.
Как тесты влияют: Разработка через тесты требует определения интерфейсов и API до написания кода. Интерфейсы проектируются с упором на удобство использования, минимальный набор методов и простоту.
Как тесты влияют: Наличие тестов создаёт уверенность, что изменения в коде не сломают существующую функциональность. Это стимулирует регулярный рефакторинг, что улучшает организацию и качество кода.
Как тесты влияют: Хорошо написанные тесты документируют ожидаемое поведение кода, показывая, как он должен использоваться и какие результаты ожидать.
Как тесты влияют: Чтобы код был тестируемым, разработчики чаще соблюдают принципы проектирования:
SOLID: Принципы проектирования для создания гибких и расширяемых систем.
KISS: Сохранение кода простым.
DRY: Избежание повторений.
Как тесты влияют: Чтобы упростить тестирование, разработчики изолируют побочные эффекты (например, работу с БД, сетью, файловой системой) в отдельных компонентах.
Как тесты влияют: Написание тестов до основного кода (TDD) выявляет проблемы в проектировании и требованиях на ранних стадиях.
Если тесты плохо написаны, это может усложнить рефакторинг.
Написание тестов занимает больше времени на ранних этапах, но окупается в долгосрочной перспективе.
Новички могут столкнуться с трудностями при проектировании тестируемого кода.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Возникает в приложениях, использующих базы данных, когда из-за неправильной организации запросов вместо одного оптимального запроса выполняется один основной запрос и N дополнительных запросов. Это может существенно снизить производительность системы.
Представим, что у нас есть две сущности:
User и связанные с ним Posts. Приложение запрашивает список всех пользователей, а затем для каждого пользователя делает отдельный запрос, чтобы получить связанные с ним посты. Вместо одного запроса к базе данных выполняется: Один запрос для получения всех пользователей. N запросов для получения постов каждого пользователя.Итог: N+1 запросов.
Это неэффективно, так как количество запросов растёт линейно с увеличением количества данных, что приводит к большим задержкам.
Вместо выполнения N отдельных запросов для связанных данных, ORM можно настроить так, чтобы они загружались вместе с основным запросом. Например: В Hibernate это можно сделать с помощью
fetch join. В Django ORM используется метод select_related() или prefetch_related().Иногда автоматическая оптимизация ORM не работает идеально. В таких случаях можно написать кастомные SQL-запросы, используя соединения (
JOIN) для получения всех необходимых данных в одном запросе.Если данные редко меняются, можно использовать кэш (например, Redis или Memcached), чтобы минимизировать количество обращений к базе данных.
Использование инструментов мониторинга запросов (например,
SQLAlchemy профайлеры или Django Debug Toolbar) позволяет вовремя выявить проблему N+1 и устранить её.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
READ UNCOMMITTED — видны незавершённые изменения.
READ COMMITTED — видны только зафиксированные изменения.
REPEATABLE READ — предотвращает изменения данных, прочитанных транзакцией.
SERIALIZABLE — полная изоляция, все транзакции выполняются последовательно.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
В Java происходит, когда объекты, которые больше не нужны, продолжают ссылаться в памяти, и сборщик мусора (Garbage Collector, GC) не может их освободить. Это может привести к росту потребления памяти и в конечном итоге к ошибке
OutOfMemoryError.Если объекты добавляются в коллекцию (например,
List, Map) и никогда не удаляются, они могут остаться в памяти, даже если больше не используются.public class MemoryLeakExample {
private static List<Object> list = new ArrayList<>();
public void addToList() {
list.add(new Object()); // Объекты накапливаются, даже если они больше не нужны.
}
}Часто утечка происходит, когда объекты, подписанные на события, не отписываются, даже если больше не используются.
class EventSource {
private List<EventListener> listeners = new ArrayList<>();
public void addListener(EventListener listener) {
listeners.add(listener);
}
}
class LeakingClass {
public LeakingClass(EventSource source) {
source.addListener(new EventListener() {
@Override
public void onEvent() {
// Обработка события
}
});
}
}Неправильное использование объектов с сильными ссылками, которые хранят данные временно, но остаются доступными слишком долго.
class Cache {
private Map<Key, Value> cache = new HashMap<>();
public void put(Key key, Value value) {
cache.put(key, value); // Объекты накапливаются и не очищаются.
}
}Использование лямбда-выражений или анонимных классов, которые удерживают ссылки на внешние объекты.
public class MemoryLeak {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
Runnable task = () -> list.add(new Object()); // Лямбда удерживает ссылку на list
task.run();
}
}Если поток продолжает работать или удерживает ссылки на объекты, которые больше не нужны, это может привести к утечке.
class LeakingThread {
public void startThread() {
Thread thread = new Thread(() -> {
while (true) {
// Бесконечный цикл, который не позволяет завершить поток
}
});
thread.start();
}
}Незакрытые соединения или потоки ввода/вывода могут удерживать ссылки на системные ресурсы.
public void readFile() {
FileInputStream fis = new FileInputStream("file.txt");
// Поток не закрыт
}Использовать профилировщики памяти, такие как VisualVM, YourKit, или JProfiler. Искать "утекшие" объекты, которые удерживаются ссылками, но больше не нужны.
Применять
WeakReference или WeakHashMap для временных данных.Удалять ненужные объекты из коллекций. Освобождать ресурсы, такие как потоки или соединения.
Удалять слушателей (listeners), когда они больше не нужны.
Использовать
try-with-resources для работы с потоками и соединениями.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Интерес к функциональному программированию (Functional Programming) растёт из-за изменений в требованиях к разработке современных приложений, таких как потребность в масштабируемости, управлении сложностью и обработке больших данных. Функциональное программирование предлагает преимущества, которые позволяют решать эти задачи более эффективно.
Функциональное программирование делает акцент на чистых функциях, которые не имеют побочных эффектов. Это упрощает разработку многопоточных приложений, поскольку отсутствует необходимость синхронизации общего состояния. Языки, такие как Scala, Clojure, и Haskell, предоставляют мощные инструменты для работы с параллелизмом и потоками данных, что важно в эпоху многоядерных процессоров.
Современные распределённые системы требуют минимизации работы с изменяемым состоянием. FP, с его подходом к неизменяемым данным, позволяет избегать проблем, связанных с изменяемостью, таких как гонки данных или сложные состояния.
Фреймворки, такие как Apache Spark и Apache Flink, используют концепции функционального программирования (например, операции над коллекциями, как
map, filter, reduce) для работы с большими данными. Это делает FP особенно полезным для обработки потоков данных и анализа больших объёмов информации.Код, написанный в функциональном стиле, становится легче для анализа и тестирования благодаря принципам:
Чистые функции: результат функции зависит только от входных данных.
Неизменяемость данных: данные не меняются после создания.
Декларативный стиль: упор на то, что должно быть сделано, а не как.
Основные императивные языки программирования (Java, Python, JavaScript, C#) добавляют функциональные возможности, такие как лямбды, стримы и функции высшего порядка. Это делает функциональный стиль более доступным для широкой аудитории разработчиков.
Исключение побочных эффектов уменьшает количество ошибок. Код становится проще для тестирования, так как функции можно протестировать в изоляции. Система типов в функциональных языках, таких как Haskell, гарантирует большую безопасность, что снижает вероятность ошибок в рантайме.
FP способствует созданию более модульного кода. Композиция функций позволяет легко строить новые функциональности из существующих компонентов.
Языки, такие как Elm, ReasonML, и фреймворки, например React (с концепцией функциональных компонентов), активно используют принципы функционального программирования. Это позволяет создавать приложения с минимальным количеством багов и высокой предсказуемостью поведения.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Обнаружение самых затратных запросов важно для оптимизации производительности базы данных. Затратные запросы могут потреблять больше ресурсов, чем необходимо, что приводит к увеличению времени выполнения, нагрузке на сервер и замедлению работы системы. Существует несколько подходов и инструментов для выявления таких запросов.
Многие системы управления базами данных (СУБД) поддерживают логирование запросов, выполнение которых занимает больше определённого времени.
Используйте
slow_query_log. Активировать лог:SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- Время выполнения в секундах
Включите
log_min_duration_statement.SET log_min_duration_statement = 1000; -- Логировать запросы, выполняющиеся более 1 секунды
Эти команды дают подробный план выполнения запросов, показывая, как база данных интерпретирует их.
MySQL
EXPLAIN SELECT * FROM orders WHERE status = 'pending';
PostgreSQL
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE status = 'pending';
Используйте таблицу
performance_schema для анализа запросов.SELECT * FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;
Используйте расширение
pg_stat_statements.CREATE EXTENSION pg_stat_statements;
Получите информацию
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;
Проводит анализ SQL-запросов, показывая самые медленные.
Позволяет отслеживать производительность запросов в реальном времени.
Специализированный инструмент для анализа производительности баз данных.
SELECT * FROM sys.schema_unused_indexes;
Проверьте в плане выполнения запросов (
EXPLAIN), используются ли индексы.Используйте нагрузочные тесты, чтобы выявить запросы, создающие "бутылочные горлышки":
Симулирует многопоточную нагрузку на базу данных.
Анализирует производительность системы под высокой нагрузкой.
Включите трассировку (например, в MySQL —
SHOW PROFILE):SET profiling = 1;
SELECT * FROM orders WHERE status = 'pending';
SHOW PROFILE FOR QUERY 1;
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
Это ключевой документ, который описывает подходы к гибкой разработке программного обеспечения. Он основан на 4 базовых ценностях и подкрепляется 12 принципами, которые помогают командам адаптироваться к изменениям, эффективно взаимодействовать и выпускать работающий продукт.
Это подчёркивает, что успех проекта зависит от командной работы и коммуникации, а не от формальностей и процедур.
Фокус на создании ценного результата, а не на написании огромного количества документации.
Постоянное взаимодействие с клиентом помогает лучше понять его потребности и вовремя внести изменения.
Agile приветствует изменения, даже на поздних этапах разработки, чтобы продукт соответствовал текущим требованиям.
Команда должна выпускать работающий продукт регулярно, начиная с ранних этапов проекта.
Это помогает адаптироваться к новым условиям и обеспечивать конкурентоспособность продукта.
Это позволяет заказчику видеть прогресс и получать ценность на протяжении всего проекта.
Это обеспечивает лучшее понимание задач и ускоряет принятие решений.
Доверие, поддержка и автономия способствуют эффективной работе команды.
Несмотря на технологии, живое взаимодействие остаётся наиболее продуктивным.
Конечный результат важнее количества завершённых задач.
Команда должна работать в комфортном ритме, чтобы сохранять продуктивность в долгосрочной перспективе.
Это снижает технический долг и упрощает внесение изменений.
Только необходимые задачи должны быть выполнены, чтобы не перегружать проект.
Это позволяет раскрыть потенциал каждого участника команды.
Ретроспективы помогают команде улучшать процессы и избегать повторения ошибок.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Это паттерн проектирования, который ограничивает создание объекта определённого класса только одним экземпляром и предоставляет глобальную точку доступа к нему. Хотя он полезен для некоторых случаев (например, управление конфигурацией или доступ к ресурсам), его использование может привести к серьёзным проблемам, из-за которых синглтон часто рассматривается как антипаттерн.
Синглтон нарушает принципы единственной ответственности (SRP) и инверсии зависимостей (DIP):
Он выполняет две задачи: управляет своим жизненным циклом (ограничивает создание экземпляров) и предоставляет бизнес-логику.
Жёсткая связь с синглтоном делает код трудно тестируемым и менее гибким.
Синглтон фактически является глобальной переменной, поскольку предоставляет универсальный доступ. Это приводит к:
Затруднению отслеживания, где и как используется объект.
Повышенной связности компонентов, что усложняет их модификацию.
Подменить синглтон в тестах сложно, так как он контролирует свой жизненный цикл. Это затрудняет использование мок-объектов.
Многоразовые тесты могут зависеть от состояния синглтона, что делает тесты нестабильными.
Если реализация синглтона не потокобезопасна, это может привести к:
Состояниям гонки при создании экземпляра.
Непредсказуемому поведению в многопоточных приложениях.
Синглтон делает класс жёстко связанным со своей реализацией, что затрудняет расширение или изменение поведения. Например, нельзя легко заменить синглтон другой реализацией без изменения клиентского кода.
Использование синглтона в нескольких местах увеличивает сложность изоляции компонентов. Выстраивать модульные тесты становится трудно, так как синглтон сохраняет состояние между вызовами.
В распределённых приложениях синглтон не гарантирует, что существует только один экземпляр объекта на всех узлах системы. Это требует дополнительных механизмов синхронизации.
Синглтон часто используется там, где можно обойтись другими механизмами (например, Dependency Injection). Это приводит к избыточной сложности и трудностям в поддержке.
Если объект доступа к базе данных реализован как синглтон, его сложно заменить другим объектом для тестирования.
Если синглтон хранит состояние, это состояние может быть изменено одним компонентом, что приведёт к ошибкам в других частях программы.
В отсутствии надёжного механизма синхронизации попытки создать объект в многопоточном окружении могут вызвать дублирование.
Вместо синглтона передавайте зависимости через конструкторы или специальные контейнеры. Это упрощает тестирование и уменьшает связность.
Используйте шаблоны, которые позволяют создавать объекты по мере необходимости, избегая их глобальности.
Позвольте клиентам явно передавать экземпляры через методы, вместо использования глобального доступа.
Разделите логику синглтона на несколько отдельных классов, каждый из которых отвечает за одну задачу.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
MVC (Model-View-Controller) и MVP (Model-View-Presenter) — это архитектурные паттерны для разделения логики. В MVC контроллер обрабатывает логику и обновляет представление, а в MVP презентер выполняет аналогичную роль, но взаимодействие с представлением более тесное. MVP чаще используется в мобильной разработке, где логика взаимодействия с представлением более сложная.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍2
Это ситуация, когда система становится чрезмерно сложной из-за большого количества зависимостей между модулями, классами, библиотеками или компонентами. Это приводит к сложной поддержке, трудностям в изменении и тестировании системы. Борьба с этой проблемой требует продуманных подходов к управлению зависимостями.
Когда модули напрямую зависят друг от друга, изменения в одном модуле требуют изменения в других.
Два или более модуля зависят друг от друга, создавая замкнутую цепь.
Приводит к сложным цепочкам зависимостей и проблемам совместимости.
Все компоненты напрямую взаимодействуют друг с другом.
Модули выполняют множество задач, что усложняет их декомпозицию.
Вместо того чтобы модули создавали зависимости самостоятельно, передавайте их извне (например, через конструктор или фабрику). Применение DI-контейнеров (Spring, Guice) позволяет централизованно управлять зависимостями.
Single Responsibility Principle (SRP): Каждому модулю должна быть отведена одна задача.
Dependency Inversion Principle (DIP): Зависимости должны строиться на абстракциях, а не на конкретных реализациях.
Разделите проект на слои: UI (пользовательский интерфейс).
Бизнес-логика.
Доступ к данным.
Между слоями используйте чётко определённые интерфейсы.
Разделите систему на независимые модули (например, с помощью Gradle или Maven). Каждый модуль должен иметь минимальный набор зависимостей.
Циклы между модулями или классами усложняют понимание и поддержку системы.
Решения:
Внедряйте интерфейсы или посредников.
Пересмотрите ответственность модулей.
Используйте только необходимые внешние библиотеки. Проверяйте лицензии, совместимость версий и активность сообщества.
Шаблоны проектирования, такие как Фабричный метод, Фасад и Адаптер, могут снизить связность и изолировать зависимости.
Напишите модульные тесты для критически важных компонентов. Используйте интеграционные тесты для проверки взаимодействия между модулями.
Анализируйте кодовую базу и избавляйтесь от устаревших или ненужных зависимостей. Упрощайте модули, разделяйте сложные компоненты.
Создание графа зависимостей помогает визуализировать связи между модулями и найти проблемные места.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1
Это исполняемый код (например, SQL-скрипты), который хранится и выполняется непосредственно в базе данных. Разработка бизнес-логики внутри хранимых процедур является спорной практикой. Она имеет как преимущества, так и недостатки в зависимости от требований системы, архитектуры и команды разработки.
Хранимые процедуры выполняются на сервере базы данных, что уменьшает накладные расходы на передачу данных между приложением и базой. Обработка больших объёмов данных становится быстрее, так как нет необходимости отправлять их в приложение и обратно.
Вся логика предметной области сосредоточена в одном месте (в БД), что облегчает контроль доступа и выполнение важных операций. Удобно для многослойных систем, где несколько клиентских приложений используют одну базу данных.
Вместо передачи большого количества SQL-запросов между сервером базы данных и приложением, выполняется один вызов процедуры.
Системы могут ограничить доступ к таблицам и предоставить доступ только к хранимым процедурам, что снижает риск несанкционированного доступа. Встроенные механизмы контроля прав доступа (например, в PostgreSQL и Oracle).
Современные СУБД поддерживают процедурные языки, такие как PL/pgSQL (PostgreSQL), T-SQL (SQL Server) или PL/SQL (Oracle), которые предоставляют функции, циклы и исключения, что позволяет реализовать сложную логику.
При необходимости переноса части функциональности между разными приложениями (например, Web-приложением и мобильным клиентом) логика в БД остаётся неизменной.
Хранимые процедуры часто сложны для чтения, отладки и тестирования по сравнению с кодом на языке программирования (например, Java, C#, Python). Версионный контроль затруднён, так как БД не всегда удобно интегрируется с системами контроля версий (Git).
Логика, написанная в хранимых процедурах, привязывает систему к конкретной СУБД (например, Oracle или SQL Server). Переход на другую БД становится дорогостоящим и трудоёмким.
Сервер базы данных становится "узким местом" системы. Если бизнес-логика выполняется только на сервере БД, это может привести к перегрузке, особенно при высоком трафике.
Инструменты для юнит-тестирования и автоматизированного тестирования хранимых процедур менее развиты, чем для традиционного кода приложений. Тестирование бизнес-логики требует специальной инфраструктуры (например, отдельной тестовой БД).
Внедрение логики в БД нарушает принципы многослойной архитектуры, такие как Separation of Concerns (разделение ответственности). Логика бизнес-уровня смешивается с уровнем данных.
Разработка и деплой изменений в хранимых процедурах требуют взаимодействия с базой данных, что замедляет процесс внедрения обновлений. Изменения в хранимых процедурах могут повлиять на производительность всей системы.
Написание и оптимизация хранимых процедур требуют специфических навыков SQL и понимания особенностей СУБД. Не все разработчики владеют этими навыками.
Если необходимо минимизировать сетевые вызовы и максимально эффективно обрабатывать большие объёмы данных.
Если бизнес-логика должна быть единой для нескольких клиентов (например, мобильного приложения, веб-сервиса).
Если безопасность и контроль доступа являются приоритетом.
Например, для старых систем или проектов, где миграция на новую архитектуру невозможна.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Это два подхода к определению и проверке типов данных в языках программирования. Они определяют, как язык работает с типами переменных и выражений во время выполнения и компиляции программы.
Означает, что типы переменных определяются во время компиляции и не могут изменяться в процессе выполнения программы. Это свойственно языкам с жёсткой типизацией, например, таким как C, C++, Java, Rust, Kotlin.
Компилятор проверяет, соответствуют ли типы используемых данных их объявленным типам. Ошибки типов обнаруживаются ещё до запуска программы, что повышает её надёжность.
Разработчику часто нужно указывать тип переменной явно (например,
int, float и т.д.). В некоторых языках есть вывод типов (type inference), когда компилятор сам определяет тип переменной, основываясь на её значении.Поскольку типы фиксируются до выполнения программы, это снижает вероятность ошибок, связанных с неправильной обработкой данных.
Поскольку типы известны на этапе компиляции, компилятор может оптимизировать код, что улучшает производительность программы.
Означает, что типы переменных определяются во время выполнения программы. Это свойственно интерпретируемым языкам программирования, таким как Python, JavaScript, Ruby, PHP.
Тип переменной можно изменить в процессе выполнения программы. Например, переменная может сначала хранить число, а затем строку.
Разработчику не нужно указывать тип переменной, так как язык автоматически определяет её тип на основе присвоенного значения.
Программист может сосредоточиться на логике программы, не думая о типах на этапе написания кода.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Происходит, когда программа выделяет память для объектов, но не освобождает её, даже когда эти объекты больше не нужны. Это особенно критично в языках, где управление памятью ложится на разработчика, например, в C или C++. Однако утечки памяти могут возникать и в языках с автоматической сборкой мусора (GC), таких как Python или Java, если есть "живые" ссылки на ненужные объекты.
В функции
memoryLeakExample() память для переменной ptr выделяется динамически с помощью new. Однако память не освобождается с помощью delete, когда указатель выходит из области видимости. При многократном вызове этой функции программа будет терять память с каждым вызовом, что приведёт к утечке и потенциальному исчерпанию ресурсов.#include <iostream>
void memoryLeakExample() {
int* ptr = new int(5); // Динамическое выделение памяти
std::cout << "Выделена память по адресу: " << ptr << " со значением: " << *ptr << std::endl;
// Здесь мы забываем освободить память
} // Утечка памяти: указатель ptr выходит из области видимости, а память не освобождается.
int main() {
for (int i = 0; i < 1000000; ++i) {
memoryLeakExample(); // Функция вызывается многократно
}
return 0;
}
Даже в языках с сборщиком мусора (как в Python), утечки памяти могут происходить из-за сильных ссылок, которые препятствуют сборке мусора.
class LeakyClass:
def __init__(self):
self.data = [0] * 10**6 # Занимаем большой объём памяти
def memory_leak_example():
leaks = [] # Список хранит ссылки на объекты
while True:
leaks.append(LeakyClass()) # Объекты добавляются, но не удаляются
memory_leak_example()
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4