23

Hopefully a simple question. Take for instance a Circularly-linked list:

class ListContainer
{
  private listContainer next;
  <..>

  public void setNext(listContainer next)
  {
    this.next = next;
  }
}

class List
{
  private listContainer entry;
  <..>
}

Now since it's a circularly-linked list, when a single elemnt is added, it has a reference to itself in it's next variable. When deleting the only element in the list, entry is set to null. Is there a need to set ListContainer.next to null as well for Garbage Collector to free it's memory or does it handle such self-references automagically?

Scott Dorman
  • 42,236
  • 12
  • 79
  • 110
Saulius Žemaitaitis
  • 2,956
  • 3
  • 29
  • 36

7 Answers7

24

Garbage collectors which rely solely on reference counting are generally vulnerable to failing to collection self-referential structures such as this. These GCs rely on a count of the number of references to the object in order to calculate whether a given object is reachable.

Non-reference counting approaches apply a more comprehensive reachability test to determine whether an object is eligible to be collected. These systems define an object (or set of objects) which are always assumed to be reachable. Any object for which references are available from this object graph is considered ineligible for collection. Any object not directly accessible from this object is not. Thus, cycles do not end up affecting reachability, and can be collected.

See also, the Wikipedia page on tracing garbage collectors.

jsight
  • 27,819
  • 25
  • 107
  • 140
  • Any reference counting GC with a cycle detector (such as trial deletion or mark/sweep) will also catch self references. – Luke Quinane Mar 02 '09 at 12:45
  • An example of a reference counting GC that handles cycles is CPython. An example of one that fails to handle cycles is Visual Basic 6. – Nate C-K Jan 24 '14 at 21:37
14

Circular references is a (solvable) problem if you rely on counting the references in order to decide whether an object is dead. No java implementation uses reference counting, AFAIK. Newer Sun JREs uses a mix of several types of GC, all mark-and-sweep or copying I think.

You can read more about garbage collection in general at Wikipedia, and some articles about java GC here and here, for example.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
gnud
  • 77,584
  • 5
  • 64
  • 78
  • 1
    Jikes RVM includes a high performance reference counting GC. See http://cs.anu.edu.au/~Steve.Blackburn/pubs/papers/urc-oopsla-2003.pdf for details. – Luke Quinane Mar 02 '09 at 12:47
7

The actual answer to this is implementation dependent. The Sun JVM keeps track of some set of root objects (threads and the like), and when it needs to do a garbage collection, traces out which objects are reachable from those and saves them, discarding the rest. It's actually more complicated than that to allow for some optimizations, but that is the basic principle. This version does not care about circular references: as long as no live object holds a reference to a dead one, it can be GCed.

Other JVMs can use a method known as reference counting. When a reference is created to the object, some counter is incremented, and when the reference goes out of scope, the counter is decremented. If the counter reaches zero, the object is finalized and garbage collected. This version, however, does allow for the possibility of circular references that would never be garbage collected. As a safeguard, many such JVMs include a backup method to determine which objects actually are dead which it runs periodically to resolve self-references and defrag the heap.

James
  • 2,050
  • 13
  • 15
6

As a non-answer aside (the existing answers more than suffice), you might want to check out a whitepaper on the JVM garbage collection system if you are at all interested in GC. (Any, just google JVM Garbage Collection)

