Java OOM о создании 2 массивов из одного миллиарда целых - PullRequest
5 голосов
/ 24 марта 2019

Я пишу простую программу на Java, чтобы создать 2 массива int размером 1 миллиард. Я запустил эту программу с -Xms10G, т. Е. 10 ГБ памяти все равно я получил ошибку OOM. Ниже приведен фрагмент.

public class TestBigIntArraySize {
  public static int arraySize = 1000_000_000;
  public static int [] firstArray = new int[arraySize];
  public static int [] secondArray = new int[arraySize];

  public static void main(String[] args) {
    System.out.println(1000_000_000 * Integer.SIZE);
  }
}

Насколько я могу судить, память, используемая для массива 1 млрд. Int, будет System.out.println (1000_000_000 * Integer.SIZE); который возвращает 1 935 228 928, что меньше, чем 2 ГБ. Таким образом, общие требования к моим программам будут не более 4 ГБ.

Я получаю ошибку, даже если я создаю массивы в вызове метода и возвращаю массив или статический (как показано ниже) или внутри main (). Память, которая требуется для его работы, составляет 12G, что в 3 раза больше, чем я ожидал. Я использую Oracle Java: jdk1.8.0_201

Я попробовал опцию -Xms10G -XX: NewRatio = 1 ---, которая работала.

Но я хочу уменьшить объем памяти.

Я попытался добавить больше памяти для eden -Xms9G -XX:NewRatio=0.5, но java жалуется на недопустимый аргумент.

Я попробовал вариант прямого выделения массива для старого поколения -Xms9G -XX:NewRatio=1 -XX:PretenureSizeThreshold=10000. Но это также дает OOM.

Это просто экспериментальный проект, и я просто манипулирую массивом на месте. Я хотел бы сделать это в минимально возможной памяти . Может кто-нибудь подсказать, как это сделать? Какие варианты java и почему?

Ответы [ 2 ]

3 голосов
/ 24 марта 2019

Итак, скажем, умножение на длина бита , вероятно, не лучший способ получить количество байтов. Как упомянул @mayamar, ваше фактическое использование памяти составляет около 2 * 4 гига байт .

В любом случае, давайте перейдем к фактической настройке фразы. 4 ГБ, вероятно, слишком большой и будет храниться в старом поколении напрямую. Так что вам нужно увеличить размер старого поколения. Изменение настроек нового поколения может работать, но это ... хорошо, тогда вы фактически выключаете старый генератор. Это может нанести вред вашей другой части тестовых случаев.

Ваша попытка NewRatio=1 сделать соотношение между новым и старым поколением равным 1: 1 вместо чего-то лучшего, например. 1: 100. Тем не менее, если вы сделаете слишком большое соотношение, JVM может не загрузиться (GC во время инициализации VM). Лучше просто указать это с помощью MaxNewSize.

В конце концов, выполнение чего-то похожего на это будет очень близко к вашему требованию «минимизировать использование памяти».

java -Xmx8400000000 -XX:MaxNewSize=30M -XX:OldSize=8300000000 TestBigIntArraySize 

Примечание: лучше сэкономить несколько десятков мегабайт, так как самой JVM потребуется память для работы. Если ваша программа не такая маленькая, как MVCE, вам нужно будет оставить немного больше места, если вы не хотите, чтобы GC запускал время от времени.

2 голосов
/ 24 марта 2019

Та же проблема, что и здесь .

В JDK 8, который использует Parallel GC по умолчанию, куча 10 ГБ делится на 6,67 ГБ старого поколения + 3,33 ГБ молодого поколения.Таким образом, нет места для размещения двух смежных блоков по 3,72 ГБ (1 миллиард четырехбайтовых целых чисел).

Самый простой способ решить эту проблему - включить G1 GC и вообще избежать сложного определения размеров генерации.Ваш пример будет работать с кучей 8 ГБ:

java -XX:+UseG1GC -Xmx8g TestBigIntArraySize
...