Найти в Дзене
Java

🧠 Продвинутая задача Java — Невидимая утечка памяти в лямбдах

🧠 Продвинутая задача Java — Невидимая утечка памяти в лямбдах Задача: Предположим, вы пишете сервис, в котором создаются задачи (например, отложенные действия): public class TaskScheduler { private final List<Runnable> tasks = new ArrayList<>(); public void scheduleTask(String name) { String largeData = name.repeat(10_000); // имитируем большой объект tasks.add(() -> { System.out.println("Running task: " + name); }); } public void runAll() { tasks.forEach(Runnable::run); } } Теперь создайте 1_000_000 таких задач: TaskScheduler scheduler = new TaskScheduler(); for (int i = 0; i < 1_000_000; i++) { scheduler.scheduleTask("Task" + i); } Вопрос: Почему String largeData не освобождается сборщиком мусора, хотя он нигде явно не используется? Разбор: На первый взгляд, largeData нигде не используется — его можно было бы освободить. Но на самом деле лямбда-захват переменной name удерживает весь стек метода scheduleTask, включая largeData. Это называется неявная утечка памяти через з

🧠 Продвинутая задача Java — Невидимая утечка памяти в лямбдах

Задача:

Предположим, вы пишете сервис, в котором создаются задачи (например, отложенные действия):

public class TaskScheduler {

private final List<Runnable> tasks = new ArrayList<>();

public void scheduleTask(String name) {

String largeData = name.repeat(10_000); // имитируем большой объект

tasks.add(() -> {

System.out.println("Running task: " + name);

});

}

public void runAll() {

tasks.forEach(Runnable::run);

}

}

Теперь создайте 1_000_000 таких задач:

TaskScheduler scheduler = new TaskScheduler();

for (int i = 0; i < 1_000_000; i++) {

scheduler.scheduleTask("Task" + i);

}

Вопрос:

Почему String largeData не освобождается сборщиком мусора, хотя он нигде явно не используется?

Разбор:

На первый взгляд, largeData нигде не используется — его можно было бы освободить.

Но на самом деле лямбда-захват переменной name удерживает весь стек метода scheduleTask, включая largeData.

Это называется неявная утечка памяти через замыкания.

---

Как исправить?

Чтобы избежать утечки, выносите только нужные значения в лямбду:

public void scheduleTask(String name) {

String taskName = name; // только то, что действительно нужно

tasks.add(() -> {

System.out.println("Running task: " + taskName);

});

}

Или даже:

tasks.add(() -> System.out.println("Running task: " + name));

НО! Убедитесь, что переменные вне лямбды не держат в памяти тяжёлые объекты, которые не нужны после исполнения.

💡 Вывод:

- Java лямбды могут неявно захватывать контекст, включая большие объекты

- Это может привести к утечкам памяти, особенно в long-lived объектах (пулы задач, слушатели и т.п.)

- Профилируйте и проверяйте, что захватывает ваша лямбда

#java #memoryleak #lambda #gc #profiling

@javarush