Before reading this blog, I highly recommend you read this introduction to client state first: The Art of State Part 1.

For part 2, we’ll be diving into the details of how object management in the client state works. We will deal with highly abstract concepts, so I recommend you keep a warm drink and brain food snacks nearby while reading this (for heady object management ideas, I prefer munching on chips myself).

Disclaimer: This blog post applies to Mendix 7.23.7 and 8.0. Garbage collection behavior may differ in other releases. This blog will explain about how Mendix apps communicate with the runtime so you can model better apps and troubleshoot app issues more effectively.

1. What Is Garbage Collection and Why Is It Needed?

You will create objects, commit, rollback, and delete objects while modeling your application. You can show such objects in the UI, pass them around to microflows and nanoflows, modify them, or work with them in custom widgets. Some of the objects you’re working are persistable; some of them are not. Non-persistable objects always live in memory and are thus never saved to the database. You can also never commit a new persistable object — that would make it effectively non-persistable, since it lives in the memory.

During a typical user session, a user works with an application for hours visiting dozens of pages, thus creating several objects (persistable or non-persistable). Those objects become part of the client state and take up space in the browser’s memory, which could slow down your app over time. As a Mendix developer, you do not need to remove every object you create but don’t commit in the application, and your app will still work without performance decreasing. How is this possible? What does Mendix do with a non-persistable object which a user created ten pages ago? What about a change made on a persistable object?

The garbage collection mechanism is the answer to these questions.

Garbage Day!
Time to take out the trash.

The Mendix client analyzes objects and changes in its state and removes them if it finds they are no longer necessary. The goal of this process is to minimize the state hold in memory. Figuring out if a Mendix object is required by an app is based upon a few criteria which I’ve detailed below.

2. When Does Garbage Collection Happen?

In my neighborhood, it’s generally on Tuesday mornings. For a Mendix app, garbage collection happens in the background as you use the app. It kicks in regularly, analyzes all objects and their changes in the state, and removes objects or changes that it identifies as no longer necessary.

3. Can I See Garbage Collection Happening?

You can use the state inspection shortcut (Ctrl + Alt + G) to inspect the client state and possibly see garbage collection. However, you will need a little bit of luck to see the objects which are about to be collected. Frequently, collection will occur before you can see it. Here you can see an object which will be collected, as garbage collection has not kicked in yet:

state view of an object prior gc

In just a few seconds, this object will not be visible anymore.

You can also enable a special setting to see which objects are removed when garbage collections occur. To do this, add a new data section with the logCleanupStatistics: trueproperty to the dojoConfig object configured in your application’s theme/index.html file. Your code should look like this:

dojoConfig = {
    baseUrl: "mxclientsystem/dojo/",
    cacheBust: "{{cachebust}}",
    rtlRedirect: "index-rtl.html",
    data: {
      "logCleanupStatistics": true
    } 
};

After adding this section, an information message will be added whenever garbage collection happens:

gc statistics

Disclaimer: the configuration mentioned above is not public and subject to change or removal without notice.

4. How Does Garbage Collection Work?

Now you know that unnecessary objects and changes created or loaded in the application are removed from the client state, allowing your applications to keep running quickly. But how does this work?

The conceptual answer to that question is simple: the Mendix client should know if an object is needed by any component at any given time. If not, it can remove it from the state. And since a Mendix object can refer to one or more objects through associations (or be referenced), the Mendix client needs to keep track of the dependent objects as well.

Let’s dig deeper into how these concepts actually work in the Mendix client.

5. Subscriptions

Subscriptions are key for garbage collection. The subscriptions system is a sub-system of the Mendix client, and with it, you can ask for notifications whenever changes happen to a Mendix object or its attributes. It looks like a simple API for custom widgets, but in fact, the Mendix client often uses subscriptions internally. Here are a few internal use examples:

  • Whenever a data view loads an object, it subscribes to loaded object so it can receive notifications about specific situations (for example when object is deleted).
  • Most input widgets depend on subscriptions to get updates about the objects they’re working with. This way they can update their value whenever they were changed somewhere else. Dojo widgets often subscribe to their objects manually. However pluggable widgets don’t need to do this, because their subscriptions are taken care of by the Mendix client.
  • The Mendix client subscribes to objects for conditional visibility and editability rules on the page, so it re-evaluates conditions when the relevant object or attribute changes.
  • Text widgets with parameters subscribe to their objects. Text widgets also subscribe all intermediate objects if a template value goes over associations, so the widgets know they should update whenever associations change.

Subscriptions are treated as inputs by the garbage collection mechanism to mark that a subscribed object is needed in the app and should not be collected — a component or widget still uses or needs it.

