92

Today my colleagues and me have a discussion about the usage of the final keyword in Java to improve the garbage collection.

For example, if you write a method like:

public Double doCalc(final Double value)
{
   final Double maxWeight = 1000.0;
   final Double totalWeight = maxWeight * value;
   return totalWeight;  
}

Declaring the variables in the method final would help the garbage collection to clean up the memory from the unused variables in the method after the method exits.

Is this true?

Goran Martinic
  • 3,807
  • 4
  • 21
  • 14
  • there are _two_ things here, actually. 1) when you write to a method local field _and_ an instance. When you write to an instance there _could_ be a benefit. – Eugene Dec 18 '19 at 12:41

15 Answers15

92

Here's a slightly different example, one with final reference-type fields rather than final value-type local variables:

public class MyClass {

   public final MyOtherObject obj;

}

Every time you create an instance of MyClass, you'll be creating an outgoing reference to a MyOtherObject instance, and the GC will have to follow that link to look for live objects.

The JVM uses a mark-sweep GC algorithm, which has to examine all the live refereces in the GC "root" locations (like all the objects in the current call stack). Each live object is "marked" as being alive, and any object referred to by a live object is also marked as being alive.

After the completion of the mark phase, the GC sweeps through the heap, freeing memory for all unmarked objects (and compacting the memory for the remaining live objects).

Also, it's important to recognize that the Java heap memory is partitioned into a "young generation" and an "old generation". All objects are initially allocated in the young generation (sometimes referred to as "the nursery"). Since most objects are short-lived, the GC is more aggressive about freeing recent garbage from the young generation. If an object survives a collection cycle of the young generation, it gets moved into the old generation (sometimes referred to as the "tenured generation"), which is processed less frequently.

So, off the top of my head, I'm going to say "no, the 'final' modifer doesn't help the GC reduce its workload".

In my opinion, the best strategy for optimizing your memory-management in Java is to eliminate spurious references as quickly as possible. You could do that by assigning "null" to an object reference as soon as you're done using it.

Or, better yet, minimize the size of each declaration scope. For example, if you declare an object at the beginning of a 1000-line method, and if the object stays alive until the close of that method's scope (the last closing curly brace), then the object might stay alive for much longer that actually necessary.

If you use small methods, with only a dozen or so lines of code, then the objects declared within that method will fall out of scope more quickly, and the GC will be able to do most of its work within the much-more-efficient young generation. You don't want objects being moved into the older generation unless absolutely necessary.

