JVM OOM: GroupDocs Java Viewer static field accumulates ~13 GB (no application references)

Hi all,

we have a java web app rendering document previews as PDF using com.groupdocs:groupdocs-viewer:24.12 as follows

 FileType fileType = FileType.fromExtension(extension);
    LoadOptions loadOptions = new LoadOptions(fileType);
    loadOptions.setSkipExternalResources(true);

    try (Viewer viewer = new Viewer(inputStream, loadOptions);
         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {

        PdfViewOptions pdfViewOptions = new PdfViewOptions(() -> byteArrayOutputStream);
        pdfViewOptions.setDefaultFontName(DEFAULT_FONT_NAME);   // "Noto Serif CJK SC"

        viewer.view(pdfViewOptions);

        return byteArrayOutputStream.toByteArray();
    }

After some time the JVMs OOM, the heapdump reveals

AppClassLoader
 └─ class com.groupdocs.viewer.internal.c.a.pd.internal.l7v.lk
    └─ lI  (static field)
       └─ …ms.System.Collections.Generic.lf
          └─ Object[]   → 13.2 GB

which is a static field rooted in the App ClassLoader never unloaded. The memory is reachable only through the static field, no app references, so it cannot be freed releasing app state.

Please advise if 13.2 GB main memory are a library requirement to render documents, as it feels slightly inappropriate.

Please advise if this is a known issue already fixed in versions > 24.12, which might be outdated.

If this is an internal cache, it should rather use weak/soft ref or bounded eviction so the JVM can reclaim memory instead of OOMing.

Please advise if there are options and ways to configure and limit the heap used by the library to a reasonable limit, e.g. 1-4 GB?

Further details available on request.

Best Regards!

we also see the following in the thread stack trace, no active rendering

"weak-ref-list-remover" #16393 [175938] daemon prio=5 os_prio=0 cpu=0.15ms elapsed=951917.63s tid=0x00007f19259b9680 nid=175938 waiting on condition  [0x00007f19169b7000]
   java.lang.Thread.State: WAITING (parking)
        at jdk.internal.misc.Unsafe.park(java.base@21.0.11/Native Method)
        - parking to wait for  <0x0000000287b36f30> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(java.base@21.0.11/LockSupport.java:371)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.11/AbstractQueuedSynchronizer.java:519)
        at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.11/ForkJoinPool.java:3780)
        at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.11/ForkJoinPool.java:3725)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.11/AbstractQueuedSynchronizer.java:1746)
        at java.lang.ref.ReferenceQueue.await(java.base@21.0.11/ReferenceQueue.java:67)
        at java.lang.ref.ReferenceQueue.remove0(java.base@21.0.11/ReferenceQueue.java:158)
        at java.lang.ref.ReferenceQueue.remove(java.base@21.0.11/ReferenceQueue.java:234)
        at com.groupdocs.viewer.internal.c.a.i.internal.lo.q$a.run(Unknown Source)
        at java.lang.Thread.runWith(java.base@21.0.11/Thread.java:1596)
        at java.lang.Thread.run(java.base@21.0.11/Thread.java:1583)

   Locked ownable synchronizers:
        - None
"DisposerCleaner" #16394 [175939] daemon prio=5 os_prio=0 cpu=0.14ms elapsed=951917.53s tid=0x00007f191a3af7e0 nid=175939 waiting on condition  [0x00007f191619f000]
   java.lang.Thread.State: WAITING (parking)
        at jdk.internal.misc.Unsafe.park(java.base@21.0.11/Native Method)
        - parking to wait for  <0x00000002736e32e0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(java.base@21.0.11/LockSupport.java:371)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.11/AbstractQueuedSynchronizer.java:519)
        at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.11/ForkJoinPool.java:3780)
        at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.11/ForkJoinPool.java:3725)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.11/AbstractQueuedSynchronizer.java:1746)
        at java.lang.ref.ReferenceQueue.await(java.base@21.0.11/ReferenceQueue.java:67)
        at java.lang.ref.ReferenceQueue.remove0(java.base@21.0.11/ReferenceQueue.java:158)
        at java.lang.ref.ReferenceQueue.remove(java.base@21.0.11/ReferenceQueue.java:234)
        at com.groupdocs.viewer.internal.c.a.i.internal.qF.a.run(Unknown Source)
        at java.lang.Thread.runWith(java.base@21.0.11/Thread.java:1596)
        at java.lang.Thread.run(java.base@21.0.11/Thread.java:1583)

   Locked ownable synchronizers:
        - None

Hello @alskdgbf ,

Thank you for the detailed report and for providing the heap dump and thread information.

Based on the GC root shown in the dump, this does not appear to be related to an optional document caching API in GroupDocs.Viewer. The referenced path (com.groupdocs.viewer.internal.c.a.pd.internal.l7v.lk ) points to an internal static structure used by the engine and associated with our internal third-party PDF processing library.

Using more than 13 GB of memory is not expected for typical PDF preview rendering. Memory usage of this magnitude generally suggests that some data is accumulating over time under load, such as fonts, internal caches, or results of repeated resource-intensive conversions, rather than representing the library’s normal baseline memory consumption.

GroupDocs.Viewer does not provide a public API to impose a hard heap-memory limit on the library, such as restricting it to 1–4 GB. The JVM -Xmx option applies to the entire Java process and cannot be used to set a separate heap limit specifically for GroupDocs.Viewer.

Regarding version 24.12, this version is already quite old. We recommend upgrading to the latest GroupDocs.Viewer for Java version, 26.9, and running the same workload again. A number of memory-management and stability improvements have been introduced in subsequent releases. At the same time, we cannot determine from the provided heap dump alone whether the specific issue related to this static root has already been fixed. This needs to be verified against the latest version.

Please upgrade to GroupDocs.Viewer for Java 26.9 and repeat the same test scenario, especially with setDefaultFontName("Noto Serif CJK SC") and a representative number and variety of documents.

If you still experience OOM (Out of Memory) errors or uncontrolled memory growth with the latest version, please provide the following details:

  • JDK version
  • Operating system
  • Approximate document processing rate (documents per hour)
  • Whether using the CJK default font is required
  • A minimal reproducible project
  • A new heap dump showing the same static root

We will be happy to investigate the issue further once we can confirm that it is reproducible with the latest version, or if you can provide a sample that reproduces the problem on the latest build.

Best regards,
Evgen Efimov