JasperReports is an open-source Java reporting tool that enables users to create, design, and generate reports in various formats, including PDF, HTML, Excel, CSV, and XML. It is widely used for generating dynamic content from multiple data sources, such as alternative Crystal Reports databases, XML files, and custom data sources.
Problem
In this scenario, the developer generates monthly or yearly PDF reports. A report contains a lot of data that consumes a lot of memory.
In the other scenario, an application handles many clients, generating real-time reports simultaneously that consume logical memory.
A standard error that developers get from generating large PDFs.
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Download JasperSoft Studio
The developer uses JasperSoft Studio to create PDFs with Java.

Example: generate a PDF report with JasperReports.
1. In pom.xml, add a dependency.
<!-- https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports --> <dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports</artifactId> <version>7.0.0</version> </dependency> <!-- https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports-fonts --> <dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports-fonts</artifactId> <version>7.0.0</version> </dependency> <!-- https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports-pdf --> <dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports-pdf</artifactId> <version>7.0.0</version> </dependency>
2. Create a JRXML file with Jaspersoft Studio.
The developer can download Jaspersoft Studio.

3. Create JRXML to generate a monthly PDF report on user activity data.

4. Create parameters and fields.

5. The developer can add more elements to learn and practice JasperReports.

6. In the design tab, the developer can design a template for a report.
7. In the Source tab, the developer can view the source code of a report.
8. In the preview tab, the developer can preview a report.