The Mendix client also subscribes to objects which aren’t shown in the UI at a given moment, even without using the notification callback. This is a way to indicate that Mendix client needs those objects. Here are a few examples of such behavior:

  • Objects representing $currentUser and $currentSession are always subscribed to within the Mendix client, as they live as long as users use the app and might be needed by the application at any time .

    Note: a $currentSession object is never sent back to the client for security reasons, but the Mendix client still subscribes to it. The reason for this will be explained below.

  • A data source microflow for a data grid might return more objects than the data grid can show on a single page. Those objects become part of the client state. The Mendix client subscribes to all of those objects so that garbage collection does not remove them from the client state accidentally.

  • When a page with a parameter is closed, garbage collection could remove the parameter object from the client state (assuming that object is no longer necessary). Such a page parameter can be a non-persistable object, which means removing it makes it forever inaccessible. But since a user can go back using the back button of the browser, parameter objects need to be kept in the state. That’s why Mendix client subscribes to the page parameter objects of the last five pages: so garbage collection does not remove them.

5.1 Can I See Why an Object Is Being Kept in the State?

Sometimes you can see why a certain object is kept in the state by using the Ctrl + Alt + G shortcut. In the example below, the Artist object is subscribed by widgets on the current page. Sometimes you can see null entries in the subscribedWidgets array. That means that they’re either subscribed from the Mendix client internally or from a custom widget:

state with subscriptions

So, it is clear that subscribed objects should be kept in the state as long as they’re subscribed.

Is that all?

6. The Importance of Reachability

As stated earlier, keeping only subscribed objects is not enough. The Mendix client needs to take associations into account. Here is a non-persistable Mendix object called Object A inside the client state:

Because it is not subscribed to, this object can be removed from the state when garbage collection happens.

Let’s add another object — a non-persistable object called Object B:

Both objects can be removed from the client state, because none of them have been subscribed to.

Now, imagine that Object A has an association, and is referring to Object B as its value:

This situation does not change anything. Both objects can be removed from the client state because none of them are subscribed to.

Here Object A is subscribed to using the mx.data.subscribe API (note the red glow around Object A):

In this situation, both objects should be kept in the state. Why?

Object A is subscribed to, and therefore should not be collected because the garbage collection mechanism does not remove subscribed objects.

Object B is not subscribed to, but that does not mean that it can be collected because Object A refers to it. Consider a microflow which accepts Object A as parameter. Inside this microflow, Object B is retrieved with a Retrieve activity using the Association Retrieve type. If garbage collection removes Object B from state, this microflow wouldn’t be able to retrieve it.

So garbage collection should also take this into account and keep Object B in the client state. It should do this because of the following:

  • Object B is “reachable” from Object A: they’re associated. A retrieve activity in a microflow or a nanoflow can reach it over Object A.
  • Object B is non-persistable, meaning it can not be loaded from the database by the retrieve activity in a microflow. If it were a persistent entity (which is committed), a microflow could retrieve it from the application database.

Let’s add a new object to the mix. What should happen with Object C when garbage collection happens?

Object C should also be kept in the state, because it can be reached through Object A -> Object B -> Object C.

It is also possible that Object A can refer to two different objects as the value of the same association. How can this happen? Imagine that Object A is committed with an association referring to Object B. When you make that same association point to Object D without committing, you introduce a “change” for this association. In this case, Object A knows both the committed and the changed value for the same association:

Garbage collection takes this case into account and keeps both Object B and Object C in the state, because both can be reachable from Object A.

All the examples above concern non-persistable objects. Let’s add persistable objects to the mix. They’re represented with the light blue background:

Let’s go through what should happen to each persistable object when garbage collection happens.

Object B should not be collected for the following reasons:

  • The (new) suffix at the end of its name indicates that it hasn’t been committed yet, effectively making the object behave like a non-persistable one. It can only be found in memory.
  • It has been referenced from Object A, marking it “reachable.” If it hadn’t been referenced, garbage collection could have removed it from the client state.

Object C should not be collected for the following reasons:

  • The ** at the end of its name indicates that it has uncommitted attribute/association changes. Such changes only live in memory until Object C is either committed or rolled back.
  • It has been referenced from Object A, marking it “reachable.” If it hadn’t been referenced, garbage collection could have removed it from the client state.

Object D can be collected for the following reasons:

  • It is a committed object (there is no (new) suffix).
  • It also doesn’t contain any changes (no ** after its name).

This means that Object D does not contain uncommitted attribute values, and can be found in the app’s database whenever it is required. So the garbage collection mechanism removes it from the client state, even if it is referenced from Object A.

Object E can be collected because it is not referenced from any subscribed objects. This means that it is not accessible at all. Garbage collection can remove this object from the client state, including the uncommitted attribute/association changes. Since that object is no longer reachable, it is safe to remove the object.