benjismith
  • 16,559
  • 9
  • 57
  • 80
  • Food for thought. I always thought inline code was faster, but if jvm runs short of memory then it will slow as well. hmmmmm... – WolfmanDragon Dec 09 '08 at 21:33
  • 1
    I'm just guessing here... but I assume that the JIT compiler can inline final primitive values (not objects), for modest performance increases. Code inlining, on the other hand, is capable of producing significant optimizations, but doesn't have anything to do with final variables. – benjismith Dec 10 '08 at 00:58
  • 2
    It wouldn't be possible to assign null to an already created final object, then perhaps *final* instead of help, could make things harder – Hernán Eche Dec 14 '11 at 21:05
  • 1
    One could use { } to limit scope in a large method also, rather than breaking it out to several private methods which might not be relevant to other class methods. – mjs Dec 10 '12 at 14:12
  • You can also use local variables instead of fields when it is possible in order to enhance memory garbage collection and to reduce referential ties. – sivi Mar 10 '14 at 09:21
  • [it can reduce the workload](https://stackoverflow.com/a/59392595/1059372). as many have said, _local_ final variables are indeed useless information for the GC; but instance ones are not. – Eugene Dec 18 '19 at 13:02
38

Declaring a local variable final will not affect garbage collection, it only means you can not modify the variable. Your example above should not compile as you are modifying the variable totalWeight which has been marked final. On the other hand, declaring a primitive (double instead of Double) final will allows that variable to be inlined into the calling code, so that could cause some memory and performance improvement. This is used when you have a number of public static final Strings in a class.

In general, the compiler and runtime will optimize where it can. It is best to write the code appropriately and not try to be too tricky. Use final when you do not want the variable to be modified. Assume that any easy optimizations will be performed by the compiler, and if you are worried about performance or memory use, use a profiler to determine the real problem.

Ken Gentle
  • 13,277
  • 2
  • 41
  • 49
Aaron
  • 19,151
  • 4
  • 28
  • 23
26

No, it is emphatically not true.

Remember that final does not mean constant, it just means you can't change the reference.

final MyObject o = new MyObject();
o.setValue("foo"); // Works just fine
o = new MyObject(); // Doesn't work.

There may be some small optimisation based around the knowledge that the JVM will never have to modify the reference (such as not having check to see if it has changed) but it would be so minor as to not worry about.

Final should be thought of as useful meta-data to the developer and not as a compiler optimisation.

SCdF
  • 57,260
  • 24
  • 77
  • 113
17

Some points to clear up:

  • Nulling out reference should not help GC. If it did, it would indicate that your variables are over scoped. One exception is the case of object nepotism.

  • There is no on-stack allocation as of yet in Java.

  • Declaring a variable final means you can't (under normal conditions) assign a new value to that variable. Since final says nothing about scope, it doesn't say anything about it's effect on GC.

DarthJDG
  • 16,511
  • 11
  • 49
  • 56
Kirk
  • 749
  • 1
  • 7
  • 10
  • There is on-stack allocation (of primitives and references to objects in the heap) in java: https://stackoverflow.com/a/8061692/32453 Java requires objects in the same closure as an anonymous class/lambda to be final as well, but turns out that's just to reduce "memory required/frame"/reduce confusion so is unrelated to its being collected... – rogerdpack Nov 03 '17 at 19:14
12

Well, I don't know about the use of the "final" modifier in this case, or its effect on the GC.

But I can tell you this: your use of Boxed values rather than primitives (e.g., Double instead of double) will allocate those objects on the heap rather than the stack, and will produce unnecessary garbage that the GC will have to clean up.

I only use boxed primitives when required by an existing API, or when I need nullable primatives.

benjismith
  • 16,559
  • 9
  • 57
  • 80
5

Final variables cannot be changed after initial assignment (enforced by the compiler).

This does not change the behaviour of the garbage collection as such. Only thing is that these variables cannot be nulled when not being used any more (which may help the garbage collection in memory tight situations).

You should know that final allows the compiler to make assumptions about what to optimize. Inlining code and not including code known not to be reachable.

final boolean debug = false;

......

if (debug) {
  System.out.println("DEBUG INFO!");
}

The println will not be included in the byte code.

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347
4

There is a not so well known corner case with generational garbage collectors. (For a brief description read the answer by benjismith for a deeper insight read the articles at the end).

The idea in generational GCs is that most of the time only young generations need to be considered. The root location is scanned for references, and then the young generation objects are scanned. During this more frequent sweeps no object in the old generation are checked.

Now, the problem comes from the fact that an object is not allowed to have references to younger objects. When a long lived (old generation) object gets a reference to a new object, that reference must be explicitly tracked by the garbage collector (see article from IBM on the hotspot JVM collector), actually affecting the GC performance.

The reason why an old object cannot refer to a younger one is that, as the old object is not checked in minor collections, if the only reference to the object is kept in the old object, it will not get marked, and would be wrongly deallocated during the sweep stage.

Of course, as pointed by many, the final keyword does not reallly affect the garbage collector, but it does guarantee that the reference will never be changed into a younger object if this object survives the minor collections and makes it to the older heap.

Articles:

IBM on garbage collection: history, in the hotspot JVM and performance. These may no longer be fully valid, as it dates back in 2003/04, but they give some easy to read insight into GCs.

Sun on Tuning garbage collection

Community
  • 1
  • 1
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
3

GC acts on unreachable refs. This has nothing to do with "final", which is merely an assertion of one-time assignment. Is it possible that some VM's GC can make use of "final"? I don't see how or why.

dongilmore
  • 614
  • 3
  • 7
3

final on local variables and parameters makes no difference to the class files produced, so cannot affect runtime performance. If a class has no subclasses, HotSpot treats that class as if it is final anyway (it can undo later if a class that breaks that assumption is loaded). I believe final on methods is much the same as classes. final on static field may allow the variable to be interpreted as a "compile-time constant" and optimisation to be done by javac on that basis. final on fields allows the JVM some freedom to ignore happens-before relations.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
2

There seems to be a lot of answers that are wandering conjectures. The truth is, there is no final modifier for local variables at the bytecode level. The virtual machine will never know that your local variables were defined as final or not.

The answer to your question is an emphatic no.

Matt Quigley
  • 7,614
  • 4
  • 25
  • 26
  • That might be true, but the compiler can still use the final information during the data flow analysis. –  Jul 18 '14 at 08:02
  • @WernerVanBelle the compiler *already knows* that a variable is only set once. It has to do data flow analysis already in order to know that a variable might be null, is not initialized before use, etc. So, local finals don't offer any new information to the compiler. – Matt Quigley Jul 20 '14 at 03:12
  • It doesn't. Dataflow analysis can deduce a lot of things, but it is possible to have a turing complete program inside a block that will or will not set a local variable. The compiler cannot know in advance whether the variable will be written and will be constant at all. Therefore the compiler, without the final keyword, cannot guarantee that a variable is final or not. –  Jul 21 '14 at 05:53
  • @WernerVanBelle I'm genuinely intrigued, can you give an example? I don't see how there can be a final assignment to a non-final variable that the compiler doesn't know about. The compiler knows that if you have an uninitialized variable, it won't let you use it. If you try to assign a final variable inside of a loop, the compiler won't let you. What is an example where a variable that CAN be declared as final, but is NOT, and the compiler cannot guarantee that it is final? I suspect that any example would be a variable that could not be declared as final in the first place. – Matt Quigley Jul 22 '14 at 06:44
  • Ah after re-reading your comment, I see that your example is a variable initialized inside a conditional block. Those variables can't be final in the first place, unless all condition paths initialize the variable one time. So, the compiler does know about such declarations - that's how it allows you to compile a final variable initialized in two different places (think `final int x; if (cond) x=1; else x=2;`). The compiler, without the final keyword, therefore can guarantee that a variable is final or not. – Matt Quigley Jul 22 '14 at 06:58
  • What I tried to say is that without final the compiler does not know that a variable is actually a constant (although it could be a constant). With final, it is sure about that. That means that final allows for more optimizations than without. –  Jul 22 '14 at 15:28
  • Hmm, you'll have to give an example. I still am sure that the compiler knows with or without the keyword whether a variable is constant. Imagine this: the compiler "pretends" that _every_ local variable is declared final. If it can compile with the final variable, it now "knows" that it can be final without the keyword. If it can't compile, it conversely "knows" that the variable can't be final. Whether or not someone added the keyword in the text has no bearing on that. – Matt Quigley Jul 22 '14 at 21:09
  • A case where a variable is constant, yet the compiler will not be able to detect it. int a=1; while(a<100) a*=2; [at this position 'a' is always 128]. Nevertheless I do understand your issue here because this code could not be written with final. An interesting example would be a case where you could potentially add final and where it would actually help the compiler. –  Jul 22 '14 at 22:09
  • The 'turing complete' example is: { int a = 3; if (complexFalse()) a = 4; } where complexFalse() is a method that the compiler cannot prove will always be false (cf. halting-problem). In this case you couldn't declare a final without converting the if-statement into a ternary expression---at which point dataflow works again anyway. IOW, while trivially correct, the exception isn't useful in practice. – Recurse Dec 10 '14 at 02:55
  • @Recurse That example isn't valid, though; `a` in your example cannot be declared as final. `{final int a = 3; if (complexFalse()) a = 4}` will not compile in Java, so the inclusion or absence of the `final` keyword does not apply. The point is, there is never anything a human can do with the final modifier (in Java) that will tell compiler anything it does not already know. – Matt Quigley Dec 11 '14 at 06:53
1

Strictly speaking about instance fields, final might improve performance slightly if a particular GC wants to exploit that. When a concurrent GC happens (that means that your application is still running, while GC is in progress), see this for a broader explanation, GCs have to employ certain barriers when writes and/or reads are done. The link I gave you pretty much explains that, but to make it really short: when a GC does some concurrent work, all read and writes to the heap (while that GC is in progress), are "intercepted" and applied later in time; so that the concurrent GC phase can finish it's work.

For final instance fields, since they can not be modified (unless reflection), these barriers can be omitted. And this is not just pure theory.

Shenandoah GC has them in practice (though not for long), and you can do, for example:

-XX:+UnlockExperimentalVMOptions  
-XX:+UseShenandoahGC  
-XX:+ShenandoahOptimizeInstanceFinals

And there will be optimizations in the GC algorithm that will make it slightly faster. This is because there will be no barriers intercepting final, since no one should modify them, ever. Not even via reflection or JNI.

Eugene
  • 117,005
  • 15
  • 201
  • 306
1

All method and variable can be overridden bydefault in subclasses.If we want to save the subclasses from overridig the members of superclass,we can declare them as final using the keyword final. For e.g- final int a=10; final void display(){......} Making a method final ensures that the functionality defined in the superclass will never be changed anyway. Similarly the value of a final variable can never be changed. Final variables behaves like class variables.

0

absolutely, as long as make object's life shorter which yield great benefit of memory management, recently we examined export functionality having instance variables on one test and another test having method level local variable. during load testing, JVM throws outofmemoryerror on first test and JVM got halted. but in second test, successfully able to get the report due to better memory management.

0

The only time I prefer declaring local variables as final is when:

  • I have to make them final so that they can be shared with some anonymous class (for example: creating daemon thread and let it access some value from enclosing method)

  • I want to make them final (for example: some value that shouldn't/doesn't get overridden by mistake)

Does they help in fast garbage collection?
AFAIK a object becomes a candidate of GC collection if it has zero strong references to it and in that case as well there is no guarantee that they will be immediately garbage collected . In general, a strong reference is said to die when it goes out of scope or user explicitly reassign it to null reference, thus, declaring them final means that reference will continue to exists till the method exists (unless its scope is explicitly narrowed down to a specific inner block {}) because you can't reassign final variables (i.e. can't reassign to null). So I think w.r.t Garbage Collection 'final' may introduce a unwanted possible delay so one must be little careful in defining there scope as that controls when they will become candidate for GC.

sactiw
  • 21,935
  • 4
  • 41
  • 28
0

The only thing that I can think of is that the compiler might optimize away the final variables and inline them as constants into the code, thus you end up with no memory allocated.

Sijin
  • 4,530
  • 21
  • 22