9. Create a UserActivity bean for the report detail band.
import lombok.Getter; import lombok.Setter; @Getter @Setter public class UserActivity { private String userId; private String activityType; private String activityTime; private String ipAddress; private String deviceType; private String location; private String browserType; private String operatingSystem; }
10. Create the GeneratePDFJasperReport class to generate a PDF report.
package com.example.demo.sandbox; import com.example.demo.bean.UserActivity; import net.sf.jasperreports.engine.JasperReport; import net.sf.jasperreports.engine.JasperCompileManager; import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperExportManager; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class GeneratePDFJasperReport { public static void main(String[] args) { generateReport(); } public static void generateReport(){ try { // Compile the Jasper report from .jrxml to .jasper String sourceFileName = "C:/JaspersoftWorkspace/MyReports/demo_large.jrxml"; JasperReport jasperReport = JasperCompileManager.compileReport(sourceFileName); // Generate sample data List<UserActivity> activities = generateUserActivities(); // Create a JRDataSource from the list JRDataSource jrDataSource = new JRBeanCollectionDataSource(activities); // Parameters for the report Map<String, Object> parameters = new HashMap<>(); parameters.put("P_MONTH","JULY"); parameters.put("P_YEAR","2024"); // Fill the report JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, jrDataSource); // Export the report to a PDF file String outputFileName = "D:/pdf/report.pdf"; JasperExportManager.exportReportToPdfFile(jasperPrint, outputFileName); System.out.println("Report generated successfully!"); } catch (JRException e) { e.printStackTrace(); } } public static List<UserActivity> generateUserActivities() { List<UserActivity> activities = new ArrayList<>(); for (int i = 1; i <= 10; i++) { UserActivity activity = new UserActivity(); activity.setUserId(String.valueOf(i)); activity.setActivityType("activity"); activity.setActivityTime("2024-07-07 10:00"); activity.setIpAddress("192.168.0.1"); activity.setDeviceType("PC"); activity.setLocation("US"); activity.setBrowserType("CHROME"); activity.setOperatingSystem("WINS"); activities.add(activity); } return activities; } }
Test generate PDF with Intelij.
1. Execute GeneratePDFJasperReport main to generate PDF.
Report generated successfully!

2. In this example, JasperReports uses only logical memory.
3. When the data in user activity has hundreds of thousands or millions of records, it causes the “OutOfMemoryError”.
Monitor the memory usage of Java applications.
To monitor logical memory, the developer can use IntelliJ IDEA Ultimate for profiling.

The other option is free, VisualVM, a visual tool that integrates command-line JDK tools and lightweight profiling capabilities.
Generate a large PDF.
1. To simulate the ” OutOfMemoryError ” case by reducing memory,
Add VM options -Xmx256m -Xms128m and increase user activity data to 200K records.

public static List<UserActivity> generateUserActivities() { List<UserActivity> activities = new ArrayList<>(); for (int i = 1; i <= 200000; i++) { UserActivity activity = new UserActivity(); activity.setUserId(String.valueOf(i)); activity.setActivityType("activity"); activity.setActivityTime("2024-07-07 10:00:00"); activity.setIpAddress("192.168.0.1"); activity.setDeviceType("PC"); activity.setLocation("US"); activity.setBrowserType("CHROME"); activity.setOperatingSystem("WINS"); activities.add(activity); } return activities; }
2. Open VisualVM to monitor memory usage.
3. When executing the “main” method. It causes Java heap space errors.
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space Exception in thread "RMI TCP Connection(idle)" java.lang.OutOfMemoryError: Java heap space
4. VisualVM shows memory usage.

5. Memory usage peaks when generating PDF and causes Java heap space errors.
Virtualizer
JasperReports provides a feature to solve this problem. Virtualizer controls memory usage by using physical memory to help generate large PDFs.
JRSwapFileVirtualizer
manages memory by offloading data from memory to disk.
JRFileVirtualizer
Writes data to multiple temporary files rather than a single swap file.
JRGzipVirtualizer
This virtualizer compresses data before writing it to disk, saving disk space at the cost of additional CPU usage for compression and decompression.
Generate a large PDF with Virtualizer.
JRSwapFileVirtualizer
public static void generateReportVirtualizer(){ try { // Compile the Jasper report from .jrxml to .jasper String sourceFileName = "C:/JaspersoftWorkspace/MyReports/demo_large.jrxml"; JasperReport jasperReport = JasperCompileManager.compileReport(sourceFileName); // Generate sample data List<UserActivity> activities = generateUserActivities(); // Create a JRSwapFileVirtualizer JRSwapFile jrSwapFile = new JRSwapFile("C:/JaspersoftWorkspace/MyReports/",1024,1); JRSwapFileVirtualizer virtualizer = new JRSwapFileVirtualizer(20, jrSwapFile, true); // Create a JRDataSource from the list JRDataSource jrDataSource = new JRBeanCollectionDataSource(activities); // Parameters for the report Map<String, Object> parameters = new HashMap<>(); parameters.put("P_MONTH","JUNE"); parameters.put("P_YEAR","2024"); parameters.put(JRParameter.REPORT_VIRTUALIZER, virtualizer); // Fill the report JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, jrDataSource); // Export the report to a PDF file String outputFileName = "D:/pdf/report.pdf"; JasperExportManager.exportReportToPdfFile(jasperPrint, outputFileName); System.out.println("Report generated successfully!"); } catch (JRException e) { e.printStackTrace(); } }
1. Run the “main” method and use VisualVM to monitor memory usage.

Report generated successfully!


2. The report was generated successfully without a memory error.
On the VisualVM, memory usage does not reach peak memory usage.
3. The developer should manage the disk space because JRSwapFileVirtualizer uses physical memory. After generating a report, the developer must delete the temporary file created by JRSwapFileVirtualizer.

JRGzipVirtualizer
public static void generateReportVirtualizer(){ try { // Compile the Jasper report from .jrxml to .jasper String sourceFileName = "C:/JaspersoftWorkspace/MyReports/demo_large.jrxml"; JasperReport jasperReport = JasperCompileManager.compileReport(sourceFileName); // Generate sample data List<UserActivity> activities = generateUserActivities(); // Create a JRGzipVirtualizer JRGzipVirtualizer virtualizer = new JRGzipVirtualizer(50); // Create a JRDataSource from the list JRDataSource jrDataSource = new JRBeanCollectionDataSource(activities); // Parameters for the report Map<String, Object> parameters = new HashMap<>(); parameters.put("P_MONTH","JUNE"); parameters.put("P_YEAR","2024"); parameters.put(JRParameter.REPORT_VIRTUALIZER, virtualizer); // Fill the report JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, jrDataSource); // Export the report to a PDF file String outputFileName = "D:/pdf/report.pdf"; JasperExportManager.exportReportToPdfFile(jasperPrint, outputFileName); System.out.println("Report generated successfully!"); } catch (JRException e) { e.printStackTrace(); } }
1. Run the “main” method and use VisualVM to monitor memory usage.

2. The report was generated successfully without a memory error.
On VisualVM, memory usage does not reach peak. However, JRSwapFileVirtualizer has a different memory usage pattern.
JRFileVirtualizer
public static void generateReportVirtualizer(){ try { // Compile the Jasper report from .jrxml to .jasper String sourceFileName = "C:/JaspersoftWorkspace/MyReports/demo_large.jrxml"; JasperReport jasperReport = JasperCompileManager.compileReport(sourceFileName); // Generate sample data List<UserActivity> activities = generateUserActivities(); // Create a JRFileVirtualizer int maxSize = 100; // Max number of pages in memory String tempDir = "C:/JaspersoftWorkspace/MyReports/"; // Temp directory for virtualizer files JRFileVirtualizer virtualizer = new JRFileVirtualizer(maxSize, tempDir); // Create a JRDataSource from the list JRDataSource jrDataSource = new JRBeanCollectionDataSource(activities); // Parameters for the report Map<String, Object> parameters = new HashMap<>(); parameters.put("P_MONTH","JUNE"); parameters.put("P_YEAR","2024"); parameters.put(JRParameter.REPORT_VIRTUALIZER, virtualizer); // Fill the report JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, jrDataSource); // Export the report to a PDF file String outputFileName = "D:/pdf/report.pdf"; JasperExportManager.exportReportToPdfFile(jasperPrint, outputFileName); System.out.println("Report generated successfully!"); } catch (JRException e) { e.printStackTrace(); } }
1. Run the “main” method and use VisualVM to monitor memory usage.

2. The report was generated successfully without a memory error.

3. A temporary file is created when generating a PDF with JRFileVirtualizer. When the PDF is finished being generated, the temporary files are deleted.
Precompilation
Jaspersoft Studio provides the feature to compile “.jrxml” to “.jasper”.
Compiling reports can detect some errors. The developer can compile a report to check for mistakes before implementing JasperReports in the Java application.

The developer can use “.jasper” instead of “.jrxml” to save resources.
String jasperFile = "C:/JaspersoftWorkspace/MyReports/demo_large.jasper"; JasperReport jasperReport = (JasperReport) JRLoader.loadObjectFromFile(jasperFile);
Finally
Each Virtualizer has pros and cons. The developer must decide which virtualizer to use when generating PDFs, based on the condition of each individual report.