Освобождает ли вызов метода clear() у ArrayList память, занимаемую элементами?

Ответ

Да, но с важным уточнением.

Метод ArrayList.clear() выполняет две операции:

  1. Освобождает ссылки на все элементы, хранящиеся в списке. Это позволяет сборщику мусора (Garbage Collector) удалить сами объекты-элементы, если на них больше нет других ссылок.
  2. Обнуляет счетчик размера (size = 0).

Однако, внутренний массив (Object[] elementData), в котором хранятся ссылки на элементы, остается прежнего размера. Его емкость (capacity) не уменьшается. Это сделано для производительности: если в очищенный список снова начнут добавлять элементы, не потребуется немедленное дорогостоящее перевыделение памяти и копирование массива.

Как действительно уменьшить потребление памяти? После clear() можно вызвать метод trimToSize(). Он создает новый внутренний массив минимально необходимого размера (по умолчанию 0, а при следующем добавлении - 10), копируя в него старые данные (которых нет), тем самым освобождая память от большого пустого массива.

Пример:

ArrayList<String> list = new ArrayList<>(1000); // capacity = 1000
list.add("A");
list.add("B");
System.out.println(list.size()); // 2

list.clear(); // Элементы "A" и "B" могут быть собраны GC.
               // Но массив elementData.length все еще равен 1000.
System.out.println(list.size()); // 0

list.trimToSize(); // Теперь elementData.length станет 0 (или DEFAULT_CAPACITY=10)
                   // Старый массив на 1000 ячеек будет удален GC.

Вывод: clear() освобождает память от элементов, но не от избыточной емкости внутреннего массива. Для полного освобождения используйте trimToSize().

Ответ 18+ 🔞

Э, слушай, а вот этот твой ArrayList.clear() — он же не совсем так работает, как многие думают, блядь. Ну, то есть работает, но с одной хитрой жопой, ёпта.

Представь себе, у тебя список, как здоровенный чемодан. Ты туда накидал две пары носков — это твои элементы. Потом ты вызываешь clear(). Что делает адекватный человек? Вываливает носки нахуй и выбрасывает чемодан, чтобы место не занимал. А что делает Java? Она носки-то вываливает, да, но чемодан, сука, оставляет! Просто закрывает его и ставит в угол. Говорит: «А вдруг ещё понадобится, я его не буду перешивать». И этот чемодан, ёбана, места столько же и занимает, хоть он и пустой!

По-умному это называется: ссылки на элементы он обнуляет — сборщик мусора потом может прийти и эти объекты подмести, если они больше никому не нужны. И счётчик размера (size) он, конечно, в ноль сбрасывает. Но вот этот внутренний массив, elementData, где всё лежало — его ёбнуть размером он не хочет. Ёперный театр, зачем? А вдруг ты через пять секунд опять двадцать гигабайт данных в него пихнёшь? Чтобы каждый раз не пересоздавать массив — дорого, блядь.

Так как же всё-таки этот чемодан выкинуть, если он тебе не нужен? А вот тут, дружок, есть метод trimToSize(). Это как взять ножницы и отрезать от чемодана всё лишнее, оставив только дно. Вызываешь его после clear() — и он создаёт новый, крошечный массив (вообще пустой, на 0 элементов), а старый здоровый пустой массив отправляется на съедение сборщику мусора. Вот тогда память и освобождается по-настоящему.

Смотри, как это выглядит в коде, тут всё честно:

// Заказал чемодан на 1000 мест, блядь
ArrayList<String> list = new ArrayList<>(1000);
// А положил всего две хуиты
list.add("A");
list.add("B");
System.out.println(list.size()); // 2, логично

// Вытряхнул всё нахер
list.clear(); // Элементы "A" и "B" могут быть утилизированы GC.
               // Но массив elementData всё ещё размером в 1000 ячеек, ёбта!
System.out.println(list.size()); // 0

// А вот теперь взял и отрезал всё лишнее
list.trimToSize(); // Теперь elementData.length станет 0 (или сразу 10, по умолчанию)
                   // Старый массив на 1000 ячеек — пизда ему, будет удалён GC.

Итог, чтобы два раза не вставать: clear() освобождает память от содержимого, но не от самого контейнера. А trimToSize() — это уже полная зачистка, чтоб ни тебе, ни памяти лишнего.