I was amazed at some of the techniques used, and when reading through some of the concepts like "Eden" I really realized for the first time that Java and the JVM actually could beat C/C++ in speed. (Whenever C/C++ frees an object/block of memory, code is involved... When Java frees an object, it actually doesn't do anything at all; since in good OO code, most objects are created and freed almost immediately, this is amazingly efficient.)

Modern GC's tend to be very efficient, managing older objects much differently than new objects, being able to control GCs to be short and half-assed or long and thorough, and a lot of GC options can be managed by command line switches so it's actually useful to know what all the terms actually refer to.

Note: I just realized this was misleading. C++'s STACK allocation is very fast--my point was about allocating objects that are able to exist after the current routine has finished (which I believe SHOULD be all objects--it's something you shouldn't have to think about if you are going to think in OO, but in C++ speed may make this impractical).

If you are only allocating C++ classes on the stack, it's allocation will be at least as fast as Java's.

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • Hi! Could you explain a bit more regarding what you mean by "When Java frees an object, it actually doesn't do anything at all; since in good OO code, most objects are created and freed almost immediately"? Thanks :) – rinogo Dec 12 '09 at 00:56
  • The first part of the GC area is divided into 2 parts called "Eden". Only 1/2 is used at a time. All it does is fill it from bottom to top. When that area is full, anything that has been de-allocated has been completely forgotten at this point--anything remaining is copied over to the other half and then the first half is considered empty and the other half starts to fill. When an object looks like it'll be around for a while, java moves it from eden to another area and uses a completely different mechanism to track it. – Bill K Dec 12 '09 at 01:37
  • This means that allocating and freeing small objects is about as expensive as C's stack allocation which also doesn't have a specific step for "Freeing". The nice part about Java is the exact same mechanism works for both short-term and long-term objects... The runtime figures out which is appropriate and when to use it, so it can analyze the code and figure out which should go where as quickly as necessary. GC can be finely tuned as well by command-line options. – Bill K Dec 12 '09 at 01:40
  • For reference, this kind of GC is called **stop-and-copy**. Saying that allocating and freeing cost the same with a stop-and-copy GC as with the stack (allocating only increments a counter, freeing is a no-op) is misleading. The GC adds a hidden cost: once in a while, it interrupts the program, follows pointers recursively from old Eden’s roots, copy live objects to the new Eden, and updates all pointers to them. These collection phases happen each time your half of Eden is full. The more you allocate, the more often you trigger it. It *is more costly* than allocating on the stack. – Maëlan Apr 30 '20 at 17:41
4

yes Java Garbage collector handle self-reference!

How?

There are special objects called called garbage-collection roots (GC roots). These are always reachable and so is any object that has them at its own root.

A simple Java application has the following GC roots:

  1. Local variables in the main method
  2. The main thread
  3. Static variables of the main class

enter image description here

To determine which objects are no longer in use, the JVM intermittently runs what is very aptly called a mark-and-sweep algorithm. It works as follows

  1. The algorithm traverses all object references, starting with the GC roots, and marks every object found as alive.
  2. All of the heap memory that is not occupied by marked objects is reclaimed. It is simply marked as free, essentially swept free of unused objects.

So if any object is not reachable from the GC roots(even if it is self-referenced or cyclic-referenced) it will be subjected to garbage collection.

Aniket Thakur
  • 66,731
  • 38
  • 279
  • 289
4

Java collects any objects that are not reachable. If nothing else has a reference to the entry, then it will be collected, even though it has a reference to itself.

Dave L.
  • 43,907
  • 11
  • 63
  • 62
  • So you mean an unreferenced object `new ListContainer()` will be immediately collected? PS: `ListContainer` class come from the question's codes. – Han XIAO Jul 22 '18 at 10:25
  • Typically an expression like `new ListContainer()` will be evaluated, and while the reference is still on the stack, be assigned to a variable or passed as an argument to a method, and so would be referenced for some portion of time. If (or when) it becomes actually unreferenced, then it may be collected in the future. There's no guarantee that it would happen immediately, but only before the JVM runs out of memory. – Dave L. Sep 11 '18 at 21:05
2

Simply, Yes. :)

Check out http://www.ibm.com/developerworks/java/library/j-jtp10283/

All JDKs (from Sun) have a concept of "reach-ability". If the GC cannot "reach" an object, it goes away.

This isn't any "new" info (your first to respondents are great) but the link is useful, and brevity is something sweet. :)

Sam
  • 2,939
  • 19
  • 17