Проблема с памятью Java с использованием Apache.POI для чтения и записи Excel

Я пытаюсь прочитать файл Excel... внести некоторые изменения... сохранить в новый файл.

Я создал небольшую форму с кнопкой... При нажатии кнопки..

  • Он загрузит файл Excel и загрузит все данные в список массивов класса, который я создал.
  • Он будет перебирать список массивов и изменять несколько свойств объектов.
  • Он сохранит данные в новый файл Excel.
  • Наконец, он очистит список массивов и покажет окно сообщения о завершении.

Теперь проблема с памятью.
Когда форма загружается, я вижу в диспетчере задач Windows... javaw использует около 23 МБ.
Во время чтения и записи excel... память расходуется до 170 МБ.
После очистки списка массивов.... Память не очищается и остается около 150 МБ.

Следующий код прикреплен к событию, связанному с нажатием кнопки.

MouseListener mouseListener = new MouseAdapter() {
        public void mouseReleased(MouseEvent mouseEvent) {
            if (SwingUtilities.isLeftMouseButton(mouseEvent)) {
                ArrayList<Address> addresses = ExcelFunctions.getExcelData(fn);
                for (Address address : addresses){
                    address.setZestimate(Integer.toString(rnd.nextInt(45000)));
                    address.setRedfinestimate(Integer.toString(rnd.nextInt(45000)));
                }
                ExcelFunctions.saveToExcel(ofn,addresses);
                addresses.clear();
                JOptionPane.showMessageDialog(null, "Done");
            }
        }
    };


Код для файла Reading/Excel в этом классе.

