Performance of programs using immutable objects
Asked Answered
S

1

5

Does writing programs with immutable objects cause performance problems? If a given object is immutable and we need to somehow change its state, we have to map it to a new object with a slightly changed state. Thus, we can find ourselves in a situation where we create a lot of objects which congest memory and, as I understand it, can be problematic for the garbage collector. Is what I have described taking place or is there some aspect that I am not aware of on this topic?

Siesta answered 13/3, 2022 at 9:15 Comment(3)
You are right, immutability can lead to the creation of many objects. For example: if you use immutable date objects and increment a date in a loop.Empennage
Writing programs with inappropriate data structures might cause performance problems. There are situations where immutable object is not best design choice to start with (eg counters etc).Nonviolence
The need to instantiate new objects can be considered a drawback of using immutable classes, and sometimes you are probably better off without them. But the cost of instantiation should rarely be an issue. Also note that gc performance is not related to the number of objects you instantiate; it is more about the number of objects you keep references to. In other words, gc performance is mainly related to the number of "non-garbage" objects. In addition, using immutable classes can have other perf benefits, e.g. they are thread-safe by convention and can minimize locks in a multi-threaded app.Modigliani
C
6

When you repeatedly modify a mutable object, it will likely produce less garbage than repeatedly constructing new immutable objects to represent the intermediate state. But there are several reasons why using immutable objects still is not necessarily imposing a performance problem:

  • In typical applications, this scenario occurs only occasionally, compared to other uses where immutable objects do not suffer or even turn out to win. Most notably:

    • getters do not need to create a defensive copy when returning an immutable object
    • setters can store the incoming argument by reference if immutable
    • verification methods can be implemented the simple (or naïve) way, without having to deal with the check-then-act problem, as immutable objects can not change between the check and the subsequent use
    • immutable objects can be safely shared, to actually reduce the amount of created objects or used memory
  • The impact of garbage collection to the performance is often overestimated. We routinely use the immutable type java.lang.String, benefitting from the advantages mentioned above. Strings are also one of the most often used hash map keys.

    There’s the mutable companion class StringBuilder for the scenario of repeated string manipulations, but its main advantage is not about the number of allocated objects. The problem with string construction is that each object has to create a copy of the contained characters, so operations like repeatedly concatenating characters lead to a quadratic time complexity when constructing a new string on each step. The StringBuilder still reallocates its buffer under the hood when necessary but with a nonlinear growth, which yields an amortized linear time complexity for repeated concatenation.

    As explained in this answer, the costs of garbage collection mainly depend on the still existing objects, so temporary objects usually do not impact the garbage collection much unless you create an excessive amount of them. But even the latter scenario should only be addressed if you have an actual performance problem and an impartial profiling tool proves that a particular allocation site truly is the culprit.

  • Sophisticated applications may have to deal with undo/redo or other versioning features, which require keeping copies of the alternative application state anyway. At this point, using immutable objects may actually become an advantage, as you do not need to copy objects which did not change between two versions of the application state. A changed application state may consist 99% of the same objects as the previous state.

  • One of the big reasons why the topic has become popular (again) is that immutable objects provide the easiest way to implement efficient and correct parallel processing. There is no need to acquire a lock to prevent inconsistent modifications. As said above, there’s no need to worry about the check-then-act problem. Further, when the application state can be expressed as a single reference to a compound immutable object, the check for a concurrent update reduces to a simple reference comparison. Compare also with this answer.

So there are a lot of performance advantages with immutable objects which may compensate the disadvantages, if there are any. At the same time, potential bottlenecks can be fixed with the established solution of temporarily dealing with mutable objects for an operation while keeping the overall immutable behavior.

Conlon answered 14/3, 2022 at 12:36 Comment(4)
"unless you create an excessive amount of them" - Does that mean that immutables are not a good fit for applications that are processing large amounts of data? For example in case of Big Data?Siesta
Big Data implies a lot of reading and analyzing which works perfect with immutable data. The quoted phrase was referring to an excessive amount of temporary objects, representing intermediate calculation results. A large amount may trigger garbage collection earlier than without, but as also mentioned, this garbage collection may in turn be very cheap. It heavily depends on the environment, the chosen garbage collection algorithm(s) and the available memory—the more the better. That’s why I suggested to only worry, if it turns out to impose an actual performance problem.Conlon
Recently, I found another interesting article about immutable in Java blog.allegro.tech/2020/04/immutability-in-java.html. It caught my attention that Java was defined as a language that does not fully support this concept and that using immutables can have a negative impact on performance. For example, looking at this sentence from the mentioned article: "Many people don’t think about it, but you can allocate objects / memory fast enough to slow down your application to noticeable degree."Siesta
Well, you can use any operation to slow down your application, by just repeatedly performing the operation. I could counter with this example which is specifically designed to destroy the garbage collector’s performance and uses a mutable collection which uses even more mutable objects behind the scenes. Besides that, the example you found does match precisely the “unless you create an excessive amount of them” point. Further note that the main focus of game loops usually is latency, not throughput.Conlon

© 2022 - 2024 — McMap. All rights reserved.