System.out.print занимает слишком много памяти при печати на консоль. Можно ли уменьшить? - PullRequest
0 голосов
/ 23 января 2019

У меня есть простая программа:

public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 1_000_000; i++) {
            System.out.print(1);
        }
    }
}

И запустил профилирование. Вот результаты:

enter image description here

Я предполагаю, что память увеличивается из-за вызовов этого метода:

public void print(int i) {
        write(String.valueOf(i));
    }

Можно ли печатать значения int в консоли без использования памяти?

На локальной машине я пытаюсь добавить if (i % 10000 == 0) System.gc(); к циклу и выравнивать потребление памяти. Но система, которая проверяет решение, не принимает решения. Я пытался изменить значения шага, но все равно не проходит ни в память (должно работать меньше 20 МБ), ни во время (<1 сек) </p>

РЕДАКТИРОВАТЬ Я пытаюсь это

    String str = "hz";
    for (int i = 0; i < 1_000_0000; i++) {
        System.out.print(str);
    } 

enter image description here Но тот же результат:

РЕДАКТИРОВАТЬ2 если я напишу этот код

public class Test {
    public static void main(String[] args) {
        byte[] bytes = "hz".getBytes();
        for (int i = 0; i < 1_000_0000; i++) {
            System.out.write(bytes, 0, bytes.length);
        }
    }
}

У меня есть это

enter image description here

Поэтому я не верю, что Java издает свои шумы. Они будут в обоих случаях.

Ответы [ 3 ]

0 голосов
/ 23 января 2019

Тип использования памяти типичен для Java. Ваш код не имеет значения. Чтобы контролировать использование памяти Java, вам нужно использовать некоторые параметры -X, например "-Xms512m -Xmx512m" установит минимальный и максимальный размер кучи равным 512m. Кстати, чтобы свести к минимуму график памяти, подобный свиноматке, рекомендуется установить минимальное и максимальное значения в одно и то же значение. Эти параметры могут быть переданы Java в командной строке при запуске Java, например:

java -Xms512m -Xmx512m myProgram

Есть и другие способы. Вот одна ссылка, где вы можете узнать больше об этом: Oracle docs . Есть другие параметры, которые управляют размером стека и некоторые другие вещи. Сам код, если он написан без учета использования памяти, также может влиять на использование памяти, но в вашем случае это слишком тривиально кода, чтобы что-либо делать. Большинство проблем с памятью решается путем настройки параметров использования памяти jvm

0 голосов
/ 23 января 2019

Поскольку вы обнаружили, что печать byte[] сохраняет выделение памяти в необходимых пределах, вы можете использовать этот факт:

Выделите байтовый массив длиной представления ASCII Integer.MIN_VALUE (11 - самое длинное целое число может быть). Затем вы можете заполнить массив в обратном направлении, чтобы преобразовать число i:

int p = buffer.length;
if (i == Integer.MIN_VALUE) {
  buffer[--p] = (byte) ('0' - i % 10);
  i /= 10;
}
boolean neg = i < 0;
if (neg) i = -i;
do {
  buffer[--p] = (byte) ('0' + i % 10);
  i /= 10;
} while (i != 0);
if (neg) buffer[--p] = '-';

Затем напишите это в свой поток:

out.write(buffer, p, buffer.length - p);

Вы можете использовать один и тот же буфер, чтобы записать столько чисел, сколько пожелаете.

0 голосов
/ 23 января 2019

Вам нужно конвертировать int в символы без генерации нового String каждый раз, когда вы это делаете.Это можно сделать несколькими способами:

  • Создать собственный метод «int to characters», который преобразует в байты ASCII в byte[] (см. Пример кода @ AndyTurner).Затем напишите byte[].И повторите.

  • Используйте ByteBuffer, заполните его напрямую, используя пользовательский метод конвертации "в символы", и используйте Channel для вывода байтов, когда буфер заполнен.И повторите.

Если все сделано правильно, вы сможете выводить числа без всякого мусора ... кроме ваших одноразовых буферов.

Обратите внимание, чтоSystem.out - это PrintStream упаковка BufferedOutputStream упаковка FileOuputStream.И когда вы выводите String прямо или косвенно, используя один из методов print, это фактически делает через BufferedWriter, который является внутренним по отношению к PrintStream.Это сложно ... и, очевидно, метод print(String) генерирует мусор где-то в этой сложности.


Относительно вашего EDIT 1: когда вы многократно выводите постоянную строку, вы по-прежнему генерируете мусор.Я был удивлен этим, но я предполагаю, что это происходит в BufferedWriter.

Относительно вашего РЕДАКТИРОВАНИЯ 2: когда вы неоднократно пишете с byte[], генерация мусора практически исчезает.Это подтверждает, что хотя бы одно из моих предложений будет работать.

Однако, поскольку вы отслеживаете JVM с помощью внешнего профиля, на вашей JVM также работает агент , который периодически отправляет обновления вваш профилировщикЭтот агент, скорее всего, будет генерировать небольшое количество мусора.И могут быть другие источники мусора в JVM;например, если у вас включено ведение журнала JVM GC.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...