2011-09-26 41 views
3

我編寫了一個用於管理和運行Jasper報告的Web應用程序。最近,我一直在處理一些報告,這些報告會生成非常大的(1500+頁)輸出,並試圖解決由此產生的內存問題。我發現了JRFileVirtualizer,這讓我能夠以非常有限的內存佔用情況成功運行報告。但是,我的應用程序的一個特點是它存儲以前運行的報告的輸出文件,並允許將它們導出爲各種格式(PDF,CSV等)。因此,我發現自己處於擁有500 + MB .jrprint文件並希望將其導出爲例如CSV的需求的情況。下面是一些簡單的例子代碼:Jasper在導出時報告OutOfMemoryError

JRCsvExporter exporter = new JRCsvExporter(); 
exporter.setParameter(JRExporterParameter.INPUT_FILE_NAME, jrprintPath); 
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, outputStream); 
exporter.exportReport(); 

不幸的是,當我嘗試這對我提到的大文件,我得到一個OutOfMemoryError

Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded 
    at java.io.ObjectInputStream$HandleTable.grow(ObjectInputStream.java:3421) 
    at java.io.ObjectInputStream$HandleTable.assign(ObjectInputStream.java:3227) 
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1744) 
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329) 
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351) 
    at java.util.ArrayList.readObject(ArrayList.java:593) 
    at sun.reflect.GeneratedMethodAccessor184.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974) 
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1849) 
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753) 
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329) 
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351) 
    at net.sf.jasperreports.engine.base.JRVirtualPrintPage.readObject(JRVirtualPrintPage.java:423) 
    ... 

從瀏覽一些碧玉內部的,它看起來像無論我如何嘗試設置此導出(我也嘗試直接加載並設置JASPER_PRINT參數),最終都會調用JRLoader.loadObject(...),這會嘗試將我的整個500MB報告加載到內存中(請參閱net.sf.jasperreports.engine.JRAbstractExporter.setInput())。

我的問題是,有沒有辦法解決這個問題? 500MB是可行的,但它並沒有離開我的應用程序非常面向未來的,並報告執行JRVirtualizer溶液離開我希望有將用於出口類似的東西。我願意讓自己的手變髒,並擴展一些Jasper內部類,但理想的解決方案將由Jasper自己提供,理由很明顯。

+0

1500+頁的報告,這是如此之大,它是無用的。也許考慮打破這些報告。 –

+0

不幸的是,這是一個使用通用工具出於許多不同目的的情況。此報告(CSV格式)通過腳本導入到另一個數據庫中,而不是由人類瀏覽。 – Eric

+0

我已經用JasperSoft提交了一個功能請求,因爲它看起來像項目預算約束會阻止我尋求像下面這樣建議的更復雜的解決方案。 http://jasperforge.org/projects/jasperreports/tracker/view.php?id=5478 – Eric

回答

4

自發布此問題以來,我也提交了JasperSoft feature request。作爲後續行動,我指出了JRVirtualizationHelper.setThreadVirtualizer方法。此方法允許您設置與當前線程關聯的JRVirtualizer,這將在JasperPrint反序列化過程中使用。

我已經在我的項目中測試過了,效果令人滿意。看起來我希望存在的功能確實存在,儘管它在API中的可見性可能有所改進。

代碼示例:

JRVirtualizer virtualizer = new JRSwapFileVirtualizer(1000, new JRSwapFile(reportFilePath, 2048, 1024), true); 
JRVirtualizationHelper.setThreadVirtualizer(virtualizer); 
+0

你需要調用clearThreadVirtualizer(),還是自動清理某處? –

2

我認爲你的問題是.jrprint是一個序列化的Java對象,你必須完全反序列化。您需要以某種方式將其分解爲小文件,然後在輸出時連接輸出。

我的建議是參與了一點,但我認爲它可能工作,至少在某些情況下:

  1. 使用JRVirtualizer填寫您的報告。使用返回JasperPrint實例的方法,以避免將所有內容轉儲爲巨大的.jrprint。
  2. 使用JRXmlExporter執行內部導出。訣竅是使用合適的JRExportParameter s告訴Jasper分別導出每個頁面(您可以使用ZipOutputStream作爲容器以避免包含大量文件的目錄)。
  3. 當您想要進行真正的導出時,請使用JASPER_PRINT_LIST。重要的是,該列表實現懶惰並創建一個使用JRPrintXmlLoaderJasperPrint實例之一,所以你並不需要一次加載整個事情。

無論如何,你應該檢查Jasper源代碼來檢查這種方法是否可行。

+0

感謝您的建議。我認爲你在反序列化的過程中擊中了頭部。我想從Jasper看到的是在反序列化/導出期間提供JRVirtualizer的選項,以便自動執行此操作。我會沿着這條線做一些實驗,看看我能想出什麼。 – Eric

+0

@Eric在反序列化過程中,'JRVirtualizer'對Jasper來說是很多工作:基本上它意味着使用'Externalizable'或自定義機制來序列化'.jrprint',而不是基於自動的Java'Serializable'。雖然虛擬化反序列化看起來是正確的選擇,但我懷疑他們會更容易爲我的方法的一些變體提供內置支持,提供一頁一頁的自動導出到一個zip和一個懶惰列表實現。 – gpeche