2

我正在使用 jasper 报告生成一个巨大的 Excel 报告,该报告可能会扩展到 100k+ 行。

我首先得到我的报告行,然后将它们提供给 jasperReport 对象。这样做没有问题。

fillReport 方法需要一些时间,但似乎也做得很好。

但是在导出我的报告时,我的内存使用量很大,最终我的 GC 超载,我的应用程序崩溃了。作为记录,这是我得到的例外:

java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.util.Arrays.copyOf(Unknown Source)
    at java.io.ByteArrayOutputStream.grow(Unknown Source)
    at java.io.ByteArrayOutputStream.ensureCapacity(Unknown Source)
    at java.io.ByteArrayOutputStream.write(Unknown Source)
    at java.io.ObjectOutputStream$BlockDataOutputStream.write(Unknown Source)
    at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
    at java.io.ObjectOutputStream.defaultWriteObject(Unknown Source)
    at net.sf.jasperreports.engine.fill.JRTemplatePrintElement.writeObject(JRTemplatePrintElement.java:363)
    at sun.reflect.GeneratedMethodAccessor359.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at java.util.ArrayList.writeObject(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor154.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at net.sf.jasperreports.engine.fill.JRAbstractLRUVirtualizer.writeData(JRAbstractLRUVirtualizer.java:704)
    at net.sf.jasperreports.engine.fill.JRSwapFileVirtualizer.pageOut(JRSwapFileVirtualizer.java:87)
    at net.sf.jasperreports.engine.fill.JRAbstractLRUVirtualizer.virtualizeData(JRAbstractLRUVirtualizer.java:664)

我想知道是否有办法优化这种内存使用并防止这种崩溃,特别是当我需要能够让我的用户同时生成这些报告时。我曾尝试使用交换文件和虚拟器,但这似乎只是在优化填充报告过程。

这是我调用报告生成函数的代码:

 List<LineaInformeFacturacionExcelCVO> listaLineasInformeFacturacionExcelCVO = new ArrayList<LineaInformeFacturacionExcelCVO>();
        List<LineaInformeFacturacionExcel> lineasInformeFacturacionExcel = lineaInformeFacturacionExcelDao.getLineasInformeFacturacionExcel(filtros);

getBytesInformeExcel(reportRealPath, parameters, listaLineasInformeFacturacionExcelCVO, true, jrExporterParameters);

这是函数本身:

    public byte[] getBytesInformeExcel(String reportRealPath, Map<String, Object> parameters, List<?> list, boolean virtualize, Map<JRExporterParameter, Object> jrExporterParameters) throws EmptyReportException, JRException {


    if (list != null && list.isEmpty()) {
                throw new EmptyReportException();
            }

              // Acordarse de pasarlo entre subrepors
            if (parameters == null)
                parameters = new HashMap<String, Object>();

            Locale spainLocale = new Locale("es","ES");     
            TimeZone europeMadridTimezone = TimeZone.getTimeZone("Europe/Madrid");                  
            parameters.put(JRParameter.REPORT_LOCALE, spainLocale);
            parameters.put(JRParameter.REPORT_TIME_ZONE, europeMadridTimezone);     

            JRSwapFileVirtualizer virtualizer = null;
            if (virtualize) {           
                virtualizer = new JRSwapFileVirtualizer(250, new JRSwapFile(System.getProperty("java.io.tmpdir"), 250, 250));
                parameters.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);                    
            }

            parameters.put(JRParameter.IS_IGNORE_PAGINATION, false);

            JasperReport jasperReport;

            byte[] reportBytes = null;
            JasperPrint jasperPrint = null;

            try {

                jasperReport = (JasperReport) JRLoader.loadObjectFromFile(reportRealPath);          

                Crono crono = new Crono();
                crono.startCrono();
                LOG.info("Iniciando JasperFillManager.fillReport() ");

                if (list == null) {
                    Connection connection = lineaInformeFacturacionDao.obtenerConnection();          
                    jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, connection);
                }
                else {
                    jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, new JRBeanCollectionDataSource(list));
                }

                LOG.info("Fin JasperFillManager.fillReport() :" + crono.stopCrono() + " ms.");

                // Comprueba si el report está vacío.
                if (jasperPrint.getPages().isEmpty() || jasperPrint.getPages().get(0).getElements().isEmpty()) {
                       throw new EmptyReportException();
                }       

                ByteArrayOutputStream xlsReport = new ByteArrayOutputStream();

                JRExporter exporter = new JRXlsExporter();;
                String formato = parametrosConfiguracion.getPropertie("informes.excel.formato");
                if(StringUtils.isNotBlank(formato)) {
                    if(formato.equals(parametrosConfiguracion.getPropertie("excel.formato.xls"))) {
                        exporter = new JRXlsExporter();
                    } else if(formato.equals(parametrosConfiguracion.getPropertie("excel.formato.xlsx"))) {

                        exporter = new JRXlsxExporter();
                    }
                }                        

                exporter.setParameter(JRXlsExporterParameter.JASPER_PRINT, jasperPrint);
                exporter.setParameter(JRXlsExporterParameter.OUTPUT_STREAM, xlsReport);

                // Carga los parámetros por defecto
                exporter.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE);
                exporter.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE);
                exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
                exporter.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);

                // Carga estos parámetros pasados como parámetro sobreescribiendo los por defecto 
                if (jrExporterParameters != null) {
                    exporter.setParameters(jrExporterParameters);
                    exporter.setParameter(JRXlsExporterParameter.JASPER_PRINT, jasperPrint);
                    exporter.setParameter(JRXlsExporterParameter.OUTPUT_STREAM, xlsReport);
                }            

                crono = new Crono();
                crono.startCrono();
                LOG.info("Iniciando exporter.exportReport() ");  
                exporter.exportReport();
                LOG.info("Fin exporter.exportReport() :" + crono.stopCrono() + " ms.");

                reportBytes = xlsReport.toByteArray();     


            }finally{
                if (virtualize) {
                    virtualizer.cleanup();
                }
            }

            return reportBytes;
}

