diff --git a/CLAUDE.md b/CLAUDE.md
index 12cb355..a410410 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -30,6 +30,7 @@ java -jar target/cloudsimplus-benchmarks-8.5.1-uber.jar -l
Alternative entry points (run from the IDE or with `exec`/`java -cp`):
- `org.cloudsimplus.benchmarks.Run` runs every JMH benchmark matching `org.cloudsimplus.` with preset options (1 fork, 5 warmup, 25 measurement iterations, ms, 1 thread).
- `org.cloudsimplus.benchmarks.SimulationTimeBenchmark` is a plain `main` (not JMH): it runs a full simulation several times, growing Hosts/VMs/Cloudlets each run, and prints execution time.
+- `org.cloudsimplus.listeners.ListenersMemoryBenchmark` is a plain `main` (not JMH): it prints the heap retained by each VM and Host (run it with `-XX:+UseSerialGC`). To compare with CloudSim Plus 8.5.1 (before issue #586), build with `./mvnw clean package -Pcloudsimplus-8`, run it, then build without the profile and run it again (see its Javadoc).
There are no unit tests in this project.
diff --git a/pom.xml b/pom.xml
index fd04e18..8a4ea56 100755
--- a/pom.xml
+++ b/pom.xml
@@ -78,15 +78,10 @@
org.cloudsimpluscloudsimplus
- ${project.version}
+ ${cloudsimplus.version}
+
-
-
- ch.qos.logback
- logback-classic
- 1.4.5
-
+ ${project.version}2.50.00.14.21.0.0
@@ -255,4 +252,33 @@
+
+
+
+
+ cloudsimplus-8
+
+ 8.5.1
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org/cloudsimplus/listeners/ListenersNotificationBenchmark.java
+
+
+
+
+
+
+
diff --git a/src/main/java/org/cloudsimplus/listeners/ListenersMemoryBenchmark.java b/src/main/java/org/cloudsimplus/listeners/ListenersMemoryBenchmark.java
new file mode 100644
index 0000000..560ab48
--- /dev/null
+++ b/src/main/java/org/cloudsimplus/listeners/ListenersMemoryBenchmark.java
@@ -0,0 +1,153 @@
+/*
+ * CloudSim Plus: A modern, highly-extensible and easier-to-use Framework for
+ * Modeling and Simulation of Cloud Computing Infrastructures and Services.
+ * http://cloudsimplus.org
+ *
+ * Copyright (C) 2015-2021 Universidade da Beira Interior (UBI, Portugal) and
+ * the Instituto Federal de Educação Ciência e Tecnologia do Tocantins (IFTO, Brazil).
+ *
+ * This file is part of CloudSim Plus.
+ *
+ * CloudSim Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * CloudSim Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with CloudSim Plus. If not, see .
+ */
+package org.cloudsimplus.listeners;
+
+import org.cloudsimplus.hosts.HostSimple;
+import org.cloudsimplus.resources.PeSimple;
+import org.cloudsimplus.vms.VmSimple;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.ref.Reference;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.IntFunction;
+
+/// Measures the heap retained by each VM and Host, to compare the memory used
+/// before and after the listener lists became the shared `List.of()` (issue #586).
+/// Before that, each VM created 6 empty `ArrayList`s and each Host created
+/// 2 empty `ArrayList`s and 1 empty `HashSet`, even when no listener was added.
+///
+/// It's a plain `main` (not JMH), since JMH measures time and allocation rate,
+/// but here we need the memory that stays in the heap after the objects are created.
+/// It creates [#OBJECTS] objects, keeps them in an array and measures the used heap
+/// after a GC, before and after creating them. The difference divided by the number of objects
+/// is the heap retained by each object. This is repeated [#ROUNDS] times and the median is shown.
+///
+/// The results are only accurate with a stop-the-world GC, where [System#gc()]
+/// does a full collection, so run it with `-XX:+UseSerialGC`.
+///
+/// To compare CloudSim Plus 8.5.1 (before #586) with the current version, build and run it once for each version:
+///
+/// ```bash
+/// ./mvnw clean package -Pcloudsimplus-8 # CloudSim Plus 8.5.1
+/// java -XX:+UseSerialGC -cp target/cloudsimplus-benchmarks-*-uber.jar org.cloudsimplus.listeners.ListenersMemoryBenchmark 8.5.1
+///
+/// ./mvnw clean package # current CloudSim Plus version
+/// java -XX:+UseSerialGC -cp target/cloudsimplus-benchmarks-*-uber.jar org.cloudsimplus.listeners.ListenersMemoryBenchmark current
+/// ```
+///
+/// @author Manoel Campos da Silva Filho
+/// @see CloudSim Plus issue #586
+/// @see CloudSim Plus issue #523
+public final class ListenersMemoryBenchmark {
+ /// Number of objects of each type created in each round.
+ private static final int OBJECTS = 100_000;
+
+ /// Number of times each measurement is repeated.
+ private static final int ROUNDS = 5;
+
+ /// Number of times [System#gc()] is called before reading the used heap,
+ /// so that objects waiting for finalization or in reference queues are also collected.
+ private static final int GC_CALLS = 3;
+
+ private static final double VM_MIPS = 1000;
+ private static final long VM_PES = 1;
+
+ private static final double PE_MIPS = 1000;
+ private static final long HOST_RAM = 4096;
+ private static final long HOST_BW = 10_000;
+ private static final long HOST_STORAGE = 100_000;
+
+ private static final MemoryMXBean MEMORY = ManagementFactory.getMemoryMXBean();
+
+ /// A private constructor to avoid class instantiation.
+ private ListenersMemoryBenchmark(){}
+
+ /// Runs the benchmark.
+ /// @param args an optional label to identify the CloudSim Plus version being measured (e.g., "before" or "after")
+ public static void main(final String[] args) {
+ final String label = args.length > 0 ? args[0] : "current";
+ System.out.printf("%s (%s): %,d objects per round, median of %d rounds%n",
+ ListenersMemoryBenchmark.class.getSimpleName(), label, OBJECTS, ROUNDS);
+
+ printResult("VmSimple", measure(ListenersMemoryBenchmark::newVm));
+ printResult("HostSimple", measure(ListenersMemoryBenchmark::newHost));
+ }
+
+ private static VmSimple newVm(final int index) {
+ return new VmSimple(VM_MIPS, VM_PES);
+ }
+
+ /// Creates a Host with a single PE.
+ /// The PE is included in the measured size, but it's the same before and after the change.
+ private static HostSimple newHost(final int index) {
+ return new HostSimple(HOST_RAM, HOST_BW, HOST_STORAGE, List.of(new PeSimple(PE_MIPS)));
+ }
+
+ /// {@return the median heap (in bytes) retained by each object created by a given function}
+ /// @param factory a function that creates an object for a given index
+ private static double measure(final IntFunction