In the previous section we mentioned that $currentSession (System.Session) is always subscribed to in the Mendix client, but it never ends up in the state. Now you can guess the reason: there might be objects in the state which reference the $currentSession, therefore “reachable” from it. That’s why they might also be kept in the state.

7. Conclusion

With the description above, we can further summarize the garbage collection process as below:

Garbage collection starts from all subscribed objects in the state and finds all “reachable” objects through associations from them by building a dependency graph. By analyzing this graph it decides to either keep or discard each object and (if exists) its changes in the client state.

7.1 Which Objects Are Kept in the State?

  • All subscribed objects are kept in the state.
  • All objects “reachable” from subscribed objects are kept if they fall into one of these categories:
    • Non-persistent objects, because they can’t be found in the database.
    • Persistent objects which are created but not yet committed, because they aren’t in the app’s database yet.
    • Persistent objects which are committed but contain changes, because changes aren’t in the database yet — but a microflow might need them.

7.2 Which Objects are Removed from the State?

  • Objects which are not reachable from subscribed objects are discarded. Such objects are often from previous pages the user had visited.
  • Objects which are still reachable from subscribed objects but whose state is the same as the database are discarded. This case applies to persistable objects when they’re committed to the database and have no changes. If the objects are needed, they can be loaded from the database.

After reading the descriptions above, you might feel a little cat-atonic:

So enough with the descriptions. Let’s practice what you’ve learned!

8. The Garbage Collection Game

Earlier I explained the garbage collection mechanism with graphics. Now you will get to play a game to see if you understand the mechanism. The game’s scenarios come from our internal unit tests for the GC mechanism. Let’s see if you can guess the correct answers!

The game is simple. Below you’ll see a representation of the client state, and the legend is the same as above. Here is an example:

Through the graphic, you can see the following things about Object 1:

  • It is a persistent entity (blue background)
  • It is subscribed (the red glow around it)
  • It is not committed to the database yet (has “(new)” at the end of its name)
  • It contains changes. (has ** at the end of its name)

You may have a question after seeing this case: Can a “new” object have changes? It can because when an object is created, its attributes hold their “default” value which can be defined in the domain model. Those default values become the current “committed” value of the attributes, and any changes made to them are stored in the state.

Objects can also reference each other through associations:

Above, you can see that Object 1 has an association with a value set to Object 2. Neither the name nor cardinality of the association matter for this game, so they’re not specified. This arrow states that Object 2 can be accessed with a Retrieve Activity in a microflow using the Association retrieve type.

There are seven levels in the game. Each level represents the current state of the “client state” before garbage collection happens. Your aim is to determine which objects in the state will be kept, and which objects will be removed from the state when garbage collection happens. After that you can click on “View answers” to reveal the results. Then you can move on to the next level.

Let the game begin!


I hope you enjoyed the game! Now that you understand how Mendix client cleans up the state, here are some clues to model better apps while keeping garbage collection in mind.

9. Best Practices: Keeping Garbage Collection in Mind while Modeling

9.1 Do Not Return Too Many Objects from Data Sources

Imagine a list view with a microflow source that returns thousands of objects. All of those objects are kept in the state, while a list view can only show a small portion of them at a given time depending on the paging configuration. This case is also true for a nanoflow data source and an association data source. Instead of this approach, try to use XPath or a database data source, which is highly optimized for large data sets. It loads only the data that can be shown on the current page.

9.2 Split Large Entities into Smaller Ones

Working with large objects increases the size of your app’s state. An entire large object is stored in memory even if it has a single change or is subscribed to for only one of its attributes. Splitting such objects might be beneficial for garbage collection, especially for non-persistable objects. This way, the garbage collection algorithm can remove unused smaller objects if they satisfy the conditions mentioned above.

9.3 Do Not Create “Star Objects”

A “star object” is an object which is referred to from hundreds of other objects. For example, if you have a SearchResult object, and there are 500 SearchResultItem referring to it, SearchResult becomes a star object. If there are subscriptions for either SearchResult or any of the SearchResultItem objects, all of the objects might be kept in the state. This pattern is often a problem if all of the objects are non-persistable or contain changes.

If you need to work with such objects, make sure that you commit or delete them manually, or set the values of the associations to a star object to empty after you use them.

9.4 Check the State’s Size During Development

Use the Ctrl + Alt + G shortcut to check the size of the state. Checking the state’s contents is especially important when working with big pages. For example, you might see that an object is lingering there because you forgot to unsubscribe from it in your custom widget.

9.5 Check Runtime Logs for Excess State

The Mendix client sends required parts of the state to runtime requests and a warning is logged if the number objects sent from client exceeds a certain threshold. If you see such a warning, you should revisit that page and investigate why there are so many objects kept in the state.

I hope you enjoyed reading this post and find it helpful. If you have any comments, questions, or general praise 😉 reach out to me here.