最后,如果这有任何帮助,这里是生成此报告时内存分析程序的屏幕截图:

在此处输入图像描述

崩溃发生在第二个 GC 使用高峰。

直接导出到文件

exporter.setParameter(JRXlsExporterParameter.OUTPUT_FILE, new File("c://archivo.xlsx"));

而不是 ByteStream 会产生以下异常:

java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.io.ByteArrayOutputStream.grow(Unknown Source)
    at java.io.ByteArrayOutputStream.ensureCapacity(Unknown Source)
    at java.io.ByteArrayOutputStream.write(Unknown Source)
    at java.io.ObjectOutputStream$BlockDataOutputStream.drain(Unknown Source)
    at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at net.sf.jasperreports.engine.fill.JRAbstractLRUVirtualizer.writeData(JRAbstractLRUVirtualizer.java:704)
    at net.sf.jasperreports.engine.fill.JRSwapFileVirtualizer.pageOut(JRSwapFileVirtualizer.java:87)
    at net.sf.jasperreports.engine.fill.JRAbstractLRUVirtualizer.virtualizeData(JRAbstractLRUVirtualizer.java:664)
    at net.sf.jasperreports.engine.fill.JRAbstractLRUVirtualizer.evict(JRAbstractLRUVirtualizer.java:485)
    at net.sf.jasperreports.engine.fill.JRAbstractLRUVirtualizer.requestData(JRAbstractLRUVirtualizer.java:630)
    at net.sf.jasperreports.engine.base.ElementsBlock.ensureData(VirtualizableElementList.java:463)
    at net.sf.jasperreports.engine.base.ElementsBlock.ensureDataAndTouch(VirtualizableElementList.java:432)
    at net.sf.jasperreports.engine.base.ElementsBlock.get(VirtualizableElementList.java:283)
    at net.sf.jasperreports.engine.base.ElementsBlockList.get(VirtualizableElementList.java:717)
    at net.sf.jasperreports.engine.base.VirtualizableElementList.get(VirtualizableElementList.java:96)
    at net.sf.jasperreports.engine.base.VirtualizableElementList.get(VirtualizableElementList.java:54)
    at net.sf.jasperreports.engine.export.JRGridLayout.createWrappers(JRGridLayout.java:922)
    at net.sf.jasperreports.engine.export.JRGridLayout.<init>(JRGridLayout.java:140)
    at net.sf.jasperreports.engine.export.JRXlsAbstractExporter.exportPage(JRXlsAbstractExporter.java:1000)
    at net.sf.jasperreports.engine.export.JRXlsAbstractExporter.exportReportToStream(JRXlsAbstractExporter.java:983)
    at net.sf.jasperreports.engine.export.JRXlsAbstractExporter.exportReport(JRXlsAbstractExporter.java:650)
    at com.services.informes.InformesService.getBytesInformeExcel(InformesService.java:920)
    at com.services.informes.InformesService.getBytesInformeFacturacionExcel(InformesService.java:277)
    at com.controllers.InformesController.exportarInformeFacturacionExcel(InformesController.java:391)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:212)

这是这种情况下的内存分析: 在此处输入图像描述

4

0 回答 0