In objective C, there is a chance that two different references can point to each other.
But is this possible in Java? I mean, can two object references point to each other? If it's possible, when are they going to be garbage collected?
And, In case of nested classes, two objects (inner class's and outer class's) are linked to each other - how are these objects garbage collected?
I assume you are talking about circular references . Java's GC considers objects "garbage" if they aren't reachable through a chain starting at a GC root. Even though objects may point to each other to form a cycle, they're still eligible for GC if cut off from the root.
There are four kinds of GC roots in Java:
Local variables are kept alive by the stack of a thread. This is not a real object virtual reference and thus is not visible. For all intents and purposes, local variables are GC roots.
Active Java threads are always considered live objects and are therefore GC roots. This is especially important for thread local variables.
Static variables are referenced by their classes. This fact makes them de facto GC roots. Classes themselves can be garbage-collected, which would remove all referenced static variables. This is of special importance when we use application servers, OSGi containers or class loaders in general.
JNI References are Java objects that the native code has created as part of a JNI call. Objects thus created are treated specially because the JVM does not know if it is being referenced by the native code or not. Such objects represent a very special form of GC root.
You can also read here for more information.
Yes, you can do this. Like this:
class Pointy {
public Pointy other;
}
Pointy one = new Pointy();
Pointy two = new Pointy();
one.other = two;
two.other = one;
They're garbage collected when both objects are not pointed at by anything other than one another, or other objects which are "unreachable" from current running code. The Java garbage collectors are "tracing" garbage collectors, which means they can discover this sort of issue.
Conversely, reference-counted systems (like Objective C without its "modern" garbage collection -- I don't know what the default is) cannot normally detect this sort of issue, so the objects can be leaked.
Of course you can have objects reference each other. You could simply pass the this pointer in both objects to each other, which is perfectly valid.
However, that doesn't mean that the objects are still accessible from the GC root. Think of it as a (graph) tree. If you cut off a complete branch from the trunk, the whole branch is lost, no matter how many objects are involved or are maintaing references to each other.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Intro:
At university one learns that typical garbage collection roots in Java (and similar languages) are static variables of loaded classes, thread-local variables of currently running threads, "external references" such as JNI handles, and GC-specifics such as old-to-young pointers during Minor GCs of a generational garbage collector. In theory, this does not sound that hard.
Problem:
I am reading the HotSpot source code and was interested in how these garbage collection roots are detected inside the VM, i.e., which methods are used inside the JVM source code to visit all roots.
Investigation:
I found various files (e.g., psMarkSweep.cpp), belonging to various GC implementations, that contain very similar constructs.
Following is the method PSMarkSweep::mark_sweep_phase1 method from psMarkSweep.cpp that I think covers the strong roots:
ParallelScavengeHeap::ParStrongRootsScope psrs;
Universe::oops_do(mark_and_push_closure());
JNIHandles::oops_do(mark_and_push_closure()); // Global (strong) JNI handles
CLDToOopClosure mark_and_push_from_cld(mark_and_push_closure());
MarkingCodeBlobClosure each_active_code_blob(mark_and_push_closure(), !CodeBlobToOopClosure::FixRelocations);
Threads::oops_do(mark_and_push_closure(), &mark_and_push_from_cld, &each_active_code_blob);
ObjectSynchronizer::oops_do(mark_and_push_closure());
FlatProfiler::oops_do(mark_and_push_closure());
Management::oops_do(mark_and_push_closure());
JvmtiExport::oops_do(mark_and_push_closure());
SystemDictionary::always_strong_oops_do(mark_and_push_closure());
ClassLoaderDataGraph::always_strong_cld_do(follow_cld_closure());
// Do not treat nmethods as strong roots for mark/sweep, since we can unload them.
//CodeCache::scavenge_root_nmethods_do(CodeBlobToOopClosure(mark_and_push_closure()));
The following code from psScavenge.cpp seems to add tasks for the different types of GC roots:
if (!old_gen->object_space()->is_empty()) {
// There are only old-to-young pointers if there are objects
// in the old gen.
uint stripe_total = active_workers;
for(uint i=0; i < stripe_total; i++) {
q->enqueue(new OldToYoungRootsTask(old_gen, old_top, i, stripe_total));
}
}
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::universe));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jni_handles));
// We scan the thread roots in parallel
Threads::create_thread_roots_tasks(q);
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::object_synchronizer));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::flat_profiler));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::management));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::system_dictionary));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::class_loader_data));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jvmti));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::code_cache));
Looking at ScavangeRootsTask, we see familiar code similar to the code in psMarkSweep:
void ScavengeRootsTask::do_it(GCTaskManager* manager, uint which) {
assert(Universe::heap()->is_gc_active(), "called outside gc");
PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(which);
PSScavengeRootsClosure roots_closure(pm);
PSPromoteRootsClosure roots_to_old_closure(pm);
switch (_root_type) {
case universe:
Universe::oops_do(&roots_closure);
break;
case jni_handles:
JNIHandles::oops_do(&roots_closure);
break;
case threads:
{
ResourceMark rm;
CLDClosure* cld_closure = NULL; // Not needed. All CLDs are already visited.
Threads::oops_do(&roots_closure, cld_closure, NULL);
}
break;
case object_synchronizer:
ObjectSynchronizer::oops_do(&roots_closure);
break;
case flat_profiler:
FlatProfiler::oops_do(&roots_closure);
break;
case system_dictionary:
SystemDictionary::oops_do(&roots_closure);
break;
case class_loader_data:
{
PSScavengeKlassClosure klass_closure(pm);
ClassLoaderDataGraph::oops_do(&roots_closure, &klass_closure, false);
}
break;
case management:
Management::oops_do(&roots_closure);
break;
case jvmti:
JvmtiExport::oops_do(&roots_closure);
break;
case code_cache:
{
MarkingCodeBlobClosure each_scavengable_code_blob(&roots_to_old_closure, CodeBlobToOopClosure::FixRelocations);
CodeCache::scavenge_root_nmethods_do(&each_scavengable_code_blob);
}
break;
default:
fatal("Unknown root type");
}
// Do the real work
pm->drain_stacks(false);
}
Insight:
This lists of GC roots in the source code look quite a bit larger than what I initially wrote in my first sentence, so I try to list them below, with some comments:
Universe: Okay, the universe. Mostly mirrors of certain classes.
JNI handles: Also clear, handles created via JNI that keep objects alive.
Threads: This one visits the thead-local roots. But here we see the first difference. psMarkSweep.cpp uses a CLDToOopClosure and does something with Code blobs, while psScavange.cpp does not. Afaik, CLD stands for Class loader data, but I don't know why it is used in one case, but not in another. The same accounts for code blobs.
Object Synchronizer: Monitors used for synchronization.
Flat Profiler: A profiler that is part of Hotspot, keeps alive its class loader.
Management: Objects that are kept alive for certain management services, such as MemoryPoolMXBean etc.
JVMTI: Keeps alive JVMTI breakpoints and objects allocated by JVMTI.
System Dictionary: Keeps alive all classes that are loaded by the Java system classloader, and thus the objects referenced by the classes' static fields.
Class Loader Data Graph: This one is a bit unclear to me. I think this are the class loaders that are not the Java system classloader, i.e., this covers classes (and their static fields) that have been loaded by different class loaders?
Code Cache: The code cache contains certain code blobs, but I am still not sure what exactly these code blobs are. It seems a code blob represent information about (compiled) code frames, am I right about this? But I still do not understand why these code blobs are sometimes visited while traversing the thread stacks (as done in psMarkSweep.cpp) and sometimes visited using the CodeCache (as done in psScavenge.cpp).
(Only for Minor GC) Old-to-young roots: Clear.
Questions:
While many things can be found in the source code, I still struggle to understand some of these GC roots, or how these GC roots are found.
What is a code blob? What GC roots does it contain that are not already covered by visiting a thread with an oop closure? What is the code cache?
To gather all thread-local roots: What is the difference between using a CLDToOopClosure and a MarkingCodeBlobClosure in combination with Threads::oops_do (as done in psMarkSweep.cpp), compared to visiting the threads with an oop closure and additionally executing ClassLoaderDataGraph::oops_do and CodeCache::scavenge_root_nmethods_do (as used by psScavenge.cpp).
What is the class loader data graph (compared to the system dictionary)? Is it the collection of the applications class loaders?
What about interned strings, how do they survive GCs? Do they reside somewhere outside the heap, where garbage collection does not affect them?
Do other garbage collectors, e.g., the G1 GC, introduce new types of root pointers? (I don't think that this should be the case)
Remark:
I know this is a veeeery long question with various subquestions, but I think it would have been hard to split it into multiple ones.
I appreciate every posted answer, even if it does not cover an answer to all the questions asked above, even answers to parts of them will help me. Thanks!
As you've already discovered yourself, <Subsystem>::oops_do() is a typical mechanism in HotSpot JVM to visit GC roots of <Subsystem>. Good analysis, by the way. Just keep going through VM sources, and you'll find the answers, as there are plenty useful comments in the code.
Note that the purpose of oops_do is not only to mark reachable objects, but also to process the references themselves, particularly, to relocate them during compaction.
CodeBlob is a piece of generated code. It covers not only JITted methods (aka nmethods) but also various VM stubs and routines generated in runtime.
// CodeBlob - superclass for all entries in the CodeCache.
//
// Suptypes are:
// nmethod : Compiled Java methods (include method that calls to native code)
// RuntimeStub : Call to VM runtime methods
// DeoptimizationBlob : Used for deoptimizatation
// ExceptionBlob : Used for stack unrolling
// SafepointBlob : Used to handle illegal instruction exceptions
These pieces of code may contain embedded references to Heap objects, e.g. String/Class/MethodHandle literals and static final constants.
The purpose of CLDToOopClosure in Threads::oops_do is to mark objects referenced through method pointers not marked otherwise:
// The method pointer in the frame might be the only path to the method's
// klass, and the klass needs to be kept alive while executing. The GCs
// don't trace through method pointers, so typically in similar situations
// the mirror or the class loader of the klass are installed as a GC root.
// To minimze the overhead of doing that here, we ask the GC to pass down a
// closure that knows how to keep klasses alive given a ClassLoaderData.
cld_f->do_cld(m->method_holder()->class_loader_data());
Similarly, MarkingCodeBlobClosure is used to mark objects referenced only from active nmethods:
// In cases where perm gen is collected, GC will want to mark
// oops referenced from nmethods active on thread stacks so as to
// prevent them from being collected. However, this visit should be
// restricted to certain phases of the collection only. The
// closure decides how it wants nmethods to be traced.
if (cf != NULL)
cf->do_code_blob(_cb);
Note that CodeCache::scavenge_root_nmethods_do is not called during marking phase:
// Do not treat nmethods as strong roots for mark/sweep, since we can unload them.
//CodeCache::scavenge_root_nmethods_do(CodeBlobToOopClosure(&mark_and_push_closure));
SystemDictionary is responsible mainly for resolving symbolic names of classes. It does not serve as a GC root for marking (except for Bootstrap and System classloaders). On the other hand, ClassLoaderDataGraph maintains the complete linkset of class loader entities. It does serve as GC root and is responsible for class unloading.
// A ClassLoaderData identifies the full set of class types that a class
// loader's name resolution strategy produces for a given configuration of the
// class loader.
// Class types in the ClassLoaderData may be defined by from class file binaries
// provided by the class loader, or from other class loader it interacts with
// according to its name resolution strategy.
// ...
// ClassLoaderData carries information related to a linkset (e.g.,
// metaspace holding its klass definitions).
// The System Dictionary and related data structures (e.g., placeholder table,
// loader constraints table) as well as the runtime representation of classes
// only reference ClassLoaderData.
//
// Instances of java.lang.ClassLoader holds a pointer to a ClassLoaderData that
// that represent the loader's "linking domain" in the JVM.
Interned strings don't need to survive GC. They are not GC roots. It's OK to unload interned strings unreachable otherwise, and HotSpot actually does that.
A Garbage Collector does not introduce new types of roots itself, but it may use algorithms and data structures that affect the meaning of "reachability". E.g. concurrent collectors may treat all references modified between initial mark and final remark as reachable even if they are not.
As the this post has several questions, I dare to answer a few of them:
1) A code blob is JITed code. It may contain hardcoded (as assembler immediate) object pointers (for example to class objects, or to static finals). If the object is moved, the immediate in the code is adjusted.
2) no idea
3) classloader data are native meta data objects (not within the heap) that may contain references (for example) to loaded classes.
4) interned strings reside in the heap just as regular objects (in old VMs in the perm gen). The only difference is that (for the reason of interning and caching) they are usually never collected and always implicitly alive.
5) To the best of my knowledge, a GC itself should not introduce a new category of GC roots, after all, a GC root as a concept that is GC-independent. However, each GC might store and handle them differently.
EDIT:
Just thought about something else:
2) the VM makes heavy use of closures, which basically means virtual calls. Virtual calls may be expensive however (especially when u do them often, for example for every object and every pointer in the heap), so instead of combining existing closures, the VM often implements specialized closures to avoid unnecessary virtual calls. This may be a reason for it.
1') i just noticed that one might interpret my answer as it being just redundant roots (because class objects and static finals are always referenced from somewhere else too). First, they are not redundant from a GC perspective because the immediates must still be adjusted if the object is moved. And second, the JIT may decide to hardcode the pointer to ANY object, if it for example detects that while interpreting, a specific call ALWAYS returns a pointer to the same object. So a code blob root may be the only root for a specific object.
Consider the following scenario.
You are building a class, in java, where the fundamental semantics of the class demand that no two instances of the class be equal in value unless they are in fact the same object (see instance-controlled classes in Effective Java by Joshua Bloch). In a sense this is like a very large enum (possibly hundreds of millions of "constants") that are not known until runtime. So, to recap, you want the class to ensure that that there are no "equal" instances on the heap. There may be lots of references to a particular object on the heap, but no extraneous equal objects. This can obviously be done in code but it seems to me that there is a major flaw that I have not seen addressed anywhere, including in Effective Java. It seems to me that in order to make this guarantee the instance-controlled class must keep a reference to every instance of itself that has EVER been created at any point during program execution and can NEVER "delete" one of those objects because it can never know that there are no longer any "pointers" to that object (besides the one that it itself keeps). In other words, if you think about this in the context of reference-counting, there will come some point in the program where the only reference to the object is the one held by the class itself (the one that says, "this was created at some point"). At that point you would like to release the memory associated with the object, but you can't because that one pointer that is left has no way of knowing that it is the last one.
Is there a good approach to providing instance-controlled classes which can also free no-longer-needed memory?
Update: So, I think I've found something that might help. It turns out java has a java.lang.ref class that provides weak references. From wikipedia: "A WeakReference is used to implement weak maps. An object that is not strongly or softly reachable, but is referenced by a weak reference is called "weakly reachable". A weakly reachable object is garbage collected in the next collection cycle. This behavior is used in the class java.util.WeakHashMap. A weak map allows the programmer to put key/value pairs in the map and not worry about the objects taking up memory when the key is no longer reachable anywhere else. Another possible application of weak references is the string intern pool. Semantically, a weak reference means "get rid of this object when nothing else references it at the next garbage collection."
You need to use one of the special reference objects, like a weak reference. These were created just to support the use case you mention.
As you create an object, you search your collection of weak references to see if the object already exists; if it does, you return a regular reference to it. If it does not, you create it and return a regular reference, and add a weak reference to it to your collection.
Your weak reference will notify you when it is not used anywhere outside of your collection; you can then remove it from your collection. With no references any where, it can then be garbage collected.
The general concept is called a "canonicalizing cache."
The WeakHashMap class is a shortcut that does some of the plumbing for this for you.
It is not clear what your requirements are. You say you want hundreds of millions of entires. This suggests that a database or NoSQL is the best way to store this.
To ensure you have no duplicates, you can keep track of referenced objects which have been retained with a WeakHashMap.
I know in Java we have concept of soft reference. What if:
1) There is a soft reference "sf" refer to an object A
2) In object A, it has a strong reference refers to object B
3) object A & B are not referenced anywhere else.
By definition, object A and object B are both "softly reachable", right?
Then say we are running out of memory now, GC kicks in. Is it possible that GC will recycle object B but not object A?
If that's the case, later if we want to access object B through "sf", it will be null. How java avoid such case to happen?
I don't see any explain in java doc.
Then say we are running out of memory now, GC kicks in. Is it possible that GC will recycle object B but not object A?
No. The GC will not break strong references in reachable objects. (It will, of course break references in unreachable objects as part of the reclamation process. But you can't observe that happening ... because to observe it, you'd need the object to still be reachable.)
This is a consequence of this statement in the javadoc for the java.lang.ref package.
"Finally, an object is unreachable, and therefore eligible for reclamation, when it is not reachable in any of the above ways."
... where "the above ways" includes strong, soft, weak and phantom reachability.
The two highlighted words mean that eligibility for reclamation is a consequence of being in the unreachable state. Since none of the other states mention reclamation eligibility, we conclude that non-reachability is a precondition for reclamation.
This certainly aligns with common sense. If (hypothetically) the GC was allowed to "null out" strong references in reachable objects, it would be impossible for an application to safely work with objects after they had gotten into this state. (Consider the case where the nulled-out reference was in an instance of a library class ...)
I think a short introduction on how tracing GCs basically work should clear things up.
A tracing GC (the ones used in Java and .NET all fall into that category) has a set of so called root pointers, those are global variables (in java that means static variables of classes) and all live variables in the stackframes. The GC traverses these and marks all objects that are alive, i.e. reachable through references from at least one of the root pointers. When it has finished, all live variables have been marked and the rest can be garbage collected.
A soft reference now can be handled in two different ways: a) we follow the soft reference and mark all objects in it or b) we don't. What exactly happens is at the mercy of the given JVM implementation, but after that has been decided there's no other difference.
Hence there are two possible scenarios:
The GC follows the soft reference to A in which case none of the two objects will be GCed.
The GC doesn't follow the soft reference to A (and there are no hard references to it). A is GCed, B is GCed if no live object has a reference to it.
From the docs:
"An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference."
"An object is strongly reachable if it can be reached by some thread without traversing any reference objects"
I think thats pretty clear. B is softly reachable because it is only reachable by traversing a soft reference.
Said Docs
There is an application with an extensive object graph. This graph mainly consists of a set of subgraphs which are connected to the rest of the graph through the only reference. But internally each such subgraph has some number of cross-references among objects. Once in a while such a sub graph needs to be thrown away. Would it be enough just to set to null the only referece which points to that subgraph to make it eligible for garbage collection?
My concern is that internal cross-references may "protect" the entire subgraph from garbage collection. In other words, is the garbage collector wise enough to figure out that all references in a subgraph do not leave the boundaries of the subgraph and therefore entire subgraph can be purged.
As stated in this SO question, circular reference is well managed.
Java does not do reference counting, it does uses tracing garbage collection (for example mark-and-sweep, copying collection or a some combination thereof). If follows all the active references to find out what objects are "reachable" and then it cleans up everything else.
References in objects not themselves reachable don't affect reachability so it doesn't matter if they are null or not.
About the only case in which setting a reference to null might, conceivably, have a significant effect is in discarding a very large object in the middle of a long running method.
In that case, setting null to the reference of the graph will help making an island of isolation (even for internal circular references) as described in this article.
You will find more details about the unreachable state in The Truth About Garbage Collection:
Unreachable
An object enters an unreachable state when no more strong references to it exist.
When an object is unreachable, it is a candidate for collection.
Note the wording:
Just because an object is a candidate for collection doesn’t mean it will be immediately
collected. The JVM is free to delay collection until there is an immediate need for thememory being consumed by the object.
It’s important to note that not just any strong reference will hold an object in memory. These must be references that chain from a garbage collection root. GC roots are a special class of variable that includes:
Temporary variables on the stack (of any thread)
Static variables (from any class)
Special references from JNI native code
Circular strong references don’t necessarily cause memory leaks.
Consider a code creating two objects, and assigns them references to each other.
public void buidDog() {
Dog newDog = new Dog();
Tail newTail = new Tail();
newDog.tail = newTail;
newTail.dog = newDog;
}
Before the method returns, there are strong references from the temporary stack variables in the buildDog method pointing to both the Dog and the Tail.
After the buildDog method returns, the Dog and Tail both become unreachable from a root and are candidates for collection (although the VM might not actually collect these objects for an indefinite amount of time).
Yes - the garbage collector can cope with circular references etc.
The JVM operates on the notion of "islands of unreachability". If there is an unreachable 'island' of interconnected objects then that set of objects is eligible for garbage collection in its entirety.
Given an aggregation of class instances which refer to each other in a complex, circular, fashion: is it possible that the garbage collector may not be able to free these objects?
I vaguely recall this being an issue in the JVM in the past, but I thought this was resolved years ago. yet, some investigation in jhat has revealed a circular reference being the reason for a memory leak that I am now faced with.
Note: I have always been under the impression that the JVM was capable of resolving circular references and freeing such "islands of garbage" from memory. However, I am posing this question just to see if anyone has found any exceptions.
Only a very naive implementation would have a problem with circular references. Wikipedia has a good article on the different GC algorithms. If you really want to learn more, try (Amazon) Garbage Collection: Algorithms for Automatic Dynamic Memory Management . Java has had a good garbage collector since 1.2 and an exceptionally good one in 1.5 and Java 6.
The hard part for improving GC is reducing pauses and overhead, not basic things like circular reference.
The garbage collector knows where the root objects are: statics, locals on the stack, etc and if the objects aren't reachable from a root then they will be reclaimed. If they are reachable, then they need to stick around.
Ryan, judging by your comment to Circular References in Java, you fell into the trap of referencing objects from a class, which was probably loaded by the bootstrap/system classloader. Every class is referenced by the classloader that loaded the class, and can thus be garbage-collected only if the classloader is no longer reachable. The catch is that the bootstrap/system classloader is never garbage collected, therefore, objects reachable from classes loaded by the system classloader cannot be garbage-collected either.
The reasoning for this behavior is explained in JLS. For example, Third Edition 12.7 http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.7.
If I remember correctly, then according to the specifications, there are only guarantees about what the JVM can't collect (anything reachable), not what it will collect.
Unless you are working with real-time JVMs, most modern garbage collectors should be able to handle complex reference structures and identify "subgraphs" that can be eliminated safely. The efficiency, latency, and likelihood of doing this improve over time as more research ideas make their way into standard (rather than research) VMs.
No, at least using Sun's official JVM, the garbage collector will be able to detect these cycles and free the memory as soon as there are no longer any references from the outside.
The Java specification says that the garbage collector can garbage collect your object
ONLY If it is not reachable from any thread.
Reachable means there is a reference, or chain of references that leads from A to B,
and can go via C,D,...Z for all it cares.
The JVM not collecting things has not been a problem for me since 2000, but your mileage may vary.
Tip: Java serialization caches objects to make object mesh transfer efficient. If you have many large, transient objects, and all your memory is getting hogged, reset your serializer to clear it's cache.
A circular reference happens when one object refers to another, and that other one refers to the first object. For example:
class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
class B {
private A a;
public void setA(A a) {
this.a = a;
}
}
public class Main {
public static void main(String[] args) {
A one = new A();
B two = new B();
// Make the objects refer to each other (creates a circular reference)
one.setB(two);
two.setA(one);
// Throw away the references from the main method; the two objects are
// still referring to each other
one = null;
two = null;
}
}
Java's garbage collector is smart enough to clean up the objects if there are circular references, but there are no live threads that have any references to the objects anymore. So having a circular reference like this does not create a memory leak.
Just to amplify what has already been said:
The application I've been working on for six years recently changed from Java 1.4 to Java 1.6, and we've discovered that we've had to add static references to things that we didn't even realize were garbage collectable before. We didn't need the static reference before because the garbage collector used to suck, and it is just so much better now.
Reference counting GCs are notorious for this issue. Notably, Suns JVM doesn't use a reference counting GC.
If the object can not be reach from the root of the heap (typically, at a minimum, through the classloaders if nothing else0, then the objects will be destroyed as they are not copied during a typical Java GC to the new heap.
The garbage collector is a very sophisticated piece of software -- it has been tested in a huge JCK test-suite. It is NOT perfect BUT there is a very good chance that as long as the java compiler(javac) will compile all of your classes and JVM will instantiate it, then you should be good.
Then again, if you are holding references to the root of this object graph, the memory will NOT be freed BUT if you know what you're doing, you should be OK.