Diagnosing Memory Leaks in AnyLogic Simulations: A Long-Running Simulation Case Study

AnyLogic Simulation Performance Degradation Over Time

       The issue appeared only during a long-running AnyLogic simulation. While short runs performed normally, the simulation gradually slowed down as the simulation horizon increased. While short runs performed normally, the simulation gradually slowed down as the simulation horizon increased. Each subsequent month took longer to calculate than the previous one, indicating that some resource was accumulating over time. The model continued to produce correct results, but its computational performance progressively degraded. This behavior pointed to a potential memory management issue rather than a problem with the simulation logic itself.
AnyLogic simulation slowdowns can result from inefficient algorithms—for example, algorithms executed too frequently or with excessive computational complexity. These CPU bottlenecks can often be identified through sampling or profiling. However, gradual performance degradation may also indicate a memory leak or object retention, where retained objects increase memory usage and garbage collection overhead over time.
       The Anylogic simulation model itself is relatively large, representing an entire refinery from feedstock sources to final product dispatch at a detailed, third-level representation of the refinery. It contains more than one hundred modeled Petroleum Refining Library objects, including sources, process units, tank farms, mixing nodes, and loading racks. In this case, the memory leak was caused by unnecessary object retention.

Diagnosing AnyLogic Memory Leaks with VisualVM

       To diagnose the AnyLogic memory leak, we used VisualVM to monitor Java heap usage and analyze object growth during the simulation. Heap snapshots were captured at different stages of the simulation to identify Java objects whose instance count continued to grow. This quickly revealed abnormal growth in the number of instances of a specific Java class associated with generated railcars (class Wagon). The analysis showed that the number of live Wagon objects increased continuously as the simulation progressed, providing a clear indication of an object retention problem.

Root Cause: Java Object Retention and Memory Leak

       The issue occurred in the Loading Rack component of the AnyLogic-based Petroleum Refining Library (PRL), which models the loading of finished petroleum products into railcars at refinery tank farms. This allows the model to account for constraints on finished-product dispatch and pumping capacity, including limitations on how quickly products can be transferred from storage to rail transport.
       Loading Rack uses a database-defined train schedule. When the original schedule is exhausted, the scheduling algorithm automatically extends it by repeating the existing arrival pattern. This allows the model to simulate long operational periods without requiring an excessively large input schedule. The problem became visible only when this schedule extension was used for long-running simulations, where new railcar objects continued to be generated over time. The investigation revealed that every newly generated railcar was being registered in an internal object registry. These references remained even after the railcars were no longer needed by the simulation, causing Java object retention and preventing garbage collection. As the schedule was extended, the registry continued to grow, keeping tens of thousands of obsolete objects reachable as the simulation progressed. The resulting memory growth gradually increased the computational overhead of the simulation, causing each subsequent month to take longer to calculate.

Fixing the AnyLogic Memory Leak: Opt-In Object Registration

       The registration mechanism was changed from automatic registration to opt-in registration. PrlObject instances are no longer added to the global holder by default; only components that require global registration explicitly enable it. Instead of registering every PrlObject by default, registration is now controlled by an explicit condition:
if (registerInPrlObjectsHolder()) PrlObjectsHolder.add(this);
       The new registerInPrlObjectsHolder() method returns false by default. Components that require global registration can explicitly override it and return true.For Wagon, the default behavior remains false. Therefore, railcars are not retained by the global registry and can be garbage-collected once they leave the Loading Rack and are removed from the simulation. This small architectural change eliminated unnecessary object retention while preserving global registration where it is actually required.

Results

       After fixing the AnyLogic memory leak, memory consumption remained stable throughout the long-running simulation, and the progressive slowdown disappeared. Simulation time stabilized at approximately 6.5 minutes per month instead of continuing to increase.

Before: 6 → 12 → 20 min/month → increasing After: ~6.5 min/month → stable

The Loading Rack continues to support automatic train schedule extension without changes to its operational behavior. This makes the component more suitable for long-running refinery digital twin simulations where large numbers of railcar objects may be created over extended simulation horizons. The fix will be included in the upcoming Petroleum Refining Library (PRL) 2.2.3 release.

AnyLogic Simulation Performance and Memory Management

       This case highlights an important aspect of AnyLogic simulation performance: creating objects is not necessarily a problem, but retaining objects that are no longer needed can become critical in long-running simulations. For models that dynamically generate large numbers of agents or other Java objects, it is important to monitor object lifecycles and periodically analyze heap usage. Tools such as VisualVM and heap snapshots can help identify unexpected object growth and retained references.
See also: A detailed guide to diagnosing memory leaks is available on The AnyLogic Modeler.

FAQ

1. Why does an AnyLogic simulation slow down over time?
The simulation gradually accumulated objects that were no longer required. As the number of retained objects increased, memory usage grew and each subsequent simulation period took longer to calculate.

2. Why does a memory leak affect long-running AnyLogic simulations?
Short simulations did not generate enough objects to expose the memory retention problem. The issue became significant only after the model had been running for an extended period with automatic schedule extension.

3. How can you identify a memory leak in AnyLogic?
VisualVM was used to monitor Java heap usage and compare heap snapshots captured at different stages of the simulation. The analysis revealed continuous growth in the number of instances of a specific Java class.

4. What causes memory leaks in AnyLogic simulations?
Newly generated Wagon objects were retained in an internal object registry even after they were no longer needed. These references prevented Java Garbage Collection from reclaiming the associated memory.

5. How was the AnyLogic memory leak fixed?
The object registration mechanism was changed so that PrlObject instances are no longer registered in the global PrlObjectsHolder by default. Registration is now enabled only for components that explicitly require it. As a result, Wagon objects are no longer retained by the global registry and can be garbage-collected after they leave the Loading Rack.

6. How can you distinguish an algorithmic performance problem from a memory leak?
Algorithmic bottlenecks are typically investigated with CPU sampling or profiling, while memory leaks and object retention are investigated by monitoring heap usage, comparing heap snapshots, and identifying objects whose instance count continues to grow.

7. Can the same problem occur in other AnyLogic models?
Yes. AnyLogic models that dynamically create large numbers of agents or Java objects can experience similar issues if obsolete objects remain referenced by collections, registries, or other long-lived objects. Monitoring object growth is particularly important for long-running simulations.