public class ExcelFunctions {
public static ArrayList<Address> getExcelData(String fn)
{
    ArrayList<Address> output = new ArrayList<Address>();
    try
    {
        FileInputStream file = new FileInputStream(new File(fn));

        //Create Workbook instance holding reference to .xlsx file
        XSSFWorkbook workbook = new XSSFWorkbook(file);

        //Get first/desired sheet from the workbook
        XSSFSheet sheet = workbook.getSheetAt(0);
        System.out.println(sheet.getSheetName());
        //Iterate through each rows one by one
        Iterator<Row> rowIterator = sheet.iterator();
        while (rowIterator.hasNext())
        {
            Row row = rowIterator.next();
            int r = row.getRowNum();
            int fc= row.getFirstCellNum();
            int lc = row.getLastCellNum();
            String msg = "Row:"+ r +"FColumn:"+ fc + "LColumn"+lc;
            System.out.println(msg);
            if (row.getRowNum() > 0) {
                Address add = new Address();
                Cell c0 = row.getCell(0);
                Cell c1 = row.getCell(1);
                Cell c2 = row.getCell(2);
                Cell c3 = row.getCell(3);
                Cell c4 = row.getCell(4);
                Cell c5 = row.getCell(5);
                if (c0 != null){c0.setCellType(Cell.CELL_TYPE_STRING);add.setState(c0.toString());}
                if (c1 != null){c1.setCellType(Cell.CELL_TYPE_STRING);add.setCity(c1.toString());}
                if (c2 != null){c2.setCellType(Cell.CELL_TYPE_STRING);add.setZipcode(c2.toString());}
                if (c3 != null){c3.setCellType(Cell.CELL_TYPE_STRING);add.setAddress(c3.getStringCellValue());}
                if (c4 != null){c4.setCellType(Cell.CELL_TYPE_STRING);add.setZestimate(c4.getStringCellValue());}
                if (c5 != null){c5.setCellType(Cell.CELL_TYPE_STRING);add.setRedfinestimate(c5.getStringCellValue());}
                output.add(add);
                c0=null;c1=null;c2=null;c3=null;c4=null;c5=null;
            }
        }
        workbook.close();
        file.close();
    }
    catch (Exception e)
    {
        System.out.println(e.getMessage());
    }
    return output;
}

public static void saveToExcel(String ofn, ArrayList<Address> addresses) {
    XSSFWorkbook workbook = new XSSFWorkbook();
    XSSFSheet sheet = workbook.createSheet("Addresses");

    Row header = sheet.createRow(0);
    header.createCell(0).setCellValue("State");
    header.createCell(1).setCellValue("City");
    header.createCell(2).setCellValue("Zip");
    header.createCell(3).setCellValue("Address");
    header.createCell(4).setCellValue("Zestimates");
    header.createCell(5).setCellValue("Redfin Estimate");

    int row = 1;

    for (Address address : addresses){
        Row dataRow = sheet.createRow(row);
        dataRow.createCell(0).setCellValue(address.getState());
        dataRow.createCell(1).setCellValue(address.getCity());
        dataRow.createCell(2).setCellValue(address.getZipcode());
        dataRow.createCell(3).setCellValue(address.getAddress());
        dataRow.createCell(4).setCellValue(address.getZestimate());
        dataRow.createCell(5).setCellValue(address.getRedfinestimate());
        row++;
    }


    try {
        FileOutputStream out =  new FileOutputStream(new File(ofn));
        workbook.write(out);
        out.close();
        workbook.close();
        System.out.println("Excel with foumula cells written successfully");

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}}


Я не могу понять, в чем проблема. Я закрываю workbook/inputstream/outputstream и очищаю Arraylist.


person Muhammad Sadiq    schedule 18.01.2016    source источник
comment
Ну... файл читается в память. Таким образом, используемая память зависит от размера файла. Вот и все.   -  person Seelenvirtuose    schedule 18.01.2016
comment
Я бы посоветовал вам пойти и прочитать о сборке мусора в Java и использовании памяти кучи в Java. То, что вы описали, для меня совершенно нормально   -  person Gagravarr    schedule 18.01.2016
comment
Чтобы уточнить ваш вопрос, вы сказали Memory is not clearing up and stays around 150MB . У вас есть фактическое исключение памяти после дополнительной операции? Или вы просто спрашиваете, почему память не очищается?   -  person Mooolo    schedule 18.01.2016
comment
@Seelenvirtuose ... размер файла меньше 1 МБ (с 2000 строк)   -  person Muhammad Sadiq    schedule 18.01.2016
comment
@Mooolo ... Я пытался запустить код события Click в цикле 100 раз ... в течение нескольких секунд ... он выстреливает до 800 МБ и остается там до конца .... У меня нет ошибок памяти ... они использование памяти остается до 800 МБ даже после того, как я получил фокус в конце события щелчка.   -  person Muhammad Sadiq    schedule 18.01.2016


Ответы (2)


Возможно, у вас нет утечки памяти...

Когда форма загружается, я вижу в диспетчере задач Windows... javaw использует около 23 МБ. Во время чтения и записи excel... память расходуется до 170 МБ. После очистки списка массивов.... Память не очищается и остается около 150 МБ.

Это не описывает утечку памяти — диспетчер задач показывает вам память, зарезервированную процессом, а не приложением куча.

Ваша JVM будет выделять кучу до настроенного максимума, скажем, 200 МБ. Как правило, после того, как эта память выделяется из ОС, JVM не возвращает ее (очень часто). Однако, если вы посмотрите на использование кучи (с помощью такого инструмента, как JConsole или JVisual VM), вы увидите, что куча восстанавливается после GC.

Как Java использует память

В качестве очень простого примера:

Память виртуальной машины JVisual

Источник изображения: https://stopcoding.files.wordpress.com/2010/04/visualvm_hfcd4.png

В этом примере у JVM максимальная куча составляет 1 ГиБ, и, поскольку приложению требовалось больше памяти, 400 МБ было зарезервировано для ОС (оранжевая область).

Синяя область — это фактическая память кучи, используемая приложением. Эффект пилы возникает в результате того, что процесс сборки мусора высвобождает неиспользуемую память. Обратите внимание, что оранжевая область остается довольно статической - обычно ее размер не изменяется с каждым событием GC...

в течение нескольких секунд ... он снимает до 800 МБ и остается там до конца .... У меня нет ошибок памяти.

Если бы у вас была утечка памяти, вы бы в конечном итоге получили ошибку нехватки памяти. «Утечка» (по крайней мере, в Java) — это когда приложение связывает память в куче, но не освобождает ее для повторного использования приложением. Если ваша наблюдаемая память быстро увеличивается, но приложение не падает, вы, вероятно, увидите, что внутренняя (в JVM) память фактически освобождается и используется повторно.

Ограничение того, сколько памяти (ОС) может использовать Java

Если вы хотите ограничить память, которую ваше приложение может зарезервировать из ОС, вам необходимо настроить максимальный размер кучи (с помощью параметра -Xmx), а также размер вашего постоянного поколения (если вы все еще используете Java 7 или более раннюю версию). Обратите внимание, что JVM сама использует некоторую память, поэтому значение, отображаемое на уровне ОС (с использованием таких инструментов, как диспетчер задач), может быть выше, чем указанная вами сумма памяти приложения.

person Michael    schedule 18.01.2016

В новой версии poi они использовали потоки Java для решения проблемы с памятью.Посмотрите

person Bhokal    schedule 18.01.2016
comment
Пожалуйста, не давайте ссылки на ответы. По крайней мере, вы должны описать части, которые непосредственно способствуют решению актуальной проблемы. Ссылки могут измениться в будущем, что сделает невозможным восстановление этого ответа! - person jkalden; 18.01.2016
comment
@Bhokal В статье, на которую вы ссылаетесь, нет упоминания об утечке памяти. Это намекает на то, что Apache POI не очень эффективно использует память (использует много памяти), но это не то же самое, что утечка? - person Michael; 18.01.2016