← Назад к вопросам
Сколько займется памяти при вызове clear у 1 млн Integer в списке размером 48 Мб?
2.0 Middle🔥 171 комментариев
#Другое
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Сколько займется памяти при вызове clear у 1 млн Integer в списке размером 48 Мб?
Ответ: ВСЕ 48 Мб ещё будут заняты (с уточнениями)
Это вопрос с подвохом, проверяющий понимание memory management в Java.
Анализ памяти в ArrayList
Исходное состояние:
┌─────────────────────────────────┐
│ ArrayList (размер 1млн элементов)│
│ Займет памяти: ~48 Мб │
└─────────────────────────────────┘
│
└─→ elementData[] (Object array)
├─→ Integer@0x1001 → 42
├─→ Integer@0x1002 → 100
├─→ Integer@0x1003 → 55
└─→ ... 999,997 more objects
После вызова list.clear():
┌─────────────────────────────────┐
│ ArrayList (size = 0) │
│ Займет памяти: ~48 Мб - 4К │
└─────────────────────────────────┘
│
└─→ elementData[] (Object array) - ВСЕ ЕЩЁ НА МЕСТЕ!
├─→ null (ссылка очищена)
├─→ null (ссылка очищена)
├─→ null (ссылка очищена)
└─→ ... 999,997 null references
Реализация ArrayList.clear()
public class ArrayList<E> extends AbstractList<E> {
transient Object[] elementData;
private int size;
public void clear() {
// Сбросить все ссылки (но не освобождить массив!)
for (int i = 0; i < size; i++)
elementData[i] = null;
// Обновить счетчик
size = 0;
}
}
Что происходит:
- elementData[] массив НЕ удаляется
- Он продолжает занимать ~48 Мб памяти
- Все ссылки на Integer объекты сбрасываются в null
- Integer объекты могут быть собраны GC
- Но сам массив остается!
Вычисление памяти
// Исходное состояние
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
list.add(i); // Автобоксинг Integer
}
// Структура памяти:
Мемория = elementData array + Integer objects
// elementData[] (Object array из 1млн элементов)
// Каждая ссылка = 8 bytes (на 64-bit JVM)
// array overhead = ~12 bytes
// Итого: 12 + (1_000_000 * 8) = 8,000,012 bytes ≈ 7.6 Мб
// Integer objects (1 млн объектов)
// Integer object = 16 bytes (12 header + 4 value)
// Итого: 1_000_000 * 16 = 16,000,000 bytes = 15.3 Мб
// + auto-boxing создаёт дополнительные объекты
// Итого: ~48 Мб (как сказано в задаче)
// ─────────────────────────────────────────────
// После list.clear():
Мемория = elementData array (остается) + null (объекты GC'd)
// elementData[] остается на месте = ~7.6 Мб
// Integer objects могут быть собраны = ~15.3 Мб
// Итого: ~40 Мб освобождается (Integer objects)
// Но массив занимает ~7.6 Мб
Правильный ответ
После clear():
✅ Освободится: ~40 Мб (Integer objects)
❌ Остается занято: ~7.6-8 Мб (elementData array)
Общая памят в ArrayList: с 48 Мб → до 8 Мб
Дополнительный анализ: Ёмкость vs Размер
// Важное различие
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
list.add(i);
}
System.out.println("Size: " + list.size()); // 1,000,000
System.out.println("Capacity: " + ?"); // ??? как получить?
// Нельзя напрямую, но можно через reflection
Field field = ArrayList.class.getDeclaredField("elementData");
field.setAccessible(true);
Object[] array = (Object[]) field.get(list);
int capacity = array.length;
System.out.println("Actual capacity: " + capacity); // ≥ 1,000,000
// При clear():
list.clear();
// Size = 0, но Capacity всё ещё ≥ 1,000,000
Вопрос: Как полностью освободить память?
Вариант 1: Создать новый ArrayList
ArrayList<Integer> list = new ArrayList<>(1_000_000);
for (int i = 0; i < 1_000_000; i++) {
list.add(i);
}
System.out.println("Before: " + getMemoryUsage());
list.clear(); // Освободит Integer objects, но массив остается
System.out.println("After clear: " + getMemoryUsage()); // ~48Mb → ~8Mb
// Полное освобождение
list = new ArrayList<>(); // GC соберет старый ArrayList и его elementData
System.out.println("After new ArrayList: " + getMemoryUsage()); // ~8Mb → ~0Mb
Вариант 2: Использовать LinkedList (может быть медленнее)
LinkedList<Integer> list = new LinkedList<>();
for (int i = 0; i < 1_000_000; i++) {
list.add(i);
}
list.clear(); // Удаляет все nodes
// Память: 48 Мб → практически 0 (каждый node - отдельный объект)
// Но LinkedList медленнее на 10x для random access
Вариант 3: Trimming capacity (если нужна частичная очистка)
// После clear(), если нужно освободить array
ArrayList<Integer> list = new ArrayList<>(1_000_000);
for (int i = 0; i < 1_000_000; i++) {
list.add(i);
}
list.clear();
// Сейчас: size = 0, capacity = 1_000_000, память ≈ 8Mb
// Способ "trimming" через reflection (не рекомендуется):
Field field = ArrayList.class.getDeclaredField("elementData");
field.setAccessible(true);
Object[] oldArray = (Object[]) field.get(list);
Object[] newArray = new Object[0];
field.set(list, newArray);
// Сейчас: память ≈ 0Mb (но это hacky)
// ✅ Правильный способ - создать новый ArrayList:
list = new ArrayList<>();
// Старый ArrayList с его elementData будет собран GC
Практический тест
public class MemoryTest {
static class Pair {
long free, total;
Pair(long free, long total) {
this.free = free;
this.total = total;
}
long used() {
return total - free;
}
}
static Pair getMemory() {
Runtime rt = Runtime.getRuntime();
return new Pair(rt.freeMemory(), rt.totalMemory());
}
public static void main(String[] args) throws Exception {
System.out.println("=== Memory Test ===");
// 1. Создать большой список
System.out.println("\n1. Creating ArrayList with 1M elements...");
Pair before = getMemory();
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
list.add(i);
}
Pair afterAdd = getMemory();
System.out.println("Memory used: " + (afterAdd.used() - before.used()) / (1024*1024) + " Mb");
// 2. Вызвать clear()
System.out.println("\n2. Calling clear()...");
list.clear();
System.out.println("Size after clear: " + list.size());
System.out.println("Capacity still: " + getCapacity(list));
Pair afterClear = getMemory();
System.out.println("Memory after clear: " + (afterClear.used() - before.used()) / (1024*1024) + " Mb");
System.out.println("Freed by clear: " + (afterAdd.used() - afterClear.used()) / (1024*1024) + " Mb");
// 3. Вызвать GC
System.out.println("\n3. Calling GC...");
System.gc();
Pair afterGC = getMemory();
System.out.println("Memory after GC: " + (afterGC.used() - before.used()) / (1024*1024) + " Mb");
System.out.println("Freed by GC: " + (afterClear.used() - afterGC.used()) / (1024*1024) + " Mb");
// 4. Создать новый список
System.out.println("\n4. Creating new ArrayList...");
list = new ArrayList<>();
System.gc();
Pair afterNew = getMemory();
System.out.println("Memory after new ArrayList: " + (afterNew.used() - before.used()) / (1024*1024) + " Mb");
System.out.println("\nРезультат: clear() не освобождает array, нужен new ArrayList()!");
}
static int getCapacity(ArrayList<?> list) throws Exception {
Field field = ArrayList.class.getDeclaredField("elementData");
field.setAccessible(true);
return ((Object[]) field.get(list)).length;
}
}
// Ожидаемый вывод:
// 1. Creating ArrayList with 1M elements...
// Memory used: 48 Mb
// 2. Calling clear()...
// Size after clear: 0
// Capacity still: 1000000
// Memory after clear: 48 Mb (only nulled references, array remains!)
// Freed by clear: 0 Mb
// 3. Calling GC...
// Memory after GC: 8 Mb (Integer objects collected)
// Freed by GC: 40 Mb
// 4. Creating new ArrayList...
// Memory after new ArrayList: ~0 Mb
// Freed by new ArrayList: 8 Mb (elementData array collected)
Резюме и ответ на вопрос
"Сколько займется памяти при clear() у 1 млн Integer в списке размером 48 Мб?"
Прямой ответ: ~8 Мб
Объяснение:
- ArrayList.clear() удаляет только ссылки на Integer объекты
- elementData массив (Object[]) ОСТАЕТСЯ на месте
- Integer объекты (~40 Мб) могут быть собраны GC
- elementData массив (~8 Мб) остается, ждёт новых добавлений
Распределение памяти:
До clear(): 48 Мб (7.6Мб array + 40Мб Integer objects)
После clear(): ~8 Мб (7.6Мб array + 0Мб Integer objects)
После new ArrayList(): ~0 Мб (все собрано GC)
Почему это так спроектировано:
- Оптимизация для случаев когда список будет заполнен снова
- Избегаем переаллокации массива
- Если часто очищаешь - нужен новый ArrayList()
Ключевой вывод:
- clear() ≠ освобождение памяти
- clear() просто nullifies ссылки
- GC собирает unreferenced objects
- Для полного освобождения нужен new ArrayList()