java.lang.OutOfMemoryError: пространство кучи Java - PullRequest
2 голосов
/ 25 февраля 2010

У меня есть ошибка, описанная ниже в трассировке, когда я пытаюсь загрузить файл FITS размером 80 193 КБ для обработки, чтобы отобразить выбранные поля. По сути, у меня есть фиктивный веб-интерфейс, который позволяет пользователю выбрать до 6 файлов FITS для загрузки и обработки. Я не получаю сообщение об ошибке при загрузке двух [разных] файлов FITS, размером примерно 54 574 КБ каждый, для обработки. Поля отображаются / печатаются на консоли. Однако при загрузке одного файла размером 80,193 КБ я получаю сообщение об ошибке ниже. Как мне решить это?

Первоначально я думал, что итерация была вычислительно дорогой, но я подозреваю, что это происходит при вызове readHDU для файла 80 МБ:

while ((newBasicHDU = newFits.readHDU()) != null) { 

Как мне решить, эффективно решить это? Я запускаю программу на Windows 7. Приветствия

Трассировка:

SEVERE: Servlet.service() for servlet FitsFileProcessorServlet threw exception
java.lang.OutOfMemoryError: Java heap space
    at java.lang.reflect.Array.multiNewArray(Native Method)
    at java.lang.reflect.Array.newInstance(Unknown Source)
    at nom.tam.util.ArrayFuncs.newInstance(ArrayFuncs.java:1028)
    at nom.tam.fits.ImageData.read(ImageData.java:258)
    at nom.tam.fits.Fits.readHDU(Fits.java:573)
    at controller.FITSFileProcessor.processFITSFile(FITSFileProcessor.java:79)
    at controller.FITSFileProcessor.doPost(FITSFileProcessor.java:53)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
    at java.lang.Thread.run(Unknown Source)

Код:

/**
     * 
     * @param
     * @return
     */
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Check that we have a file upload request
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);

        if (isMultipart) {

            Fits newFits = new Fits();
            BasicHDU newBasicHDU = null;
            ServletFileUpload upload = new ServletFileUpload();                     // Create a new file upload handler

            // Parse the request
            try {
                //List items = upload.parseRequest(request);                        // FileItem
                FileItemIterator iter = upload.getItemIterator(request);

                // iterate through the number of FITS FILES on the Server
                while (iter.hasNext()) {
                    FileItemStream item = (FileItemStream) iter.next();
                    if (!item.isFormField()) {
                        this.processFITSFile(item, newFits,newBasicHDU );
                    }
                }
            } catch (FileUploadException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }       
        }
    }

    /**
     * 
     * @param
     * @return
     */
    public void processFITSFile(FileItemStream item, Fits newFits, BasicHDU newBasicHDU) throws IOException {

        // Process the fits file
        if (!item.isFormField()) {
            String fileName = item.getName();                                       //name of the FITS File
            try {   
                System.out.println("Fits File Fields Printout: " +  fileName);
                InputStream fitsStream = item.openStream();                             
                newFits = new Fits(fitsStream);
                System.out.println( "number of hdu's if: " + newFits.getNumberOfHDUs());

                while ((newBasicHDU = newFits.readHDU()) != null)  {                //line 76
                    System.out.println("Telescope Used: " + newBasicHDU.getTelescope());
                    System.out.println("Author: " + newBasicHDU.getAuthor());
                    System.out.println("Observer: " + newBasicHDU.getObserver() );
                    System.out.println("Origin: " + newBasicHDU.getOrigin() );
                    System.out.println("End of Printout for: \n" + fileName);
                    System.out.println();               
                }

                fitsStream.close();

            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }       
    }

Ответы [ 5 ]

16 голосов
/ 25 февраля 2010

Большинство ответов были сосредоточены вокруг увеличения размера кучи по умолчанию (который составляет 64 МБ). 64 МБ объяснят, почему вы можете успешно загрузить 54 574 КБ, потому что он меньше этого размера, и я уверен, что tomcat и ваша программа при загрузке занимают не более 10 МБ. Хорошая идея - увеличить память, но на самом деле она лечит симптомы, а не болезнь.

Если вы планируете разрешить нескольким пользователям загружать большие файлы, тогда двум пользователям, загружающим файлы размером 80 МБ одновременно, потребуется 160 МБ. И если 3 пользователя сделают это, вам понадобится 240 МБ и т. Д. Вот что я бы посоветовал. Найдите библиотеку, которая не читает этот материал в ОЗУ, прежде чем записать его на диск. Это сделает ваше приложение масштабным, и это реальное решение этой проблемы.

Используйте jconsole (поставляется с JDK), чтобы посмотреть размер кучи во время работы программы. Jconsole действительно прост в использовании, и вам не нужно настраивать JVM для его использования. Так что в этом смысле это намного проще, чем профилировщик, но вы не будете получать столько подробностей о вашей программе. Тем не менее, вы можете лучше видеть три части памяти (Eden, Survivor и Tenured). Иногда странные вещи могут вызывать нехватку памяти в одной из этих областей, даже если вы выделили много памяти JVM. JConsole покажет вам подобные вещи.

5 голосов
/ 25 февраля 2010

Похоже, вы не выделили Tomcat достаточно памяти - вы можете решить эту проблему, указав, например, -Xmx512m, чтобы выделить до 512 МБ памяти.

См. здесь для более подробной информации о том, как установить это.

3 голосов
/ 25 февраля 2010

Лучшим решением было бы улучшить код, чтобы он не излишне дублировал данные в памяти. Эта трассировка предполагает, что код пытается клонировать содержимое файла в памяти. Если невозможно улучшить (сторонний?) Код так, чтобы он этого не делал, а вместо этого немедленно обрабатывает его, то лучше сконфигурировать / использовать общий FileUpload, чтобы он не сохранял загруженные файлы. в памяти, но вместо этого во временном хранилище в локальной файловой системе диска.

Ваша лучшая попытка минимизировать используемую память - использовать DiskFileItemFactory с небольшим порогом (по умолчанию, кстати, 10 КБ, что доступно).

ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());

Таким образом, вам достаточно памяти для реальной обработки.

Если вы все равно достигнете пределов кучи памяти, то следующим шагом будет увеличение кучи.

2 голосов
/ 25 февраля 2010

Вы пытаетесь использовать больше оперативной памяти, чем у вас есть.

Попробуйте увеличить максимальный объем памяти, добавив флаг -Xmx при запуске программы.

java -Xmx128m  youProgram 

Это присвоило бы вашей программе 128 мегабайт памяти как максимум .

0 голосов
/ 25 февраля 2010

Ошибка java.lang.OutOfMemoryError означает, что вы превысили объем памяти, выделенной JVM. Используйте -Xmx, чтобы изменить максимальный размер кучи памяти вашей JVM. (спасибо Software Monkey)

Вы можете сделать это, чтобы увидеть, каков размер памяти jvm:

MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
System.out.println( memoryBean.getHeapMemoryUsage() );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...