Как решить «java.lang.OutOfMemoryError: пространство кучи Java»? - PullRequest
0 голосов
/ 26 сентября 2011

Я пишу некоторый код для разбора очень большого плоского текстового файла на объекты, которые сохраняются в базе данных.Это работает с разделами файла (т. Е. Если я «наверху» первые 2000 строк), но у меня возникает ошибка java.lang.OutOfMemoryError: Java heap space, когда я пытаюсь обработать полный файл.

Я используюBufferedReader для чтения файла построчно, и у меня сложилось впечатление, что это отменяет требование загружать весь текстовый файл в память.Надеюсь, мой код достаточно понятен.Я провел свой код через анализатор памяти Eclipse, который сообщает мне:

В потоке java.lang.Thread @ 0x27ee0478 main хранятся локальные переменные с общим размером 69 668 888 (98,76%) байтов.
Память накапливается в одном экземпляре char [], загруженном*** **

Полезные комментарии с благодарностью!

Джонатан

public ArrayList<Statement> parseGMIFile(String filePath)
            throws IOException {

        ArrayList<Statement> statements = new ArrayList<Statement>();

        // Statement Properties
        String sAccount = "";
        String sOffice = "";
        String sFirm = "";
        String sDate1 = "";
        String sDate2 = "";
        Date date = new Date();
        StringBuffer sData = new StringBuffer();
        BufferedReader in = new BufferedReader(new FileReader(filePath));
        String line;
        String prevCode = "";
        int lineCounter = 1;
        int globalLineCounter = 1;

        while ((line = in.readLine()) != null) {

                // We extract the GMI code from the end of the first line
                String newCode = line.substring(GMICODE_START_POS).trim();

                // Extract date
                if (newCode.equals(prevCode)) {

                    if (lineCounter == DATE_LINE) { 
                        sDate1 = line.substring(DATE_START_POS, DATE_END_POS).trim();}

                    if (lineCounter == DATE_LINE2) {
                        sDate2 = line.substring(DATE_START_POS, DATE_END_POS).trim();}

                    if (sDate1.equals("")){
                        sDate1 = sDate2;}
                        SimpleDateFormat formatter=new SimpleDateFormat("MMM dd, yyyy");
                        try {
                            date=formatter.parse(sDate1);

                        } catch (ParseException e) {

                            e.printStackTrace();
                        }                   



                    sFirm = line.substring(FIRM_START_POS, FIRM_END_POS);
                    sOffice = line.substring(OFFICE_START_POS, OFFICE_END_POS);
                    sAccount = line.substring(ACCOUNT_START_POS,
                            ACCOUNT_END_POS);
                    lineCounter++;
                    globalLineCounter++;
                    sData.append(line.substring(0, END_OF_DATA)).append("\n");

                } else {

                    // Instantiate New Statement Object
                    Statement stmt = new Statement(sAccount, sOffice, sFirm,
                            date, sData.toString());


                    // Add to collection
                    statements.add(stmt);

                    // log.info("-----------NEW STATEMENT--------------");
                    sData.setLength(0);
                    lineCounter = 1;
                }
                prevCode = newCode;
        }
        return statements;
    }
STACKTRACE: Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dbPopulator' defined in class path resource [app-context.xml]: Invocation of init method failed; nested exception is java.lang.OutOfMemoryError: Java heap space
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1401)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:512)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:290)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:287)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:557)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:842)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:416)
    at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:93)
    at Main.main(Main.java:11)
Caused by: java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2882)
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:390)
    at java.lang.StringBuffer.append(StringBuffer.java:224)
    at services.GMILogParser.parseGMIFile(GMILogParser.java:133)
    at services.DBPopulator.init(DBPopulator.java:27)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1529)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1468)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1398)
    ... 12 more

Ответы [ 9 ]

5 голосов
/ 26 сентября 2011

Добавление дополнительной памяти в стартовые параметры ИМХО является ошибкой. Эти параметры имеют широкое применение. И может оштрафовать, увеличив gc раз. Кроме того, вы можете не знать размер заранее.

Вы используете MemoryMappedFiles и посмотрите на java.nio. *, чтобы сделать это. При этом вы можете загружать, когда читаете, и память не помещается в обычное пространство памяти.

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

  • A byte buffer может быть выделен как прямой буфер, и в этом случае виртуальная машина Java приложит все усилия для выполнения native I/O operations непосредственно на ней.

  • A byte buffer может быть создан путем сопоставления области файла непосредственно в память, и в этом случае доступно несколько дополнительных операций с файлами, определенных в классе MappedByteBuffer.

  • A byte buffer обеспечивает доступ к его содержимому в виде гетерогенной или однородной последовательности двоичных данных любого не-булева примитивного типа в байтовом порядке с прямым порядком байтов или байтов с прямым порядком байтов.

2 голосов
/ 23 октября 2012

-Xmx1024M -XX:MaxPermSize=256M решил мой java.lang.OutOfMemoryError: Java heap space error.

Надеюсь, это сработает.

2 голосов
/ 26 сентября 2011

Еще одна вещь, которая может произойти здесь: если ваш файл больше, чем половина вашей кучи и не содержит разрывов строк, в .readLine () попытается прочитать весь файл и в этом случае потерпит неудачу.

2 голосов
/ 26 сентября 2011

Кажется, ваше приложение использует память по умолчанию, выделенную виртуальной машиной (около 64 МБ, если я правильно помню).Поскольку ваше приложение специального назначения, я бы предложил увеличить объем памяти, доступной для приложения (например, запуск приложения с использованием java -Xmx256m позволит использовать до 256 МБ ОЗУ).Вы также можете попробовать запустить его, используя виртуальную машину сервера (java -server yourapp), которая попытается немного оптимизировать ситуацию.

2 голосов
/ 26 сентября 2011

Может быть, это объект операторов, который становится слишком большим? Если это так, может быть, вам следует сохранить его в базе данных партиями, а не сразу?

0 голосов
/ 09 сентября 2013

Я столкнулся с той же проблемой несколько месяцев назад

Я использовал Scanner класс:

Scanner scanner = new Scanner(file);

вместо:

BufferedReader in = new BufferedReader(new FileReader(filePath));
0 голосов
/ 14 июня 2013

Почему бы вам не попробовать заменить строку (если вы используете JDK 6, проблема с подстрокой памяти была решена в JDK 7)

String newCode = line.substring(GMICODE_START_POS).trim();

Заменить строку:

String newCode = new String(line.substring(GMICODE_START_POS));
0 голосов
/ 26 сентября 2011

Кажется, что sData вызывает переполнение.В тексте должно быть несколько (миллион?) Операторов с одним и тем же кодом GMI.

Накопление по char [] означает либо String, либо StringBuilder.Поскольку при изменении размера StringBuilder происходит сбой, это должно быть причиной.

Просто попробуйте вывести sData в stdout для отладки и посмотреть, что произойдет.

0 голосов
/ 26 сентября 2011
Код

мне кажется правильным. возможно я должен был использовать StringBuffer вместо String.

Строка довольно неприятна в Java, для каждой модификации, которую вы выполняете над ней, создается новый объект, и ссылки могут остаться в любом месте кода.

Обычно я читаю строки файла в приватном методе, используя локальные переменные, просто чтобы убедиться, что никаких ссылок на String не осталось.

Возвращаемый вами список - это список bean-компонентов со свойствами String? Если это так, замените их на StringBuffer и снова запустите профилирование.

Дайте мне знать, помогло ли это вам.

С уважением,

M.

...