Я́ков Леони́дович Шра́йберг (род. 1 сентября 1952, Житомир, УССР) — российский специалист по библиотечно-информационным технологиям, один из драйверов цифровых библиотек и ИТ-инфраструктур для науки и образования.
Майкл Озер Рабин ( иврит : מִיכָאֵל עוזר רַבִּין ; родился 1 сентября 1931 года) — лауреат премии Тьюринга; внес фундаментальный вклад в теорию автоматов, вероятностные алгоритмы и криптографию.
1982 — запуск Commodore 64. Легендарный домашний компьютер, сыгравший ключевую роль в популяризации домашних ПК и программирования.
1992 — релиз Super Mario Kart в Северной Америке (SNES). Культовая гоночная игра, которая стала одной из самых продаваемых на SNES и штурмовала жанр аркадных гонок.
1994 — «Virtual Library» (Библиотека Конгресса США). Библиотека Конгресса США начинает проект виртуальной библиотеки — один из первых в мире масштабных цифровых архивов онлайн, важная веха в развитии электронных библиотек и доступа к знаниям.
#Biography #Birth_Date #Events #01Сентября
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Основы ООП в Java
Глава 4. Полиморфизм
Поведение через суперкласс и интерфейс
Полиморфизм (от греч. "много форм") — это способность объектов разных классов отвечать на один и тот же вызов метода по-разному. Это ключевой принцип ООП, который делает код гибким и расширяемым: вы можете работать с объектами через общий тип, не зная их конкретного класса.
Типы полиморфизма в Java:
Compile-time (статический): Через перегрузку методов (overloading) — выбор метода на этапе компиляции.
Runtime (динамический): Через переопределение методов (overriding) — выбор на этапе выполнения, основан на типе объекта.
Поведение через суперкласс: Наследование и overriding
Через суперкласс полиморфизм достигается, когда подкласс переопределяет методы суперкласса. Ссылка суперкласса может указывать на объект подкласса, и вызов метода выполнит версию подкласса (динамическая диспетчеризация).
Пример
Теперь полиморфизм в действии:
Нюанс: Если метод не переопределен, вызовется версия суперкласса.
Это позволяет писать общий код:
например, массив Animal[] animals = {new Dog(), new Cat()}; и цикл for (Animal a : animals) a.makeSound(); — каждый издаст свой звук.
Поведение через интерфейс: Implements и полиморфизм
Интерфейс — это контракт, определяющий методы без реализации. Полиморфизм через интерфейсы достигается, когда классы реализуют (implements) интерфейс, предоставляя свою реализацию. Ссылка интерфейса может указывать на любой реализующий объект.
Синтаксис:
Класс:
Пример интерфейса SoundMaker:
Теперь классы реализуют его:
Полиморфизм:
Интерфейс обеспечивает полиморфизм без наследования: классы не связаны иерархией, но объединены контрактом.
#Java #для_новичков #beginner #poliphormizm
Глава 4. Полиморфизм
Поведение через суперкласс и интерфейс
Полиморфизм (от греч. "много форм") — это способность объектов разных классов отвечать на один и тот же вызов метода по-разному. Это ключевой принцип ООП, который делает код гибким и расширяемым: вы можете работать с объектами через общий тип, не зная их конкретного класса.
Типы полиморфизма в Java:
Compile-time (статический): Через перегрузку методов (overloading) — выбор метода на этапе компиляции.
Runtime (динамический): Через переопределение методов (overriding) — выбор на этапе выполнения, основан на типе объекта.
Поведение через суперкласс: Наследование и overriding
Через суперкласс полиморфизм достигается, когда подкласс переопределяет методы суперкласса. Ссылка суперкласса может указывать на объект подкласса, и вызов метода выполнит версию подкласса (динамическая диспетчеризация).
Пример
// Animal.java
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void makeSound() {
System.out.println(name + " издает звук.");
}
}
// Dog.java
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " лает: Гав!");
}
}
// Cat.java (еще один подкласс)
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " мяукает: Мяу!");
}
}
Теперь полиморфизм в действии:
public class Main {
public static void main(String[] args) {
Animal animal1 = new Dog("Шарик"); // Ссылка Animal на объект Dog
Animal animal2 = new Cat("Мурка"); // Ссылка Animal на объект Cat
animal1.makeSound(); // Вывод: Шарик лает: Гав! (версия Dog)
animal2.makeSound(); // Вывод: Мурка мяукает: Мяу! (версия Cat)
}
}
Здесь Animal animal1 = new Dog("Шарик"); Тип ссылки — Animal (статический тип), тип объекта — Dog (динамический тип).
Вызов makeSound(): JVM смотрит на динамический тип и вызывает переопределенную версию.
Нюанс: Если метод не переопределен, вызовется версия суперкласса.
Это позволяет писать общий код:
например, массив Animal[] animals = {new Dog(), new Cat()}; и цикл for (Animal a : animals) a.makeSound(); — каждый издаст свой звук.
Поведение через интерфейс: Implements и полиморфизм
Интерфейс — это контракт, определяющий методы без реализации. Полиморфизм через интерфейсы достигается, когда классы реализуют (implements) интерфейс, предоставляя свою реализацию. Ссылка интерфейса может указывать на любой реализующий объект.
Синтаксис:
public interface InterfaceName { void method(); }
Класс:
public class ClassName implements InterfaceName { @Override public void method() { ... } }
Пример интерфейса SoundMaker:
public interface SoundMaker {
void makeSound(); // Абстрактный метод
}
Теперь классы реализуют его:
// Dog.java (теперь implements SoundMaker, без extends для простоты)
public class Dog implements SoundMaker {
private String name;
public Dog(String name) {
this.name = name;
}
@Override
public void makeSound() {
System.out.println(name + " лает: Гав!");
}
}
// Cat.java
public class Cat implements SoundMaker {
private String name;
public Cat(String name) {
this.name = name;
}
@Override
public void makeSound() {
System.out.println(name + " мяукает: Мяу!");
}
}
Полиморфизм:
public class Main {
public static void main(String[] args) {
SoundMaker sound1 = new Dog("Шарик"); // Ссылка интерфейса на Dog
SoundMaker sound2 = new Cat("Мурка");
sound1.makeSound(); // Шарик лает: Гав!
sound2.makeSound(); // Мурка мяукает: Мяу!
}
}
Интерфейс обеспечивает полиморфизм без наследования: классы не связаны иерархией, но объединены контрактом.
#Java #для_новичков #beginner #poliphormizm
👍6
Нюансы полиморфизма через суперкласс и интерфейс
Статический vs динамический тип:
Статический: Тип ссылки (Animal a) — проверяется на компиляции.
Динамический: Тип объекта (new Dog()) — определяет метод на runtime.
Нюанс: Для полей — статический тип (field hiding), для методов — динамический.
Перегрузка vs переопределение:
Перегрузка: Compile-time, разные сигнатуры.
Переопределение: Runtime, одинаковые сигнатуры.
Интерфейсы vs абстрактные классы:
Интерфейсы: Только абстрактные методы (до Java 8), множественная реализация.
Суперклассы: Могут иметь реализацию, состояние, одиночное наследование.
Ошибки:
Если метод не переопределен: Вызов супер/интерфейс версии (или ошибка, если abstract).
Несовместимая сигнатура: Не overriding, а новый метод.
@Override помогает ловить ошибки.
Полиморфизм в коллекциях:
List list = new ArrayList<>(); list.add(new Dog()); — затем цикл по list с вызовом makeSound().
Ограничения:
Private/final/static методы: Не участвуют в полиморфизме.
Конструкторы: Не полиморфны.
Как создать это в IntelliJ IDEA
Создайте интерфейс: New → Interface → SoundMaker.
Implements: В классе Dog: extends/implements, IDE подскажет override.
Полиморфизм: В Main создайте ссылки и протестируйте.
Полезные советы для новичков
Используйте полиморфизм для обобщения: Пишите код для супер/интерфейса, добавляйте подклассы без изменений.
@Override всегда: Избегайте ошибок.
Интерфейсы для контрактов: Когда нужно поведение без состояния.
Ресурсы: Oracle Tutorials on Polymorphism.
#Java #для_новичков #beginner #poliphormizm
Статический vs динамический тип:
Статический: Тип ссылки (Animal a) — проверяется на компиляции.
Динамический: Тип объекта (new Dog()) — определяет метод на runtime.
Нюанс: Для полей — статический тип (field hiding), для методов — динамический.
Перегрузка vs переопределение:
Перегрузка: Compile-time, разные сигнатуры.
Переопределение: Runtime, одинаковые сигнатуры.
Интерфейсы vs абстрактные классы:
Интерфейсы: Только абстрактные методы (до Java 8), множественная реализация.
Суперклассы: Могут иметь реализацию, состояние, одиночное наследование.
Ошибки:
Если метод не переопределен: Вызов супер/интерфейс версии (или ошибка, если abstract).
Несовместимая сигнатура: Не overriding, а новый метод.
@Override помогает ловить ошибки.
Полиморфизм в коллекциях:
List list = new ArrayList<>(); list.add(new Dog()); — затем цикл по list с вызовом makeSound().
Ограничения:
Private/final/static методы: Не участвуют в полиморфизме.
Конструкторы: Не полиморфны.
Как создать это в IntelliJ IDEA
Создайте интерфейс: New → Interface → SoundMaker.
Implements: В классе Dog: extends/implements, IDE подскажет override.
Полиморфизм: В Main создайте ссылки и протестируйте.
Полезные советы для новичков
Используйте полиморфизм для обобщения: Пишите код для супер/интерфейса, добавляйте подклассы без изменений.
@Override всегда: Избегайте ошибок.
Интерфейсы для контрактов: Когда нужно поведение без состояния.
Ресурсы: Oracle Tutorials on Polymorphism.
#Java #для_новичков #beginner #poliphormizm
👍3🔥2
Как долго вы учите Java? 🤓
Anonymous Poll
4%
Уже более 5 лет 😭
7%
От 3х до 5и 💃
54%
От 1 года до 3х 💪
35%
Меньше года, но меня не остановить ✈️
🔥1
Что выведет код?
#Tasks
class A010925 {
public void print() {
System.out.println("A");
}
}
class B010925 extends A010925 {
public void print() {
System.out.println("B");
}
public void print(String s) {
System.out.println("B" + s);
}
}
public class Task010925 {
public static void main(String[] args) {
A010925 obj = new B010925();
obj.print("test");
}
}
#Tasks
🔥4
👍6🆒1
Please open Telegram to view this post
VIEW IN TELEGRAM
Сетка
Java for Beginner. Канал в Сетке
Канал для новичков в Java
🔥2🗿1🆒1
Вопрос с собеседований
Что такое ClassCastException?🤓
Ответ:
ClassCastException возникает при попытке привести объект к несовместимому типу.
Пример:
Object obj = "string";
Integer num = (Integer) obj; // ClassCastException
Избегайте через instanceof или generics.
#собеседование
Что такое ClassCastException?
Ответ:
Пример:
Object obj = "string";
Integer num = (Integer) obj; // ClassCastException
Избегайте через instanceof или generics.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Александр Семёнович Хо́лево (род. 2 сентября 1943) — один из основателей квантовой теории информации, граница (теорема) Холево — базовый результат области.
Ивар Ялмар Якобсон (швед. Ivar Hjalmar Jacobson; 2 сентября 1939 года) — соавтор UML и RUP; внёс систематизацию объектно-ориентированного анализа и проектирования.
Эрик Пол Оллман (англ. Eric Paul Allman, родился 2 сентября, 1955, Эль-Серрито) — автор sendmail; ключевая фигура раннего e-mail-интернета.
1969 — один из «дней рождения Интернета». В лаборатории Калифорнийского университета состоялась первая успешная передача данных между соседними компьютерами.
1969 — первый банкомат в США. Впервые автоматизированно выдают наличные — химический банк Rockville Centre, NY, начало эпохи ATM банков.
1997 — IBM анонсирует обновленный "шахматный суперкомпьютер" (RS/6000 SP), на 58% быстрее. Апгрейд Deep Blue спустя несколько месяцев после победы над Г. Каспаровым.
2008 — вышла первая публичная бета-версия браузера Google Chrome.
#Biography #Birth_Date #Events #02Сентября
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Dockerfile и образы для Java
Основы: ключевые инструкции в Dockerfile
Dockerfile — декларативный файл: вы пишете, что нужно, а Docker строит образ слой за слоем. Каждый слой — это изменение файловой системы, как кирпичик в стене.
FROM: Это основа — базовый образ, от которого вы строите.
Например, FROM openjdk:21-jre — берет официальный образ с Java Runtime Environment (JRE, среда выполнения без компилятора).
Термин "образ" — это архив с файлами, конфигурацией и инструкциями.
FROM определяет родительский слой; используйте official-образы из Docker Hub для безопасности (они сканируются на уязвимости).
Нюанс: тег (как :21-jre) фиксирует версию — избегайте :latest для воспроизводимости, иначе обновления сломают билд.
RUN: Выполняет команду внутри образа, создавая новый слой.
Например, RUN apt-get update && apt-get install -y curl — обновляет пакеты и ставит curl.
Это как запуск терминала в контейнере.
RUN запускает временный контейнер, выполняет команду и фиксирует изменения (copy-on-write в overlayfs).
Объединяйте команды в один RUN с &&, чтобы минимизировать слои — много слоев замедляют поиск файлов (overhead до 10% на I/O). Избегайте RUN для часто меняющихся частей, иначе кэш сломается (об этом позже).
COPY: Копирует файлы с хоста (вашей машины) в образ.
Например, COPY target/myapp.jar /app/myapp.jar — переносит JAR-файл (скомпилированное Java-приложение) в директорию /app. Альтернатива ADD — то же, но с распаковкой архивов.
COPY предпочитается для простоты; оно сохраняет ownership и permissions. Нюанс: если файлы меняются, слои ниже инвалидируются — ставьте COPY после стабильных RUN.
ENTRYPOINT: Фиксированная команда запуска контейнера, как "java -jar /app/myapp.jar". Она не переопределяется аргументами в docker run. Это "главный вход" — контейнер всегда стартует с этого.
CMD: Дефолтные аргументы для ENTRYPOINT или самостоятельная команда.
Например, CMD ["-Dserver.port=8080"]. Переопределяется в run.
ENTRYPOINT + CMD — для гибкости; используйте ENTRYPOINT для скрипта-обертки (проверки env, graceful shutdown). В JSON-формате (["java", "-jar"]) избегайте shell-обработки; shell-форм (java -jar) — для переменных.
Эти инструкции строят слои последовательно: FROM — база, RUN/COPY — изменения, ENTRYPOINT/CMD — запуск.
Как собрать свой первый образ с Java-приложением
Представьте, что вы готовите блюдо по рецепту — Dockerfile это рецепт, docker build — процесс приготовления, а образ — готовый продукт, который можно "запускать" сколько угодно раз.
Возьмем за основу готовый JAR-файл (это скомпилированное Java-приложение, как архив с кодом и зависимостями, созданный с помощью инструментов вроде Maven или Gradle). Если у вас нет JAR, вы можете создать простое Hello World-приложение в Java и скомпилировать его.
Предположим, ваша директория проекта выглядит так:
есть папка target с myapp.jar (результат компиляции).
Создайте в корне файл Dockerfile (без расширения, с большой D).
Вот содержимое Dockerfile для минимального примера:
Разберем, что здесь происходит:
FROM eclipse-temurin:21-jre: Базовый образ от Eclipse Temurin — это официальная дистрибуция OpenJDK, оптимизированная для контейнеров.
:21-jre значит версия Java 21 с только runtime-окружением (JRE), без компилятора (JDK). Это делает образ легче — около 200-300 МБ.
Temurin предпочтительнее openjdk, потому что он включает патчи для контейнеров (container-aware) и лучше поддерживается Adoptium. Если вы на ARM (как Mac M1), тег :21-jre-arm64, но с Buildx это автоматически.
COPY target/myapp.jar /app/myapp.jar: Копируем JAR с хоста в образ. /app — директория внутри контейнера. Нюанс: Путь target/ относительный к контексту build (текущей папке). Если JAR в другом месте, скорректируйте.
WORKDIR /app: Устанавливает рабочую директорию, как команда cd в терминале. Все последующие команды (RUN, CMD) будут оттуда. Это упрощает пути и делает образ чище.
#Java #middle #Docker #Dockerfile
Основы: ключевые инструкции в Dockerfile
Dockerfile — декларативный файл: вы пишете, что нужно, а Docker строит образ слой за слоем. Каждый слой — это изменение файловой системы, как кирпичик в стене.
FROM: Это основа — базовый образ, от которого вы строите.
Например, FROM openjdk:21-jre — берет официальный образ с Java Runtime Environment (JRE, среда выполнения без компилятора).
Термин "образ" — это архив с файлами, конфигурацией и инструкциями.
FROM определяет родительский слой; используйте official-образы из Docker Hub для безопасности (они сканируются на уязвимости).
Нюанс: тег (как :21-jre) фиксирует версию — избегайте :latest для воспроизводимости, иначе обновления сломают билд.
RUN: Выполняет команду внутри образа, создавая новый слой.
Например, RUN apt-get update && apt-get install -y curl — обновляет пакеты и ставит curl.
Это как запуск терминала в контейнере.
RUN запускает временный контейнер, выполняет команду и фиксирует изменения (copy-on-write в overlayfs).
Объединяйте команды в один RUN с &&, чтобы минимизировать слои — много слоев замедляют поиск файлов (overhead до 10% на I/O). Избегайте RUN для часто меняющихся частей, иначе кэш сломается (об этом позже).
COPY: Копирует файлы с хоста (вашей машины) в образ.
Например, COPY target/myapp.jar /app/myapp.jar — переносит JAR-файл (скомпилированное Java-приложение) в директорию /app. Альтернатива ADD — то же, но с распаковкой архивов.
COPY предпочитается для простоты; оно сохраняет ownership и permissions. Нюанс: если файлы меняются, слои ниже инвалидируются — ставьте COPY после стабильных RUN.
ENTRYPOINT: Фиксированная команда запуска контейнера, как "java -jar /app/myapp.jar". Она не переопределяется аргументами в docker run. Это "главный вход" — контейнер всегда стартует с этого.
CMD: Дефолтные аргументы для ENTRYPOINT или самостоятельная команда.
Например, CMD ["-Dserver.port=8080"]. Переопределяется в run.
ENTRYPOINT + CMD — для гибкости; используйте ENTRYPOINT для скрипта-обертки (проверки env, graceful shutdown). В JSON-формате (["java", "-jar"]) избегайте shell-обработки; shell-форм (java -jar) — для переменных.
Эти инструкции строят слои последовательно: FROM — база, RUN/COPY — изменения, ENTRYPOINT/CMD — запуск.
Как собрать свой первый образ с Java-приложением
Представьте, что вы готовите блюдо по рецепту — Dockerfile это рецепт, docker build — процесс приготовления, а образ — готовый продукт, который можно "запускать" сколько угодно раз.
Возьмем за основу готовый JAR-файл (это скомпилированное Java-приложение, как архив с кодом и зависимостями, созданный с помощью инструментов вроде Maven или Gradle). Если у вас нет JAR, вы можете создать простое Hello World-приложение в Java и скомпилировать его.
Предположим, ваша директория проекта выглядит так:
есть папка target с myapp.jar (результат компиляции).
Создайте в корне файл Dockerfile (без расширения, с большой D).
Вот содержимое Dockerfile для минимального примера:
textFROM eclipse-temurin:21-jre
COPY target/myapp.jar /app/myapp.jar
WORKDIR /app
ENTRYPOINT ["java", "-jar", "myapp.jar"]
Разберем, что здесь происходит:
FROM eclipse-temurin:21-jre: Базовый образ от Eclipse Temurin — это официальная дистрибуция OpenJDK, оптимизированная для контейнеров.
:21-jre значит версия Java 21 с только runtime-окружением (JRE), без компилятора (JDK). Это делает образ легче — около 200-300 МБ.
Temurin предпочтительнее openjdk, потому что он включает патчи для контейнеров (container-aware) и лучше поддерживается Adoptium. Если вы на ARM (как Mac M1), тег :21-jre-arm64, но с Buildx это автоматически.
COPY target/myapp.jar /app/myapp.jar: Копируем JAR с хоста в образ. /app — директория внутри контейнера. Нюанс: Путь target/ относительный к контексту build (текущей папке). Если JAR в другом месте, скорректируйте.
WORKDIR /app: Устанавливает рабочую директорию, как команда cd в терминале. Все последующие команды (RUN, CMD) будут оттуда. Это упрощает пути и делает образ чище.
#Java #middle #Docker #Dockerfile
👍3
ENTRYPOINT ["java", "-jar", "myapp.jar"]: Фиксированная команда запуска. Формат в скобках — exec-форма, без shell — быстрее и безопаснее (нет интерпретации переменных). При запуске контейнера Docker выполнит java -jar myapp.jar из /app.
Чтобы собрать образ, откройте терминал в директории с Dockerfile и выполните:
docker build: Команда для сборки.
-t myjavaapp:1.0: Тег (метка) для образа — имя:версия. Без тега — random ID.
.: Контекст — текущая директория, откуда берутся файлы для COPY.
Что происходит под капотом во время сборки?
Docker daemon читает Dockerfile строку за строкой, создавая промежуточные контейнеры для каждого слоя.
Для FROM — pull базового образа (если нет локально).
Затем RUN/COPY — запускает temp-контейнер, применяет изменения и commit'ит слой (как git commit). Каждый слой — delta (изменения) в overlayfs; кэш проверяется по хэшу (инструкция + файлы). Если все гладко, сборка занимает секунды. Вывод в терминале покажет шаги: "Step 1/4 : FROM...", с хэшами слоев.
После сборки проверьте: docker images — увидите myjavaapp:1.0 с размером. Используйте docker history myjavaapp:1.0 — покажет слои в обратном порядке, с размерами и командами. Это поможет оптимизировать: если слой большой (из-за RUN install), объедините.
Теперь запустите:
Контейнер стартует, выполнит JAR и выйдет (если app не daemon). Добавьте -d для фона, -p 8080:8080 для портов (если app сервер).
Логи:
Распространенные ошибки и как фиксить:
"No such file": JAR не найден — проверьте путь в COPY; добавьте .dockerignore для игнора лишнего (как .gitignore).
Pull failed: Нет интернета или registry down — используйте локальные образы.
Build краш в RUN: Добавьте --no-cache в build для полной перестройки (игнор кэша).
Если secrets нужны (пароли в build), используйте --secret id=mysecret,source=.env; в RUN echo $mysecret. В CI (GitHub Actions) интегрируйте с caching для ускорения.
Это базовый образ — для dev. В production добавьте healthcheck: HEALTHCHECK CMD curl -f http://localhost:8080/health.
Усложняем: многоэтапная сборка
Multi-stage build — это продвинутый подход, где вы разделяете процесс на этапы: один для компиляции кода (с полным JDK и build-tools вроде Maven), а другой — только для запуска (минимальный JRE). Это делает финальный образ компактным, без лишних инструментов, что снижает размер, ускоряет deploy и повышает безопасность (меньше vuln в production).
Пример с Maven (предполагаем, у вас есть src/ и pom.xml — файл конфигурации Maven):
AS build: Имя первого этапа — можно ссылаться в COPY --from.
RUN mvn clean package: Компилирует JAR, пропуская тесты для скорости (-DskipTests). В памяти: Maven кэширует deps в /root/.m2, но в multi-stage это не сохраняется в финале.
COPY --from=build: Берет только JAR из первого этапа — остальное (JDK, Maven) отбрасывается.
Сборка:
Финальный образ ~150-300 МБ vs 1ГБ+ с JDK. В BuildKit (дефолт) этапы могут параллелизоваться, если нет зависимостей; используйте --target=build для остановки на первом этапе (тесты).
Нюанс: Если deps меняются редко, копируйте pom.xml первым, затем RUN mvn dependency:go-offline — кэш deps сохранится. В CI добавьте cache-from для reuse.
Подводные камни: Если тесты нужны, уберите -DskipTests; для Gradle замените на FROM gradle:8.10-jdk21 и RUN gradle build.
#Java #middle #Docker #Dockerfile
Чтобы собрать образ, откройте терминал в директории с Dockerfile и выполните:
docker build -t myjavaapp:1.0 .
docker build: Команда для сборки.
-t myjavaapp:1.0: Тег (метка) для образа — имя:версия. Без тега — random ID.
.: Контекст — текущая директория, откуда берутся файлы для COPY.
Что происходит под капотом во время сборки?
Docker daemon читает Dockerfile строку за строкой, создавая промежуточные контейнеры для каждого слоя.
Для FROM — pull базового образа (если нет локально).
Затем RUN/COPY — запускает temp-контейнер, применяет изменения и commit'ит слой (как git commit). Каждый слой — delta (изменения) в overlayfs; кэш проверяется по хэшу (инструкция + файлы). Если все гладко, сборка занимает секунды. Вывод в терминале покажет шаги: "Step 1/4 : FROM...", с хэшами слоев.
После сборки проверьте: docker images — увидите myjavaapp:1.0 с размером. Используйте docker history myjavaapp:1.0 — покажет слои в обратном порядке, с размерами и командами. Это поможет оптимизировать: если слой большой (из-за RUN install), объедините.
Теперь запустите:
docker run myjavaapp:1.0
Контейнер стартует, выполнит JAR и выйдет (если app не daemon). Добавьте -d для фона, -p 8080:8080 для портов (если app сервер).
Логи:
docker logs <container-id>.</container-id>
Распространенные ошибки и как фиксить:
"No such file": JAR не найден — проверьте путь в COPY; добавьте .dockerignore для игнора лишнего (как .gitignore).
Pull failed: Нет интернета или registry down — используйте локальные образы.
Build краш в RUN: Добавьте --no-cache в build для полной перестройки (игнор кэша).
Если secrets нужны (пароли в build), используйте --secret id=mysecret,source=.env; в RUN echo $mysecret. В CI (GitHub Actions) интегрируйте с caching для ускорения.
Это базовый образ — для dev. В production добавьте healthcheck: HEALTHCHECK CMD curl -f http://localhost:8080/health.
Усложняем: многоэтапная сборка
Multi-stage build — это продвинутый подход, где вы разделяете процесс на этапы: один для компиляции кода (с полным JDK и build-tools вроде Maven), а другой — только для запуска (минимальный JRE). Это делает финальный образ компактным, без лишних инструментов, что снижает размер, ускоряет deploy и повышает безопасность (меньше vuln в production).
Пример с Maven (предполагаем, у вас есть src/ и pom.xml — файл конфигурации Maven):
text# Этап 1: Сборка приложения
FROM maven:3.9.6-eclipse-temurin-21 AS build
COPY src /app/src
COPY pom.xml /app
WORKDIR /app
RUN mvn clean package -DskipTests
# Этап 2: Runtime-окружение
FROM eclipse-temurin:21-jre
COPY --from=build /app/target/myapp.jar /app/myapp.jar
WORKDIR /app
ENTRYPOINT ["java", "-jar", "myapp.jar"]
AS build: Имя первого этапа — можно ссылаться в COPY --from.
RUN mvn clean package: Компилирует JAR, пропуская тесты для скорости (-DskipTests). В памяти: Maven кэширует deps в /root/.m2, но в multi-stage это не сохраняется в финале.
COPY --from=build: Берет только JAR из первого этапа — остальное (JDK, Maven) отбрасывается.
Сборка:
docker build -t myjavaapp:prod .
Финальный образ ~150-300 МБ vs 1ГБ+ с JDK. В BuildKit (дефолт) этапы могут параллелизоваться, если нет зависимостей; используйте --target=build для остановки на первом этапе (тесты).
Нюанс: Если deps меняются редко, копируйте pom.xml первым, затем RUN mvn dependency:go-offline — кэш deps сохранится. В CI добавьте cache-from для reuse.
Подводные камни: Если тесты нужны, уберите -DskipTests; для Gradle замените на FROM gradle:8.10-jdk21 и RUN gradle build.
#Java #middle #Docker #Dockerfile
👍5
Alpine vs Slim vs Distroless: плюсы и минусы
Выбор базового образа влияет на размер, производительность и совместимость. Давайте сравним варианты для Java.
Alpine (eclipse-temurin:21-jre-alpine):
Основан на легком Linux-дистрибутиве с musl libc (библиотека C). Плюсы: Очень маленький (~70-100 МБ), быстро загружается, экономит диск/сеть.
Минусы: Musl не полностью совместим с glibc (стандарт в Java) — проблемы с нативными библиотеками (JNI), шрифтами, локалями или DNS. Чревато крашами в enterprise-apps.
Когда использовать: Простые сервисы без native-кода; тестируйте entropy (-Djava.security.egd=file:/dev/./urandom для random). Musl легче в памяти (меньше overhead), но glibc стабильнее для legacy.
Slim (eclipse-temurin:21-jre-slim):
На базе Debian slim (урезанный).
Плюсы: Glibc-совместимость, размер ~150-250 МБ, включает базовые утилиты.
Минусы: Больше Alpine, но все равно минимален по сравнению с full. Когда: Баланс — для apps с зависимостями (шрифты, crypto). Senior: Добавьте RUN apt-get install -y fontconfig для fonts; лучше для multi-platform.
Distroless (gcr.io/distroless/java21:nonroot):
Сверхминимальный, без ОС-утилит. Плюсы: ~50-100 МБ, высокая безопасность (нет shell — хакеры не запустят команды), immutable.
Минусы: Нет инструментов для debug (bash, jcmd); вручную добавляйте CA-certs (COPY /etc/ssl/certs).
Чревато: Нет tzdata — env TZ=UTC; signals ok, но shutdown-hooks в коде. Когда: Production, где security > удобство. В Kubernetes — идеал для scaling; используйте debug-sidecar для отладки.
Выбор: Начните с Slim для dev, перейдите на Distroless для prod. Всегда тестируйте под load — проверьте java -version в run.
Как работает кэширование слоёв и почему важен порядок инструкций
Кэширование слоёв — это один из самых мощных механизмов Docker, который делает повторные сборки образов быстрыми и эффективными. Без него каждая сборка начиналась бы с нуля, что занимало бы много времени, особенно для сложных приложений с установкой зависимостей. Представьте кэш как "память" Docker — если ничего не изменилось в части рецепта (Dockerfile), он использует готовый результат из предыдущей сборки, вместо того чтобы готовить заново.
Каждый слой в образе — это результат выполнения одной инструкции в Dockerfile (FROM, RUN, COPY и т.д.). Docker присваивает каждому слою уникальный идентификатор на основе хэша (обычно SHA256). Хэш рассчитывается не только от самой инструкции, но и от её контекста: для RUN — это текст команды, для COPY — checksum (контрольная сумма) копируемых файлов, для FROM — тег базового образа. Если при следующей сборке хэш совпадает с кэшированным, Docker пропускает выполнение и берет готовый слой из локального хранилища.
Процесс кэширования шаг за шагом:
Проверка кэша при старте сборки: Когда вы запускаете docker build, Docker daemon (фоновый процесс, управляющий сборками) сканирует Dockerfile сверху вниз. Для первой инструкции (обычно FROM) он проверяет, есть ли в локальном кэше слой с таким же хэшем. Если базовый образ изменился (например, обновился в registry), кэш инвалидируется, и Docker pull'нет свежую версию.
#Java #middle #Docker #Dockerfile
Выбор базового образа влияет на размер, производительность и совместимость. Давайте сравним варианты для Java.
Alpine (eclipse-temurin:21-jre-alpine):
Основан на легком Linux-дистрибутиве с musl libc (библиотека C). Плюсы: Очень маленький (~70-100 МБ), быстро загружается, экономит диск/сеть.
Минусы: Musl не полностью совместим с glibc (стандарт в Java) — проблемы с нативными библиотеками (JNI), шрифтами, локалями или DNS. Чревато крашами в enterprise-apps.
Когда использовать: Простые сервисы без native-кода; тестируйте entropy (-Djava.security.egd=file:/dev/./urandom для random). Musl легче в памяти (меньше overhead), но glibc стабильнее для legacy.
Slim (eclipse-temurin:21-jre-slim):
На базе Debian slim (урезанный).
Плюсы: Glibc-совместимость, размер ~150-250 МБ, включает базовые утилиты.
Минусы: Больше Alpine, но все равно минимален по сравнению с full. Когда: Баланс — для apps с зависимостями (шрифты, crypto). Senior: Добавьте RUN apt-get install -y fontconfig для fonts; лучше для multi-platform.
Distroless (gcr.io/distroless/java21:nonroot):
Сверхминимальный, без ОС-утилит. Плюсы: ~50-100 МБ, высокая безопасность (нет shell — хакеры не запустят команды), immutable.
Минусы: Нет инструментов для debug (bash, jcmd); вручную добавляйте CA-certs (COPY /etc/ssl/certs).
Чревато: Нет tzdata — env TZ=UTC; signals ok, но shutdown-hooks в коде. Когда: Production, где security > удобство. В Kubernetes — идеал для scaling; используйте debug-sidecar для отладки.
Выбор: Начните с Slim для dev, перейдите на Distroless для prod. Всегда тестируйте под load — проверьте java -version в run.
Как работает кэширование слоёв и почему важен порядок инструкций
Кэширование слоёв — это один из самых мощных механизмов Docker, который делает повторные сборки образов быстрыми и эффективными. Без него каждая сборка начиналась бы с нуля, что занимало бы много времени, особенно для сложных приложений с установкой зависимостей. Представьте кэш как "память" Docker — если ничего не изменилось в части рецепта (Dockerfile), он использует готовый результат из предыдущей сборки, вместо того чтобы готовить заново.
Каждый слой в образе — это результат выполнения одной инструкции в Dockerfile (FROM, RUN, COPY и т.д.). Docker присваивает каждому слою уникальный идентификатор на основе хэша (обычно SHA256). Хэш рассчитывается не только от самой инструкции, но и от её контекста: для RUN — это текст команды, для COPY — checksum (контрольная сумма) копируемых файлов, для FROM — тег базового образа. Если при следующей сборке хэш совпадает с кэшированным, Docker пропускает выполнение и берет готовый слой из локального хранилища.
Процесс кэширования шаг за шагом:
Проверка кэша при старте сборки: Когда вы запускаете docker build, Docker daemon (фоновый процесс, управляющий сборками) сканирует Dockerfile сверху вниз. Для первой инструкции (обычно FROM) он проверяет, есть ли в локальном кэше слой с таким же хэшем. Если базовый образ изменился (например, обновился в registry), кэш инвалидируется, и Docker pull'нет свежую версию.
#Java #middle #Docker #Dockerfile
👍3
Расчет хэша для каждой инструкции: Для RUN хэш включает точный текст команды (даже пробелы важны).
Для COPY/ADD — хэш файлов: Docker tar'ит (архивирует) контекст сборки (директорию с Dockerfile) и вычисляет checksum. Если файл изменился хоть на байт, хэш новый. В памяти хоста это происходит быстро: daemon использует минимум RAM, но для больших контекстов (много файлов) tar может занять время.
Использование кэша: Если хэш match, Docker копирует слой из /var/lib/docker/overlay2 (хранилище слоёв на диске). Нет выполнения команды — экономия времени и ресурсов. Если нет — создает временный контейнер, выполняет инструкцию и commit'ит новый слой.
Инвалидация цепочки: Вот ключевой момент — кэш цепной. Если слой N изменился, все слои ниже (N+1, N+2...) инвалидируются, даже если их инструкции не поменялись. Это потому, что каждый слой строится на предыдущем (union-файловая система).
В памяти и на диске: Кэш хранится локально, занимая диск (сотни МБ для образов). Нет загрузки в RAM до запуска контейнера. Но в CI/CD (системах непрерывной интеграции, как GitHub Actions) кэш может быть shared через registry — с BuildKit используйте --cache-to=type=inline или type=registry для экспорта.
Почему порядок инструкций так важен? Потому что он напрямую влияет на частоту инвалидации кэша.
Идея: ставьте стабильные, редко меняющиеся инструкции в начало, а изменяемые — в конец. Это минимизирует перестройки.
Пример плохого порядка (кэш часто ломается):
Хороший порядок (кэш работает эффективно):
Нюансы
В классическом билдере (до BuildKit) кэш линейный и последовательный. BuildKit (дефолт с Docker 18.09+, обязательно с 23.0) строит граф зависимостей: анализирует весь Dockerfile и выполняет независимые инструкции параллельно (на multi-core CPU). Например, несколько RUN без зависимостей — одновременно. Это ускоряет в 2-5 раз.
Флаги: --cache-from=remote для pull кэша из registry в CI; --cache-to=type=gha для GitHub Actions.
.dockerignore: Файл в корне, как .gitignore — исключает файлы из контекста (не tar'ятся). Например, игнор target/, node_modules — ускоряет build, снижает размер контекста (max 100-200 МБ, иначе ошибки).
"Застрявший" кэш: Если хэш неверный (баги, изменения env), используйте --no-cache в build — полная перестройка. Или docker builder prune — чистка кэша (освобождает диск).
Multi-platform и кэш: С Buildx (--platform linux/amd64,linux/arm64) кэш per-архитектура: отдельно для x86 и ARM. Эмуляция (QEMU) жрёт RAM (до 2x), но манифест объединяет.
Секреты и кэш: Пароли в RUN попадут в слой — используйте --secret для временного mount в память (tmpfs, не на диск).
Влияние на Java: Для Maven/Gradle deps кэшируются в слое, но в multi-stage — не в финале. Если deps большие (сотни МБ), оптимизируйте порядок для CI — сэкономите bandwidth.
В Docker Desktop (Mac/Windows) кэш медленнее из-за VM-layer; используйте mutagen или volume-sync для ускорения. Тестируйте: Соберите дважды — второй раз должен быть мгновенным, если ничего не изменилось.
#Java #middle #Docker #Dockerfile
Для COPY/ADD — хэш файлов: Docker tar'ит (архивирует) контекст сборки (директорию с Dockerfile) и вычисляет checksum. Если файл изменился хоть на байт, хэш новый. В памяти хоста это происходит быстро: daemon использует минимум RAM, но для больших контекстов (много файлов) tar может занять время.
Использование кэша: Если хэш match, Docker копирует слой из /var/lib/docker/overlay2 (хранилище слоёв на диске). Нет выполнения команды — экономия времени и ресурсов. Если нет — создает временный контейнер, выполняет инструкцию и commit'ит новый слой.
Инвалидация цепочки: Вот ключевой момент — кэш цепной. Если слой N изменился, все слои ниже (N+1, N+2...) инвалидируются, даже если их инструкции не поменялись. Это потому, что каждый слой строится на предыдущем (union-файловая система).
В памяти и на диске: Кэш хранится локально, занимая диск (сотни МБ для образов). Нет загрузки в RAM до запуска контейнера. Но в CI/CD (системах непрерывной интеграции, как GitHub Actions) кэш может быть shared через registry — с BuildKit используйте --cache-to=type=inline или type=registry для экспорта.
Почему порядок инструкций так важен? Потому что он напрямую влияет на частоту инвалидации кэша.
Идея: ставьте стабильные, редко меняющиеся инструкции в начало, а изменяемые — в конец. Это минимизирует перестройки.
Пример плохого порядка (кэш часто ломается):
textFROM eclipse-temurin:21-jre
COPY src /app/src # Код меняется часто — хэш новый каждый раз
RUN mvn clean package # Зависимости перестраиваются заново
COPY target/myapp.jar /app/myapp.jar
Здесь COPY src в начале — если код изменился (что бывает часто), RUN mvn ниже инвалидируется, и вся компиляция повторяется (минуты времени).
Хороший порядок (кэш работает эффективно):
textFROM eclipse-temurin:21-jre
COPY pom.xml /app # Только файл зависимостей — меняется редко
RUN mvn dependency:go-offline # Скачивает deps, кэшируется
COPY src /app/src # Код в конце — меняется, но deps уже в кэше
RUN mvn clean package
COPY target/myapp.jar /app/myapp.jar
Теперь, если код изменился, только последние RUN/COPY перестроятся — deps из кэша. Экономия: 80-90% времени на повторных билдах.
Нюансы
В классическом билдере (до BuildKit) кэш линейный и последовательный. BuildKit (дефолт с Docker 18.09+, обязательно с 23.0) строит граф зависимостей: анализирует весь Dockerfile и выполняет независимые инструкции параллельно (на multi-core CPU). Например, несколько RUN без зависимостей — одновременно. Это ускоряет в 2-5 раз.
Флаги: --cache-from=remote для pull кэша из registry в CI; --cache-to=type=gha для GitHub Actions.
.dockerignore: Файл в корне, как .gitignore — исключает файлы из контекста (не tar'ятся). Например, игнор target/, node_modules — ускоряет build, снижает размер контекста (max 100-200 МБ, иначе ошибки).
"Застрявший" кэш: Если хэш неверный (баги, изменения env), используйте --no-cache в build — полная перестройка. Или docker builder prune — чистка кэша (освобождает диск).
Multi-platform и кэш: С Buildx (--platform linux/amd64,linux/arm64) кэш per-архитектура: отдельно для x86 и ARM. Эмуляция (QEMU) жрёт RAM (до 2x), но манифест объединяет.
Секреты и кэш: Пароли в RUN попадут в слой — используйте --secret для временного mount в память (tmpfs, не на диск).
Влияние на Java: Для Maven/Gradle deps кэшируются в слое, но в multi-stage — не в финале. Если deps большие (сотни МБ), оптимизируйте порядок для CI — сэкономите bandwidth.
В Docker Desktop (Mac/Windows) кэш медленнее из-за VM-layer; используйте mutagen или volume-sync для ускорения. Тестируйте: Соберите дважды — второй раз должен быть мгновенным, если ничего не изменилось.
#Java #middle #Docker #Dockerfile
👍3
JVM в контейнере: настройки памяти и сборка мусора
JVM (виртуальная машина Java) в Docker требует специальной настройки, потому что контейнеры ограничивают ресурсы, и без адаптации приложение может крашнуться.
-Xmx и -XX:+UseContainerSupport: Флаг -Xmx задаёт максимальный размер кучи (heap — области для объектов Java), например, -Xmx512m.
Но в контейнере это опасно: если docker run --memory=1g, а -Xmx не задан или больше лимита, JVM возьмёт больше памяти, и система убьёт процесс (OOM-killer).
Флаг -XX:+UseContainerSupport (включен по умолчанию в Java 10 и выше) делает JVM "container-aware": она читает лимиты из cgroups (/sys/fs/cgroup/memory/memory.limit_in_bytes) и автоматически подстраивает heap под доступную память. В коде это видно через Runtime.getRuntime().maxMemory() — возвращает скорректированное значение (обычно 75% от лимита минус резервы JVM).
Не дублируйте -Xmx — используйте -XX:MaxRAMPercentage=80 для максимального процента от cgroup-лимита и -XX:InitialRAMPercentage=50 для стартового. Это позволяет динамически scaling в Kubernetes. Следите за native memory (память вне heap: нити, буферы, Metaspace для классов) — лимитируйте -XX:MaxMetaspaceSize=256m, иначе утечки съедят весь RAM. Включайте -XX:NativeMemoryTracking=detail для трекинга (overhead ~5%, но покажет breakdown).
В старых Java (8u131-) флаг не работает — обновляйтесь.
Сборка мусора в контейнерах: GC — процесс очистки кучи от "мусора" (ненужных объектов). В контейнере лимиты CPU и памяти влияют: низкая память — частые GC, низкий CPU — длинные паузы (app замирает). Выбор GC зависит от приложения.
G1GC (-XX:+UseG1GC, дефолт с Java 9): "Garbage-First" делит heap на регионы (по 1-32 МБ) и чистит "мусорные" первыми.
Плюсы: баланс между производительностью (throughput) и паузами (low-latency), подходит для heap 1-32 ГБ.
В контейнере: Учитывает cgroups — запускает GC раньше, минимизируя OOM. Паузы: 50-200 мс.
Настройте -XX:G1HeapRegionSize=2m для маленьких heap; -XX:ConcGCThreads=4 для CPU-квот. Если паузы высокие — мониторьте с -XX:+PrintGCDetails и JFR (Java Flight Recorder).
ZGC (-XX:+UseZGC, стабильный в Java 15+): Concurrent GC — чистит параллельно с app, паузы <10 мс даже на терабайтных heap.
Плюсы: Для high-throughput/low-pause apps (trading, streaming).
Минусы: Требует много CPU — при docker --cpus=2 throttling (замедление) растянет паузы.
В контейнере: Хорошо масштабируется, но тестируйте под load. В Java 21 — production-ready; -XX:ZCollectionInterval=5s для частоты; используйте для pause-sensitive микросервисов.
Shenandoah (-XX:+UseShenandoahGC): Похож на ZGC, но с evacuation (перемещением объектов) concurrently.
Плюсы: Лучше адаптируется к variable loads, низкие паузы.
Минусы: Меньше сообщества (Red Hat-driven).
В контейнере: Отлично для dynamic environments.
-XX:ShenandoahGCHeuristics=adaptive для auto-tune; комбинируйте с virtual threads (Java 21) для IO-bound apps.
Влияние cgroups: Runtime.maxMemory() = cgroup memory limit - JVM reserves (10-20%). При --cpus=0.5 GC использует меньше нитей (-XX:ParallelGCThreads auto-подстраивается).
Тестируйте: Запустите с -verbose:gc для логов; используйте JFR (-XX:StartFlightRecording) для профилей.
Нюанс: Virtual threads (Java 19+, stable 21) снижают overhead нитей (миллионы без OOM), но GC не меняется — они в heap. Для CPU-bound — ForkJoinPool лучше.
В production: Мониторьте с Prometheus JMX exporter.
#Java #middle #Docker #Dockerfile
JVM (виртуальная машина Java) в Docker требует специальной настройки, потому что контейнеры ограничивают ресурсы, и без адаптации приложение может крашнуться.
-Xmx и -XX:+UseContainerSupport: Флаг -Xmx задаёт максимальный размер кучи (heap — области для объектов Java), например, -Xmx512m.
Но в контейнере это опасно: если docker run --memory=1g, а -Xmx не задан или больше лимита, JVM возьмёт больше памяти, и система убьёт процесс (OOM-killer).
Флаг -XX:+UseContainerSupport (включен по умолчанию в Java 10 и выше) делает JVM "container-aware": она читает лимиты из cgroups (/sys/fs/cgroup/memory/memory.limit_in_bytes) и автоматически подстраивает heap под доступную память. В коде это видно через Runtime.getRuntime().maxMemory() — возвращает скорректированное значение (обычно 75% от лимита минус резервы JVM).
Не дублируйте -Xmx — используйте -XX:MaxRAMPercentage=80 для максимального процента от cgroup-лимита и -XX:InitialRAMPercentage=50 для стартового. Это позволяет динамически scaling в Kubernetes. Следите за native memory (память вне heap: нити, буферы, Metaspace для классов) — лимитируйте -XX:MaxMetaspaceSize=256m, иначе утечки съедят весь RAM. Включайте -XX:NativeMemoryTracking=detail для трекинга (overhead ~5%, но покажет breakdown).
В старых Java (8u131-) флаг не работает — обновляйтесь.
Сборка мусора в контейнерах: GC — процесс очистки кучи от "мусора" (ненужных объектов). В контейнере лимиты CPU и памяти влияют: низкая память — частые GC, низкий CPU — длинные паузы (app замирает). Выбор GC зависит от приложения.
G1GC (-XX:+UseG1GC, дефолт с Java 9): "Garbage-First" делит heap на регионы (по 1-32 МБ) и чистит "мусорные" первыми.
Плюсы: баланс между производительностью (throughput) и паузами (low-latency), подходит для heap 1-32 ГБ.
В контейнере: Учитывает cgroups — запускает GC раньше, минимизируя OOM. Паузы: 50-200 мс.
Настройте -XX:G1HeapRegionSize=2m для маленьких heap; -XX:ConcGCThreads=4 для CPU-квот. Если паузы высокие — мониторьте с -XX:+PrintGCDetails и JFR (Java Flight Recorder).
ZGC (-XX:+UseZGC, стабильный в Java 15+): Concurrent GC — чистит параллельно с app, паузы <10 мс даже на терабайтных heap.
Плюсы: Для high-throughput/low-pause apps (trading, streaming).
Минусы: Требует много CPU — при docker --cpus=2 throttling (замедление) растянет паузы.
В контейнере: Хорошо масштабируется, но тестируйте под load. В Java 21 — production-ready; -XX:ZCollectionInterval=5s для частоты; используйте для pause-sensitive микросервисов.
Shenandoah (-XX:+UseShenandoahGC): Похож на ZGC, но с evacuation (перемещением объектов) concurrently.
Плюсы: Лучше адаптируется к variable loads, низкие паузы.
Минусы: Меньше сообщества (Red Hat-driven).
В контейнере: Отлично для dynamic environments.
-XX:ShenandoahGCHeuristics=adaptive для auto-tune; комбинируйте с virtual threads (Java 21) для IO-bound apps.
Влияние cgroups: Runtime.maxMemory() = cgroup memory limit - JVM reserves (10-20%). При --cpus=0.5 GC использует меньше нитей (-XX:ParallelGCThreads auto-подстраивается).
Тестируйте: Запустите с -verbose:gc для логов; используйте JFR (-XX:StartFlightRecording) для профилей.
Нюанс: Virtual threads (Java 19+, stable 21) снижают overhead нитей (миллионы без OOM), но GC не меняется — они в heap. Для CPU-bound — ForkJoinPool лучше.
В production: Мониторьте с Prometheus JMX exporter.
#Java #middle #Docker #Dockerfile
👍3
Много ли у Вас друзей среди IT-шников?
Anonymous Poll
24%
Да! Есть как друзья детства, так и коллеги ставшие ими 🙂
7%
Да, но только коллеги, с кем сдружились 👋
24%
Есть много знакомых, из разных мест, но со всеми поддерживаю связь только по необходимости 🤷♀️
24%
Нет, я только начинаю свой путь в IT и пока не нашел друзей. 😉
21%
Нет, мне сложно заводить новые контакты. Предпочитаю удаленку и минимум общения 🤓
👍5
Что выведет код?
#Tasks
public class Task020925 {
private static int count = 0;
public static void main(String[] args) {
synchronized (Task020925.class) {
count++;
}
System.out.println(count);
}
}
#Tasks
👍2
👍4
Продолжаем выбирать темы для разбора и голосовать за рассмотрение предложенных! 🤓
Голосуем за тему к рассмотрению в эти выходные!
Выбираем новую тему!
(можете предложить что-то из того, что предлагали на прошлой и позапрошлых неделях и что проиграло в голосовании!)
Не стесняемся!✌️
Голосуем за тему к рассмотрению в эти выходные!
Выбираем новую тему!
(можете предложить что-то из того, что предлагали на прошлой и позапрошлых неделях и что проиграло в голосовании!)
Не стесняемся!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Вопрос с собеседований
Что такое parkNanos в LockSupport?🤓
Ответ:
LockSupport.parkNanos(long nanos) приостанавливает поток на указанное время в наносекундах.
Пример:
LockSupport.parkNanos(1_000_000_000); // 1 секунда
Более точный, чем sleep, но без InterruptedException.
#собеседование
Что такое parkNanos в LockSupport?
Ответ:
Пример:
LockSupport.parkNanos(1_000_000_000); // 1 секунда
Более точный, чем sleep, но без InterruptedException.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3