Related
We all know that every object allocated in Java adds a weight into future garbage collection cycles, and Optional<T> objects are no different. We use these objects frequently to wrap nullable, which leads to safer code, but at what cost?
Does anyone have information on what kind of additional GC pressure optional objects add vs. simply returning nulls and what kind of impact this has on performance in high-throughput systems?
We all know that every object allocated in Java adds a weight into future garbage collection cycles,…
That sounds like a statement nobody could deny, but let’s look at the actual work of a garbage collector, considering common implementations of modern JVMs and the impact of an allocated object on it, especially objects like Optional instances which are typically of a temporary nature.
The first task of the garbage collector is to identify objects which are still alive. The name “garbage collector” puts a focus on identifying garbage, but garbage is defined as unreachable objects and the only way to find out which objects are unreachable, is via the process of elimination. So the first task is solved by traversing and marking all reachable objects. So the costs of this process do not depend on the total amount of allocated objects, but only those, which are still reachable.
The second task is to make the memory of the garbage available to new allocations. Instead of puzzling with the memory gaps between still reachable objects, all modern garbage collectors work by evacuating a complete region, transferring all alive objects withing that memory to a new location and adapting the references to them. After the process, the memory is available to new allocations as a whole block. So this is again a process whose costs do not depend on the total amount of allocated objects, but only (a part of) the still alive objects.
Therefore, an object like a temporary Optional may impose no costs on the actual garbage collection process at all, if it is allocated and abandoned between two garbage collection cycles.
With one catch, of course. Each allocation will reduce the memory available to subsequent allocations until there’s no space left and the garbage collection has to take place. So we could say, each allocation reduces the time between two garbage collection runs by the size of the allocation space divided by the object size. Not only is this a rather tiny fraction, it also only applies to a single threaded scenario.
In implementations like the Hotspot JVM, each thread uses a thread local allocation buffer (TLAB) for new objects. Once its TLAB is full, it will fetch a new one from the allocation space (aka Eden space). If there is none available, a garbage collection will be triggered. Now it’s rather unlikely that all threads hit the end of their TLAB right at the same time. So for the other threads which still have some space in their TLAB left at this time, it would not make any difference if they had allocated some more objects still fitting in that remaining space.
The perhaps surprising conclusion is that not every allocated object has an impact on the garbage collection, i.e. a purely local object allocated by a thread not triggering the next gc, could be entirely free.
Of course, this does not apply to allocating a large amount of objects. Allocating lots of them causes the thread to allocate more TLABs and eventually trigger the garbage collection earlier than without. That’s why we have classes like IntStream allowing to process a large number of elements without allocating objects, as would happen with a Stream<Integer>, while there is no problem in providing the result as a single OptionalInt instance. As we know now, a single temporary object has only a tiny impact on the gc, if any.
This did not even touch the JVM’s optimizer, which may eliminate object allocations in hot spots, if Escape Analysis has proven that the object is purely local.
I understand that in Java, if an object doesn't have any references to it any more, the garbage collector will reclaim it back some time later.
But how does the garbage collector know that an object has or has not references associated to it?
Is garbage collector using some kind of hashmap or table?
Edit:
Please note that I am not asking how generally gc works. really, I am not asking that.
I am asking specifically that How gc knows which objects are live and which are dead, with efficiencies.
That's why I say in my question that is gc maintain some kind of hashmap or set, and consistently update the number of references an object has?
A typical modern JVM uses several different types of garbage collectors.
One type that's often used for objects that have been around for a while is called Mark-and-Sweep. It basically involves starting from known "live" objects (the so-called garbage collection roots), following all chains of object references, and marking every reachable object as "live".
Once this is done, the sweep stage can reclaim those objects that haven't been marked as "live".
For this process to work, the JVM has to know the location in memory of every object reference. This is a necessary condition for a garbage collector to be precise (which Java's is).
Java has a variety of different garbage collection strategies, but they all basically work by keeping track which objects are reachable from known active objects.
A great summary can be found in the article How Garbage Collection works in Java but for the real low-down, you should look at Tuning Garbage Collection with the 5.0 Java[tm] Virtual Machine
An object is considered garbage when it can no longer be reached from any pointer in the running program. The most straightforward garbage collection algorithms simply iterate over every reachable object. Any objects left over are then considered garbage. The time this approach takes is proportional to the number of live objects, which is prohibitive for large applications maintaining lots of live data.
Beginning with the J2SE Platform version 1.2, the virtual machine incorporated a number of different garbage collection algorithms that are combined using generational collection. While naive garbage collection examines every live object in the heap, generational collection exploits several empirically observed properties of most applications to avoid extra work.
The most important of these observed properties is infant mortality. ...
I.e. many objects like iterators only live for a very short time, so younger objects are more likely to be eligible for garbage collection than much older objects.
For more up to date tuning guides, take a look at:
Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning
Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide (Java SE 8)
Incidentally, be careful of trying to second guess your garbage collection strategy, I've known many a programs performance for be trashed by over zealous use of System.gc() or inappropriate -XX options.
GC will know that object can be removed as quickly as it is possible. You are not expected to manage this process.
But you can ask GC very politely to run using System.gc(). It is just a tip to the system. GC does not have to run at that moment, it does not have to remove your specific object etc. Because GC is the BIG boss and we (Java programmers) are just its slaves... :(
The truth is that the garbage collector does not, in general, quickly know which objects no longer have any incoming references. And, in fact, an object can be garbage even when there are incoming references it.
The garbage collector uses a traversal of the object graph to find the objects that are reachable. Objects that are not reached in this traversal are deemed garbage, even if they are part of a cycle of references. The delay between an object being unreachable, and the garbage collector actually collecting the object, could be arbitrarily long.
There is no efficient way - it will still require traversal of the heap, but there is a hacky way: when the heap is divided into smaller pieces (thus no need to scan the entire heap). This is the reason we have generational garbage collectors, so that the scanning takes less time.
This is relatively "easy" to answer when your entire application is stopped and you can analyze the graph of objects. It all starts from GC roots (I'll let you find the documentation for what these are), but basically these are "roots" that are not collected by the GC.
From here a certain scan starts that analyzes the "live" objects: objects that have a direct (or transitive) connection to these roots, thus not reclaimable. In graph theory this is know to "color/traverse" your graph by using 3 colors: black, grey and white. White means it is not connected to the roots, grey means it's sub-graph is not yet traversed, black means traversed and connected to the roots. So basically to know what exactly is dead/alive right now - you simply need to take all your heap that is white initially and color it to black. Everything that is white is garbage. It is interesting that "garbage" is really identified by a GC by knowing what is actually alive. There are some drawings to visualize this here for example.
But this is the simple scenario: when your application is entirely stopped (for seconds at times) and you can scan the heap. This is called a STW - stop the world event and people hate these usually. This is what parallel collectors do: stop everything, do whatever GC has to (including finding garbage), let the application threads start after that.
What happens when you app is running and you are scanning the heap? Concurrently? G1/CMS do this. Think about it: how can you reason about a leaf from a graph being alive or not when your app can change that leaf via a different thread.
Shenandoah for example, solves this by "intercepting" changes over the graph. While running concurrently with your application, it will catch all the changes and insert these to some thread local special queues, called SATB Queues (snapshot at the begging queues); instead of altering the heap directly. When that is finished, a very short STW event will occur and these queues will be drained. Still under the STW what that drain has "caused" is computed, i.e. : extra coloring of the graph. This is far simplified, just FYI. G1 and CMS do it differently AFAIK.
So in theory, the process is not really that complicated, but implementing it concurrently is the most challenging part.
Effective Java says :
There is a severe performance penalty for using finalizers.
Why is it slower to destroy an object using the finalizers?
Because of the way the garbage collector works. For performance, most Java GCs use a copying collector, where short-lived objects are allocated into an "eden" block of memory, and when the it's time for that generation of objects to be collected, the GC just needs to copy the objects that are still "alive" to a more permanent storage space, and then it can wipe (free) the entire "eden" memory block at once. This is efficient because most Java code will create many thousands of instances of objects (boxed primitives, temporary arrays, etc.) with lifetimes of only a few seconds.
When you have finalizers in the mix, though, the GC can't simply wipe an entire generation at once. Instead, it needs to figure out all the objects in that generation that need to be finalized, and queue them on a thread that actually executes the finalizers. In the meantime, the GC can't finish cleaning up the objects efficiently. So it either has to keep them alive longer than they should be, or it has to delay collecting other objects, or both. Plus you have the arbitrary wait time of actually executing the finalizers.
All these factors add up to a significant runtime penalty, which is why deterministic finalization (using a close() method or similar to explicitly finalize the object's state) is usually preferred.
Having actually run into one such problem:
In the Sun HotSpot JVM, finalizers are processed on a thread that is given a fixed, low priority. In a high-load application, it's easy to create finalization-required objects faster than the low-priority finalization thread can process them. Meanwhile, the space on the heap used by the finalization-pending objects is unavailable for other uses. Eventually, your application may spend all of its time garbage collecting, because all of the available memory is in use by objects pending finalization.
This is, of course, in addition to the other many reasons to not use finalizers that are described in Effective Java.
I just picked up my copy Effective Java off my desk to see what he's referring to.
If you read Chapter 2, Section 6, he goes into good detail about the various performance hits.
You can't know when the finalizer will run, or even if it will at all. Because those resources may never be claimed, you will have to run with fewer resources.
I would recommend reading the entirety of the section - it explains things much better than I can parrot here.
If you read the documentation of finalize() closely, you will notice that finalizers enable an object to prevent being collected by the GC.
If no finalizer is present, the object simply can be removed and does not need any more attention. But if there is a finalizer, it needs to be checked afterwards, if the object didn't become "visible" again.
Without knowing exactly how the current Java garbage collection is implemented (actually, because there are different Java implementations out there, there are also different GCs), you can assume that the GC has to do some additional work if an object has a finalizer, because of this feature.
My thought is this:
Java is a garbage collected language, which deallocates memory based on its own internal algorithms. Every so often, the GC scans the heap, determines which objects are no longer referenced, and de-allocates the memory.
A finalizer interrupts this and forces the deallocation of memory outside of the GC cycle, potentially causing inefficiencies.
I think best practices are to use finalizers only when ABSOLUTELY necessary such as freeing file handles or closing DB connections which should be done deterministically.
One reason I can think of is that explicit memory cleanup is unnecessary if your resources are all Java Objects, and not native code.
Is there a way to free memory in Java, similar to C's free() function? Or is setting the object to null and relying on GC the only option?
Java uses managed memory, so the only way you can allocate memory is by using the new operator, and the only way you can deallocate memory is by relying on the garbage collector.
This memory management whitepaper (PDF) may help explain what's going on.
You can also call System.gc() to suggest that the garbage collector run immediately. However, the Java Runtime makes the final decision, not your code.
According to the Java documentation,
Calling the gc method suggests that
the Java Virtual Machine expend effort
toward recycling unused objects in
order to make the memory they
currently occupy available for quick
reuse. When control returns from the
method call, the Java Virtual Machine
has made a best effort to reclaim
space from all discarded objects.
No one seems to have mentioned explicitly setting object references to null, which is a legitimate technique to "freeing" memory you may want to consider.
For example, say you'd declared a List<String> at the beginning of a method which grew in size to be very large, but was only required until half-way through the method. You could at this point set the List reference to null to allow the garbage collector to potentially reclaim this object before the method completes (and the reference falls out of scope anyway).
Note that I rarely use this technique in reality but it's worth considering when dealing with very large data structures.
System.gc();
Runs the garbage collector.
Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.
Not recommended.
Edit: I wrote the original response in 2009. It's now 2015.
Garbage collectors have gotten steadily better in the ~20 years Java's been around. At this point, if you're manually calling the garbage collector, you may want to consider other approaches:
If you're forcing GC on a limited number of machines, it may be worth having a load balancer point away from the current machine, waiting for it to finish serving to connected clients, timeout after some period for hanging connections, and then just hard-restart the JVM. This is a terrible solution, but if you're looking at System.gc(), forced-restarts may be a possible stopgap.
Consider using a different garbage collector. For example, the (new in the last six years) G1 collector is a low-pause model; it uses more CPU overall, but does it's best to never force a hard-stop on execution. Since server CPUs now almost all have multiple cores, this is A Really Good Tradeoff to have available.
Look at your flags tuning memory use. Especially in newer versions of Java, if you don't have that many long-term running objects, consider bumping up the size of newgen in the heap. newgen (young) is where new objects are allocated. For a webserver, everything created for a request is put here, and if this space is too small, Java will spend extra time upgrading the objects to longer-lived memory, where they're more expensive to kill. (If newgen is slightly too small, you're going to pay for it.) For example, in G1:
XX:G1NewSizePercent (defaults to 5; probably doesn't matter.)
XX:G1MaxNewSizePercent (defaults to 60; probably raise this.)
Consider telling the garbage collector you're not okay with a longer pause. This will cause more-frequent GC runs, to allow the system to keep the rest of it's constraints. In G1:
XX:MaxGCPauseMillis (defaults to 200.)
*"I personally rely on nulling variables as a placeholder for future proper deletion. For example, I take the time to nullify all elements of an array before actually deleting (making null) the array itself."
This is unnecessary. The way the Java GC works is it finds objects that have no reference to them, so if I have an Object x with a reference (=variable) a that points to it, the GC won't delete it, because there is a reference to that object:
a -> x
If you null a than this happens:
a -> null
x
So now x doesn't have a reference pointing to it and will be deleted. The same thing happens when you set a to reference to a different object than x.
So if you have an array arr that references to objects x, y and z and a variable a that references to the array it looks like that:
a -> arr -> x
-> y
-> z
If you null a than this happens:
a -> null
arr -> x
-> y
-> z
So the GC finds arr as having no reference set to it and deletes it, which gives you this structure:
a -> null
x
y
z
Now the GC finds x, y and z and deletes them aswell. Nulling each reference in the array won't make anything better, it will just use up CPU time and space in the code (that said, it won't hurt further than that. The GC will still be able to perform the way it should).
To extend upon the answer and comment by Yiannis Xanthopoulos and Hot Licks (sorry, I cannot comment yet!), you can set VM options like this example:
-XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30
In my jdk 7 this will then release unused VM memory if more than 30% of the heap becomes free after GC when the VM is idle. You will probably need to tune these parameters.
While I didn't see it emphasized in the link below, note that some garbage collectors may not obey these parameters and by default java may pick one of these for you, should you happen to have more than one core (hence the UseG1GC argument above).
VM arguments
Update: For java 1.8.0_73 I have seen the JVM occasionally release small amounts with the default settings. Appears to only do it if ~70% of the heap is unused though.. don't know if it would be more aggressive releasing if the OS was low on physical memory.
A valid reason for wanting to free memory from any programm (java or not ) is to make more memory available to other programms on operating system level. If my java application is using 250MB I may want to force it down to 1MB and make the 249MB available to other apps.
I have done experimentation on this.
It's true that System.gc(); only suggests to run the Garbage Collector.
But calling System.gc(); after setting all references to null, will improve performance and memory occupation.
If you really want to allocate and free a block of memory you can do this with direct ByteBuffers. There is even a non-portable way to free the memory.
However, as has been suggested, just because you have to free memory in C, doesn't mean it a good idea to have to do this.
If you feel you really have a good use case for free(), please include it in the question so we can see what you are rtying to do, it is quite likely there is a better way.
Entirely from javacoffeebreak.com/faq/faq0012.html
A low priority thread takes care of garbage collection automatically
for the user. During idle time, the thread may be called upon, and it
can begin to free memory previously allocated to an object in Java.
But don't worry - it won't delete your objects on you!
When there are no references to an object, it becomes fair game for
the garbage collector. Rather than calling some routine (like free in
C++), you simply assign all references to the object to null, or
assign a new class to the reference.
Example :
public static void main(String args[])
{
// Instantiate a large memory using class
MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192);
// Do some work
for ( .............. )
{
// Do some processing on myClass
}
// Clear reference to myClass
myClass = null;
// Continue processing, safe in the knowledge
// that the garbage collector will reclaim myClass
}
If your code is about to request a large amount of memory, you may
want to request the garbage collector begin reclaiming space, rather
than allowing it to do so as a low-priority thread. To do this, add
the following to your code
System.gc();
The garbage collector will attempt to reclaim free space, and your
application can continue executing, with as much memory reclaimed as
possible (memory fragmentation issues may apply on certain platforms).
In my case, since my Java code is meant to be ported to other languages in the near future (Mainly C++), I at least want to pay lip service to freeing memory properly so it helps the porting process later on.
I personally rely on nulling variables as a placeholder for future proper deletion. For example, I take the time to nullify all elements of an array before actually deleting (making null) the array itself.
But my case is very particular, and I know I'm taking performance hits when doing this.
* "For example, say you'd declared a List at the beginning of a
method which grew in size to be very large, but was only required
until half-way through the method. You could at this point set the
List reference to null to allow the garbage collector to potentially
reclaim this object before the method completes (and the reference
falls out of scope anyway)." *
This is correct, but this solution may not be generalizable. While setting a List object reference to null -will- make memory available for garbage collection, this is only true for a List object of primitive types. If the List object instead contains reference types, setting the List object = null will not dereference -any- of the reference types contained -in- the list. In this case, setting the List object = null will orphan the contained reference types whose objects will not be available for garbage collection unless the garbage collection algorithm is smart enough to determine that the objects have been orphaned.
Althrough java provides automatic garbage collection sometimes you will want to know how large the object is and how much of it is left .Free memory using programatically import java.lang; and Runtime r=Runtime.getRuntime(); to obtain values of memory using mem1=r.freeMemory(); to free memory call the r.gc(); method and the call freeMemory()
Recommendation from JAVA is to assign to null
From https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html
Explicitly assigning a null value to variables that are no longer needed helps the garbage collector to identify the parts of memory that can be safely reclaimed. Although Java provides memory management, it does not prevent memory leaks or using excessive amounts of memory.
An application may induce memory leaks by not releasing object references. Doing so prevents the Java garbage collector from reclaiming those objects, and results in increasing amounts of memory being used. Explicitly nullifying references to variables after their use allows the garbage collector to reclaim memory.
One way to detect memory leaks is to employ profiling tools and take memory snapshots after each transaction. A leak-free application in steady state will show a steady active heap memory after garbage collections.
Python uses the reference count method to handle object life time. So an object that has no more use will be immediately destroyed.
But, in Java, the GC(garbage collector) destroys objects which are no longer used at a specific time.
Why does Java choose this strategy and what is the benefit from this?
Is this better than the Python approach?
There are drawbacks of using reference counting. One of the most mentioned is circular references: Suppose A references B, B references C and C references B. If A were to drop its reference to B, both B and C will still have a reference count of 1 and won't be deleted with traditional reference counting. CPython (reference counting is not part of python itself, but part of the C implementation thereof) catches circular references with a separate garbage collection routine that it runs periodically...
Another drawback: Reference counting can make execution slower. Each time an object is referenced and dereferenced, the interpreter/VM must check to see if the count has gone down to 0 (and then deallocate if it did). Garbage Collection does not need to do this.
Also, Garbage Collection can be done in a separate thread (though it can be a bit tricky). On machines with lots of RAM and for processes that use memory only slowly, you might not want to be doing GC at all! Reference counting would be a bit of a drawback there in terms of performance...
Actually reference counting and the strategies used by the Sun JVM are all different types of garbage collection algorithms.
There are two broad approaches for tracking down dead objects: tracing and reference counting. In tracing the GC starts from the "roots" - things like stack references, and traces all reachable (live) objects. Anything that can't be reached is considered dead. In reference counting each time a reference is modified the object's involved have their count updated. Any object whose reference count gets set to zero is considered dead.
With basically all GC implementations there are trade offs but tracing is usually good for high through put (i.e. fast) operation but has longer pause times (larger gaps where the UI or program may freeze up). Reference counting can operate in smaller chunks but will be slower overall. It may mean less freezes but poorer performance overall.
Additionally a reference counting GC requires a cycle detector to clean up any objects in a cycle that won't be caught by their reference count alone. Perl 5 didn't have a cycle detector in its GC implementation and could leak memory that was cyclic.
Research has also been done to get the best of both worlds (low pause times, high throughput):
http://cs.anu.edu.au/~Steve.Blackburn/pubs/papers/urc-oopsla-2003.pdf
Darren Thomas gives a good answer. However, one big difference between the Java and Python approaches is that with reference counting in the common case (no circular references) objects are cleaned up immediately rather than at some indeterminate later date.
For example, I can write sloppy, non-portable code in CPython such as
def parse_some_attrs(fname):
return open(fname).read().split("~~~")[2:4]
and the file descriptor for that file I opened will be cleaned up immediately because as soon as the reference to the open file goes away, the file is garbage collected and the file descriptor is freed. Of course, if I run Jython or IronPython or possibly PyPy, then the garbage collector won't necessarily run until much later; possibly I'll run out of file descriptors first and my program will crash.
So you SHOULD be writing code that looks like
def parse_some_attrs(fname):
with open(fname) as f:
return f.read().split("~~~")[2:4]
but sometimes people like to rely on reference counting to always free up their resources because it can sometimes make your code a little shorter.
I'd say that the best garbage collector is the one with the best performance, which currently seems to be the Java-style generational garbage collectors that can run in a separate thread and has all these crazy optimizations, etc. The differences to how you write your code should be negligible and ideally non-existent.
I think the article "Java theory and practice: A brief history of garbage collection" from IBM should help explain some of the questions you have.
One big disadvantage of Java's tracing GC is that from time to time it will "stop the world" and freeze the application for a relatively long time to do a full GC. If the heap is big and the the object tree complex, it will freeze for a few seconds. Also each full GC visits the whole object tree over and over again, something that is probably quite inefficient. Another drawback of the way Java does GC is that you have to tell the jvm what heap size you want (if the default is not good enough); the JVM derives from that value several thresholds that will trigger the GC process when there is too much garbage stacking up in the heap.
I presume that this is actually the main cause of the jerky feeling of Android (based on Java), even on the most expensive cellphones, in comparison with the smoothness of iOS (based on ObjectiveC, and using RC).
I'd love to see a jvm option to enable RC memory management, and maybe keeping GC only to run as a last resort when there is no more memory left.
Garbage collection is faster (more time efficient) than reference counting, if you have enough memory. For example, a copying gc traverses the "live" objects and copies them to a new space, and can reclaim all the "dead" objects in one step by marking a whole memory region. This is very efficient, if you have enough memory. Generational collections use the knowledge that "most objects die young"; often only a few percent of objects have to be copied.
[This is also the reason why gc can be faster than malloc/free]
Reference counting is much more space efficient than garbage collection, since it reclaims memory the very moment it gets unreachable. This is nice when you want to attach finalizers to objects (e.g. to close a file once the File object gets unreachable). A reference counting system can work even when only a few percent of the memory is free. But the management cost of having to increment and decrement counters upon each pointer assignment cost a lot of time, and some kind of garbage collection is still needed to reclaim cycles.
So the trade-off is clear: if you have to work in a memory-constrained environment, or if you need precise finalizers, use reference counting. If you have enough memory and need the speed, use garbage collection.
Reference counting is particularly difficult to do efficiently in a multi-threaded environment. I don't know how you'd even start to do it without getting into hardware assisted transactions or similar (currently) unusual atomic instructions.
Reference counting is easy to implement. JVMs have had a lot of money sunk into competing implementations, so it shouldn't be surprising that they implement very good solutions to very difficult problems. However, it's becoming increasingly easy to target your favourite language at the JVM.
The latest Sun Java VM actually have multiple GC algorithms which you can tweak. The Java VM specifications intentionally omitted specifying actual GC behaviour to allow different (and multiple) GC algorithms for different VMs.
For example, for all the people who dislike the "stop-the-world" approach of the default Sun Java VM GC behaviour, there are VM such as IBM's WebSphere Real Time which allows real-time application to run on Java.
Since the Java VM spec is publicly available, there is (theoretically) nothing stopping anyone from implementing a Java VM that uses CPython's GC algorithm.
Late in the game, but I think one significant rationale for RC in python is its simplicity. See this email by Alex Martelli, for example.
(I could not find a link outside google cache, the email date from 13th october 2005 on python list).