Коллекции 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 в a HashSet.

Куча Java != системная память. Размер кучи Java по умолчанию составляет только 128 МБ. Обратите внимание, что это также отличается от памяти, используемой JVM.

что касается вашего вопроса: документы,

public int size()

возвращает число элементы в этом коллекция. Если эта коллекция содержит больше, чем Integer.MAX_VALUE elements, returns Integer.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 раз займет достаточно много времени, чтобы вы хотели убить процесс в любом случае).