Ошибка нехватки памяти в Java - PullRequest
2 голосов
/ 17 апреля 2010

Я получаю OutOfMemoryError: куча Java

фрагменты метода:

{
// step 1: I am creating a 2 dim array
  int totalCombination = (int) Math.pow(2.0, (double) vowelCount);
// here vowelCount > 10

// step2: initializing my array
// step3: and using that array
}

Мой вопрос:

каждый раз, когда вызывается этот метод, создается этот массив. Возможно ли, что массив не освобождается.

В диспетчере задач Windows я вижу, что память, используемая Java, является чисто инкрементной. Так что это не значит, что в какой-то момент размер кучи меньше, но память используется повторно и не освобождается каким-либо образом.

Пожалуйста, дайте мне знать, если вам нужно больше деталей.

Пожалуйста, помогите отладить ошибку.

Anuj

Часть кода, которая может вызывать ошибку:

int totalCombination = (int) Math.pow (2.0, (double) vowelCount);

    int lookupArray[][] = new int[totalCombination][vowelCount];

    // initialize lookupArray

    for (int i = 0; i < totalCombination; i++) {
        for (int j = 0; j < vowelCount; j++) {
            lookupArray[i][j] = 0;
        }
    }

    // populate lookupArray
    //vowelCount : number of vowels in a word
    // if count is 2, then array will contain 00,01,10,11

    for (int i = 1; i < totalCombination; i++) {
        for (int c = 0; c < vowelCount; c++) {
            lookupArray[i][c] = lookupArray[i - 1][c];
        }
        boolean flag = true;
        for (int j = vowelCount - 1; j >= 0 && flag; j--) {
            if (lookupArray[i - 1][j] == 1) {
                lookupArray[i][j] = 0;
            } else if (lookupArray[i - 1][j] == 0) {
                lookupArray[i][j] = 1;
                flag = false;
            }
        }
    }


  // this part total combination of a word having different combination of vowels in it.


    for (int i = 0; i < totalCombination; i++) {
        int vcount = vowelCount - 1;
        StringBuffer stringBuffer = new StringBuffer();

        for (int j = 0; j < word.length(); j++) {
            if (wordArr[j] == 'a' || wordArr[j] == 'e' || wordArr[j] == 'i'
                    || wordArr[j] == 'o' || wordArr[j] == 'u') {
                if (lookupArray[i][vcount] == 1) {
                    stringBuffer.append(wordArr[j]);
                }
                vcount--;
            } else {
                stringBuffer.append(wordArr[j]);
            }
        }

Ответы [ 3 ]

8 голосов
/ 17 апреля 2010

Сила двух возрастает в геометрической прогрессии. Если значение vowelCount высокое, один массив может легко вызвать OutOfMemoryError (2^32 = 4GB).

Вы можете попытаться настроить максимальные требования к памяти вашей виртуальной машины (например, -Xmx512m), но понимаете, что ваш алгоритм требует МНОГО ПАМЯТИ . Возможно, вы захотите найти лучший алгоритм, если это вообще возможно.


Смотри также


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

Имейте в виду, что это все еще экспоненциальный рост, поэтому, несмотря на то, что вы позаботились о потребности в памяти от O(2^N) до O(N), сложность вашего времени по-прежнему O(2^N).

каждый раз, когда вызывается этот метод, создается этот массив. Возможно ли, что массив не освобождается.

Да, это очень возможно, если ссылка на массив когда-либо просочилась, а затем что-то где-то держится за эту ссылку. Сборщику мусора на самом деле все равно, что вы думаете, что это / не мусор; пока объект ссылается на что-то (и это не слабая ссылка и т. д.), это НЕ мусор.


После выяснения того, что вы пытаетесь сделать, вот мое решение. Обратите внимание, что он вообще не генерирует массив битов.

static void generate(String prefix, String suffix) {
    int i = suffix.replaceAll("[aeiou].*", "").length();
    if (i == suffix.length()) {
        System.out.println(prefix + suffix);
    } else {
        generate(prefix + suffix.substring(0, i), suffix.substring(i + 1));
        generate(prefix + suffix.substring(0, i+1), suffix.substring(i + 1));
    }
}

// generate("", "apple");

Использует регулярное выражение, чтобы найти следующий гласный. Вместо этого вы можете использовать обычный цикл for, и общий алгоритм все равно будет работать. Вы можете оптимизировать его, чтобы использовать вместо него StringBuilder (в этом фрагменте я приведу краткость и, надеюсь, ясность).


Вот альтернативное решение, которое использует split для предварительного разделения входной строки на части (O(N) пробел), затем использует StringBuilder для генерации всех других строк (O(N) пробел).

static void generate(StringBuilder sb, String[] parts, int i) {
    if (i == parts.length) {
        System.out.println(sb.toString());
    } else {
        if ("aeiou".contains(parts[i])) {
            generate(sb, parts, i + 1);
        }
        sb.append(parts[i]);
        generate(sb, parts, i + 1);
        sb.setLength(sb.length() - parts[i].length());
    }
}
static void generate(String s) {
    generate(
        new StringBuilder(),
        s.split("(?<=[aeiou])|(?=(?!^)[aeiou])"),
        0
    );
}

// generate("apple");

Регулярное выражение разбивает "apple" на [ "a", "ppl", "e" ]. Он разделяется везде после гласной или (если это не начало строки) везде перед гласной.

Теперь должно быть очевидно, что требование к пространству составляет O(N), поэтому, если длина вашей строки не равна смехотворно длинной , это не должно вызывать OutOfMemoryError.

Конечно, если вы храните сгенерированных строк - все O(2^N) из них - в памяти, тогда конечно вы получите OutOfMemoryError. Я надеюсь, что этот факт очевиден.

Вся идея состоит в том, чтобы не хранить в памяти ничего, что вам не нужно для генерации этого ОГРОМНОГО ВЫХОДА. Если затем вы сохраните весь этот ОГРОМНЫЙ ВЫХОД в памяти (вместо, скажем, печати их в stdout или в файл), тогда он потерпит поражение всей цели, и вы получите OutOfMemoryError , как ожидается .

1 голос
/ 17 апреля 2010

Я предполагаю, что у вас есть что-то вроде следующего кода:

int totalCombination = 1 << vowelCount;
System.out.println("totalCombination = " + totalCombination);
System.out.println("totalCombination (in Millions) = " + totalCombination / 1000 / 1000);

int[] arr = new int[totalCombination];

В 32-битной виртуальной машине размер массива не может превышать 4 ГБ, то есть 1024 миллиона записей. Убедитесь, что вы всегда печатаете меньшие числа в приведенном выше коде.

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

1 голос
/ 17 апреля 2010

Вы можете рассмотреть возможность использования профилировщика, который может дать вам представление о том, какие типы объектов существуют в любой момент времени в вашей программе. Например, в NetBeans есть встроенный профилировщик.

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

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