Skip to content

Cache NetworkClassLoader in server scope to avoid redundant reloads#30

Merged
bdw429s merged 1 commit into
coldbox-modules:developmentfrom
sjdaniels:development
Jun 16, 2026
Merged

Cache NetworkClassLoader in server scope to avoid redundant reloads#30
bdw429s merged 1 commit into
coldbox-modules:developmentfrom
sjdaniels:development

Conversation

@sjdaniels

Copy link
Copy Markdown
Contributor

Uses a versioned server-scope key (matching URLClassLoader pattern) and a double-checked lock to reuse an existing NetworkClassLoader instance across requests, preventing repeated class path scanning on every JavaLoader init, and NetworkClassLoader instances accumulating in JVM Metaspace.

Description

Problem

On ColdFusion/Lucee applications using ColdBox, every application reinit causes a new NetworkClassLoader instance to be created and orphaned in JVM memory, never to be garbage collected.

Root cause: Loader.cfc's setup() method correctly checks server scope to avoid recreating the JavaLoader instance on reinit. However, when the JavaLoader already exists in server scope, the else branch calls init() on it to reconfigure it:

} else {
    // reconfigure it, maybe settings changed
    getJavaLoaderFromScope().init( argumentCollection = moduleSettings );
}

JavaLoader.init() unconditionally calls loadClasses(), which unconditionally creates a brand new NetworkClassLoader instance on every invocation with no check for an existing one:

networkClassLoaderProxy = createJavaProxy( networkClassLoaderClass );
classLoader = networkClassLoaderProxy.init();
// ...
setURLClassLoader( classLoader );

While ensureNetworkClassLoaderOnServerScope() correctly caches the underlying URLClassLoader in server scope and reuses it across reinits, loadClasses() wraps it in a new NetworkClassLoader every time. The old NetworkClassLoader instances are never closed and cannot be garbage collected because their threads are GC roots, causing them to accumulate in JVM Metaspace indefinitely.

Impact

On a busy application with frequent deploys (which trigger reinits), this causes:

  • One new NetworkClassLoader instance leaked per reinit
  • Each leaked instance holds its own thread set and class metadata in Metaspace
  • Metaspace grows continuously until the JVM crashes with heap exhaustion
  • Typical crash cycle of 5-7 days on applications deploying several times per day

This can be observed by running:

jcmd <PID> VM.classloaders 2>&1 | grep -c "NetworkClassLoader"

A healthy instance should show 1. A leaking instance will show a count matching the number of reinits since the last JVM restart.

Fix

Cache the NetworkClassLoader in server scope using the same double-checked lock pattern already used by ensureNetworkClassLoaderOnServerScope() for the URLClassLoader. On subsequent reinits, loadClasses() detects the cached instance, sets it as the active classloader, and returns early without creating a new one.

The cache key uses the same uuid + version base as the existing server scope key with a .networkclassloader suffix to avoid collision with the URLClassLoader entry.
Result

After this fix, NetworkClassLoader count remains stable at 1 across any number of application reinits for the lifetime of the JVM, and Metaspace growth from classloader accumulation is eliminated entirely.

Issues

Sorry - I do not see in Forgebox where the bug tracker for this module is located.

Type of change

Please delete options that are not relevant.

  • Bug Fix
  • Improvement

Uses a versioned server-scope key (matching URLClassLoader pattern) and a double-checked lock to reuse an existing NetworkClassLoader instance across requests, preventing repeated class path scanning on every JavaLoader init, and NetworkClassLoader instances accumulating in JVM Metaspace.
@bdw429s bdw429s merged commit c961aef into coldbox-modules:development Jun 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants