Коллекции Java: что происходит, когда" размер "превышает"int"?
Я знаю, что коллекции Java очень голодны памяти, и сделал тест сам, доказывая, что 4GB едва хватает для хранения нескольких миллионов Integer
на HashSet
.
но что, если у меня "достаточно" памяти? Что будет с Collection.size()
?
EDIT: решила: Collection.size()
возвращает Integer.MAX
при превышении целочисленного диапазона.
новый вопрос: как определить "реальное" количество элементов коллекции тогда?
Примечание 1: извините, это, вероятно, вопрос let-me-google-it-for - you, но я действительно ничего не нашел;)
примечание 2: насколько я понимаю, каждая запись целого набора:
reference + cached_hashcode + boxed_integer_object + real_int_value
, да?
Примечание 3: смешно, даже с JDK7 и "сжатыми указателями", когда JVM использует 2 ГБ реальной памяти, он показывает только 1,5 ГБ выделенной памяти в VisualVM
.
для тех, кто заботится:
4 ответов
ваш вопрос, похоже, имеет совсем другое содержание, чем название.
вы уже ответили на вопрос в заголовке (Integer.MAX_VALUE
возвращается). И нет: вы не можете узнать "истинный" размер с нормальным API, безопасным для итерации по коллекции и подсчета (используя long
конечно).
если вы хотите сохранить Set
of int
значения и вы знаете, что диапазон и количество значений может стать очень большим, тогда a BitSet
на самом деле может быть лучше реализации:
import java.util.*;
import java.lang.management.*;
public final class IntegersInBitSetMemoryConsumption {
private final static int MILLION = 1000 * 1000;
public static void main(String... args) {
BitSet set = new BitSet(Integer.MAX_VALUE);
for (int i = 1;; ++i) {
if ((i % MILLION) == 0) {
int milsOfEntries = (i / MILLION);
long mbytes = ManagementFactory.getMemoryMXBean().
getHeapMemoryUsage().getUsed() / MILLION;
double ratio = mbytes / milsOfEntries;
System.out.println(milsOfEntries + " mil, " + mbytes + " MiB used, "
+ " ratio of bytes per entry: " + ratio);
}
set.set(i);
}
}
}
это создаст структуру данных постоянного размера, которая может содержать все значения внутри диапазона без изменения размера и занимать относительно небольшой объем памяти (1 бит на возможное значение плюс некоторые накладные расходы).
этот метод имеет два недостатка, однако:
- он не поддерживает отрицательные
int
значения - это не указать!--3--> В API
оба можно легко обойти, написав обертку, которая использует два BitSet
объекты (возможно, лениво выделенные) для хранения диапазона положительных и отрицательных значений соответственно и реализует методы адаптера для Set
интерфейс.
Я знаю, что коллекции Java очень память-голодная, и сделала тест сама, доказывая, что 4GB едва достаточно, чтобы храните несколько миллионов
Integers
в aHashSet
.
Куча Java != системная память. Размер кучи Java по умолчанию составляет только 128 МБ. Обратите внимание, что это также отличается от памяти, используемой JVM.
что касается вашего вопроса: документы,
возвращает число элементы в этом коллекция. Если эта коллекция содержит больше, чем
Integer.MAX_VALUE
elements, returnsInteger.MAX_VALUE
.
в исходном коде:
/**
* Returns the number of elements in this collection. If this collection
* contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
* <tt>Integer.MAX_VALUE</tt>.
*
* @return the number of elements in this collection
*/
int size();
общий ответ для любой реальной архитектуры процессора заключается в том, что вы просто не можете. Причина проста: не может быть больше выделенных объектов (размером не менее 1 слова), чем адресуемая память.
конечно, учитывая виртуальный характер JVM, есть сценарий, где это может произойти.
int
всегда будет подписан 32bit, и вы можете реализовать и запустить JVM поверх 64-битной машины, где может быть адресовано более 2 ГБ памяти.
в этом случае документация говорит нам, что Integer.MAX_INT
будут возвращены... И это большая проблема, потому что любой цикл, который использовал целочисленную переменную, полагающуюся на i < col.size()
остановить будет работать вечно (хотя я думаю, что все, что петли 2**31-1
раз займет достаточно много времени, чтобы вы хотели убить процесс в любом случае).