🧠 Продвинутая задача Java — Невидимая утечка памяти в лямбдах
Задача:
Предположим, вы пишете сервис, в котором создаются задачи (например, отложенные действия):
Теперь создайте 1_000_000 таких задач:
Вопрос:
Почему
Разбор:
На первый взгляд,
Но на самом деле лямбда-захват переменной
Это называется неявная утечка памяти через замыкания.
---
Как исправить?
Чтобы избежать утечки, выносите только нужные значения в лямбду:
Или даже:
НО! Убедитесь, что переменные вне лямбды не держат в памяти тяжёлые объекты, которые не нужны после исполнения.
💡 Вывод:
- Java лямбды могут неявно захватывать контекст, включая большие объекты
- Это может привести к утечкам памяти, особенно в long-lived объектах (пулы задач, слушатели и т.п.)
- Профилируйте и проверяйте, что захватывает ваша лямбда
#java #memoryleak #lambda #gc #profiling
@javatg
Задача:
Предположим, вы пишете сервис, в котором создаются задачи (например, отложенные действия):
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
@javatg
👍15🤔5❤4🔥2