Java из кучного пространства во время сериализации

17

Следующий код вызывает OutOfMemmoryError: пустое место для примерно 3 миллионов строк. Память, выделенная для JVM, составляет 4 ГБ, с использованием 64-битной установки.

while (rs.next())
{

    ArrayList<String> arrayList = new ArrayList<String>();
    for (int i = 1; i <= columnCount; i++)
    {
        arrayList.add(rs.getString(i));
    }

    objOS.writeObject(arrayList);
}

Память, на которую ссылается ArrayList, имеет право на сбор мусора на каждой итерации цикла while, а внутренне JVM вызывает сбор мусора ( System.gc() ), прежде чем выбросить ошибку OutOfMemory из-за кучи.

Итак, почему возникает исключение?

    
задан Nik 21.09.2011 в 07:52
источник

2 ответа

35

Является ли objOS ObjectOutputStream ?

Если это так, то это ваша проблема: ObjectOutputStream сохраняет сильную ссылку на каждый объект, который когда-либо был написан для него, чтобы избежать записи одного и того же объекта дважды (он просто напишет ссылаясь на «этот объект, который я написал ранее с id x ").

Это означает, что вы эффективно протекаете all ArrayList istances.

Вы можете сбросить этот «кеш», вызвав reset() на ваш ObjectOutputStream . Так как вы все равно не используете , используя эту функцию, вы можете вызвать reset() непосредственно после вызова writeObject() .

    
ответ дан Joachim Sauer 21.09.2011 в 07:59
источник
4

Я согласен с @Joachim.

Ниже предложение было мифом

Кроме того, рекомендуется (в хорошем соглашении) не объявлять объект внутри цикла . Вместо этого объявите его непосредственно перед запуском цикла и используйте ту же ссылку для инициализации. Это потребует, чтобы ваш код использовал одну и ту же ссылку для каждой итерации и уменьшал нагрузку на поток освобождения памяти (т. Е. Сбор мусора).

Истина
У меня есть отредактированный , потому что я чувствую, что может быть много людей, которые (как и я до сегодняшнего дня) все еще верят, что объявление объекта внутри цикла может нанести вред управлению памятью; что неверно. Чтобы продемонстрировать это, я использовал тот же код, который был размещен на stackOverflow для этого .
Ниже приведен фрагмент кода

package navsoft.advskill.test;

import java.util.ArrayList;

public class MemoryTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        /* Total number of processors or cores available to the JVM */
        System.out.println("Available processors (cores): "
                + Runtime.getRuntime().availableProcessors());
        /*
         * Total amount of free memory available to the JVM
         */
        long freeMemory = Runtime.getRuntime().freeMemory();
        System.out.println("Free memory (bytes): "
                + freeMemory);
        /*
         * This will return Long.MAX_VALUE if there is no preset limit
         */
        long maxMemory = Runtime.getRuntime().maxMemory();
        /*
         * Maximum amount of memory the JVM will attempt to use
         */
        System.out.println("Maximum memory (bytes): "
                + (maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory));
        /*
         * Total memory currently in use by the JVM
         */
        System.out.println("Total memory (bytes): "
                + Runtime.getRuntime().totalMemory());
        final int LIMIT_COUNTER = 1000000;

        //System.out.println("Testing Only for print...");
        System.out.println("Testing for Collection inside Loop...");
        //System.out.println("Testing for Collection outside Loop...");
        //ArrayList<String> arr;
        for (int i = 0; i < LIMIT_COUNTER; ++i) {
            //arr = new ArrayList<String>();
            ArrayList<String> arr = new ArrayList<String>();
            System.out.println("" + i + ". Occupied(OldFree - currentFree): "+ (freeMemory - Runtime.getRuntime().freeMemory()));
        }
        System.out.println("Occupied At the End: "+ (freeMemory - Runtime.getRuntime().freeMemory()));
        System.out.println("End of Test");
    }

}

Результат вывода ясно показывает, что нет разницы в занятии / освобождении памяти, если вы либо объявляете объект внутри, либо вне цикла. Поэтому рекомендуется иметь декларацию как можно меньше. Я благодарю всех экспертов по StackOverflow (особенно @Miserable Variable) за то, что вы мне это посоветовали.

Надеюсь, это тоже уберет ваши сомнения.

    
ответ дан Naved 21.09.2011 в 08:12
источник