When Java's Garbage collector reclaims memory? - java

I know that Arrays of primitive type are implicitly initialized to 0 in java.
So if I have a code :
public class Foo{
public static void main(String args[]){
int[] arr = new int[50];
for(int i = 0; i < 30; i++){
arr[i] = i;
}
for(int i = 0; i < 30; i++){ // This Line
System.out.println(arr[i]);
}
}
}
I want to know if memory from arr[30] to arr[49] would already have been reclaimed by garbage collector at the line which has the comment?

I want to know if memory from arr[30] to arr[49] would already have been reclaimed by garbage collector at the line which has the comment?
No, absolutely not. The array is a single object. It's either reachable, or it's not reachable... and in this case, it's still reachable.
In a more complex example, if this were a String[], I wouldn't expect the strings referenced by elements arr[30] to arr[49] to be eligible for garbage collection, either. A very smart GC might be able to tell which parts of an array are unused and not include those in GC roots, but I don't think I've ever seen that. (It would have to know that no other code ever had a reference to the array, for one thing. It seems an unusual situation, and therefore one probably not worth optimizing.)

No they would not have been reclaimed - because there is still an active reference to them.
As soon as the garbage collector notices that some objects are not in use anymore, it will mark them for deletion. (A referenced object is an which still has a pointer to it).
After they are marked for deletion - a later cycle of the garbage collection will delete the actual object. This will cause some more free space to appear in those places.
After the objects are deleted - the remaining used space is compacted (so all the empty space sits together, essentially).

Related

Java Garbage Collection regarding class instances

I am a bit confused on how to utilize Java's Garbage Collection to dispose of instances of objects that aren't in use anymore. I have a few questions:
In my game, I generate Cannons without storing them in a variable like so:
new Cannon("down", tileX, tileY, 65);
Will this object always be eligible for garbage collection? If yes, then when will it actually be disposed of?
==
For my Cannon class, I add all instances to a static array list upon creation. This was my first attempt at using the garbage collection:
ArrayList<Cannon> cannonList = Cannon.getCannons();
for (int i = 0; i < cannonList.size(); i++) {
Cannon c = (Cannon) cannonList.get(i);
c = null;
}
for (int i = 0; i < cannonList.size(); i++) {
cannonList.remove(i);
}
System.gc();
When I set "c = null;", does it make the original Cannon to "null", thus making it eligible for garbage collection, or does it make a new reference c to the object and then setting it to null, making it do nothing at all for me?
==
My Cannon class continuously creates instances of the EnemyProjectile class. The EnemyProjectiles class contains a boolean field called "visible". What is the correct way to dispose of my EnemyProjectile class and make it eligible for garbage collection when "visible" is equal to false?
The joy of using Java, is that memory is managed for you behind the scenes so that, unlike C or C++, you don't have to worry about deconstructing or disposing of objects. When an object is no longer "usable" (as defined by falling out of scope and being unreachable from another active object) then the garbage collector quietly reclaims the space it was occupying.
In Java, you cannot control when objects are garbage collected nor should you try.
Code that depends on a deterministic garbage collection of unused objects will invariably be fragile and difficult to maintain in Java. in part this is because different JVM implementations will garbage collect at different times. System.gc(); is, at best, a suggestion to the JVM that it do garbage collection, but is no guarantee when (or even if) it will happen.
The best thing you can do is design your program so that reference variables have the absolute shortest possible lifespan. Your code is at risk of memory leaks any time a long-lived object retains a reference to a short-lived object (listeners are an example) or when you create data structures that "manage memory" themselves (eg. your own queue or stack implementation).
ArrayList<Cannon> cannonList = Cannon.getCannons();
for (int i = 0; i < cannonList.size(); i++) {
Cannon c = (Cannon) cannonList.get(i);
c = null;
}
for (int i = 0; i < cannonList.size(); i++) {
cannonList.remove(i);
}
System.gc();
In this snippet, there are several issues:
When you use a data structure from the Java Collections API, you should use the interface as the type, not the concrete class. This is by convention, but doing so will keep your code more flexible. Instead of...
ArrayList<Cannon> cannonList = Cannon.getCannons();
write this instead (valid for any class instances that implement the List interface):
List<Cannon> cannonList = Cannon.getCannons();
When possible, you should use the enhanced for-each loop introduced with Java SE 5. It is less error prone. Your for loops should look like this:
for (Cannon c : cannonList) {
c = null; // this for loop actually accomplishes no useful work since
// the reference is null'd as soon as it gets a reference to
// a Cannon object. The reference in your ArrayList is unaffected
// by this assignment
}
cannonList.clear(); // more concise than removing each element from the list.
TL;DR: Garbage collection occurs when it happens. It is non-deterministic. You are never guaranteed when or even if it will happen. Design your Java programs to make your objects eligible for garbage collection at the earliest possible time ... and then don't worry at all about what happens.

how does garbage collection with respect to Strings in Java?

I am reading about memory management in JVM and that if an object has no more references to it, it is garbage collected.
lets say, I have a program
public test {
public static void main(String[ ] args) {
String name = "hello";
for (int i =0 ; i < 5; i++) {
System.out.println(i);
}
}
}
As you can see, the String name is not used anywhere, so its reference is kept through out and not garbage collected.
now I have,
String name = "hello"
String name2 = name.substring(1,4)//"ell"
here again, the reference for hello must be present always, and cannot be garbage collected, since name2 uses it.
so when do these String or any objects get garbage collected, which have references but are obsolete, i.e. no longer used in code?
I can see one scenario where trimming down an array causes memory leak and hence setting its reference to null is a good way to garbage collect those obsolete references.
I can see one scenario where trimming down an array causes memory leak
and hence setting its reference to null is a good way to garbage
collect those obsolete references.
Strings are reference types, so all the rules for reference types with respect to garbage collection apply to strings. The JVM may also do some optimizations on String literals but if you're worrying about these, then you're probably thinking too hard.
When does the JVM collect unreferenced objects?
The only answer that matters is: you can't tell and it needn't ever, but if it does you can't know when that will be. You should never write Java code around deterministic garbage collection. It is unnecessary and fraught with ugliness.
Speaking generally, if you confine your reference variables (including arrays or collections of reference types) to the narrowest possible scope, then you'll already have gone a long way toward not having to worry about memory leaks. Long-lived reference types will require some care and feeding.
"Trimming" arrays (unreferencing array elements by assigning null to them) is ONLY necessary in the special case where the array represents your own system for managing memory, eg. if you are making your own cache or queue of objects.
Because the JVM can't know that your array is "managing memory" it can't collect unused objects in it that are still referenced but are expired. In cases where an array represents your own system for managing memory, then you should assign null to array elements whose objects have expired (eg. popped off a queue; J. Bloch, Essential Java, 2nd Ed.).
Technically, the JVM is not required to garbage-collect objects ever. In practice, they usually come behind a little while after the last reference is gone and free up the memory.
First, be aware that constants are always going to be around. Even if you assign a new value to name, the system still has a copy of "hello" stored with the class that it will reuse every time you hit that initializer statement.
However, don't confuse using an object for some sort of calculation with keeping a reference to it forever. In your second example, while "hello" is in fact kept around, that's just because it's living in the constant pool; name2 doesn't have any sort of "hold" on it that keeps it in memory. The call to substring executes and finishes, and there's no eternal hold on name. (The actual implementation in the Oracle JVM shares the underlying char[], but that's implementation-dependent.)
Clearing out arrays is a good practice because it's common for them to be long-lived and reused. If the entire array gets garbage collected, the references it holds get erased (and their objects garbage collected if those were the last ones).
Every variable in Java has a scope: The piece of code during which the variable is defined. The scope of a local variable like name in your example is between the brackets {} it is in. Thus, the name variable will be defined when the thread reaches the String name = "hello"; declaration, and will be kept alive until the main method is finished (because then the brackets the variable was in are closed).
Strings are a different story though than other variables. Strings are cached internally and may not actually be made available for the garbage collector yet.

Java, make sure objects are being deleted when removed from arrayList

Lets say that I'm deleting a "dead" object called "Enemy".
Using something like this:
for(int i = 0; i < enemies.size(); i++)
{
Enemy en = (Enemy) enemies.get(i);
if(en.getVisible() == true)
en.update();
else
enemies.remove(i);
}
Does the object get deleted after being removed from ArrayList? Or "should" it? I've been mainly doing C++ code before and the garbage collection confuses me.
Any way I can see if the objects are being deleted or not by the garbage collector?
Thanks
I really like seeing the opposite of the whole garbage collecting fiasco with C/C++ and Java. Java has it's own garbage collector, you do not need to worry about memory management - .remove() will suffice.
If you remove an object from ArrayList, and that object doesn't have any other reference, then it would be 'eligible' for the garbage collector. After that, you need not worry about removing it from the heap: JVM would do that through automatic garbage collector.
I think it depends on how you entered the object. If you saved it elsewhere it should be still in existence, however if you directly added it to the arrayList it probably is garbage.
P.S. your code needs a correction
enemies.remove(i);
enemies.remove(i--);
Taking my comment about ConcurrentModificationException back. You won't get it, but your loop is still not correct. Take a look:
List<String> list = new ArrayList<String>(Arrays.asList("A","B","C","D"));
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
if ("B".equals(s) || "C".equals(s)) list.remove(i);
}
System.out.println(list);
Output:
[A, C, D]
C is not removed due to i always increasing and skipping elements.
For the most part, you won't need to worry about explicit memory management in Java - as long as there are no other objects referring to them after being removed from the list, the garbage collector will (eventually) remove them.
If the objects in the enemies list are holding onto some system resource or something else that needs to be explicitly disposed of (say a file handle or something), you'll want to clean this up before losing the reference:
Enemy enemy = enemies.remove();
enemy.dispose(); // this is your method to clean up the internals - name it what you want
// continue your loop here
On a note related to your sample code, you'll want to use an Iterator rather than just a for loop iterating over the indexes so you can remove properly without running into issues around the current index and list size. You may also want to consider a different List implementation (such as LinkedList, as insert/remove from the middle of an ArrayList can get expensive if it's big.
For your other question:
Any way I can see if the objects are being deleted or not by the garbage collector?
You could override the finalize method of your class - be careful when you do this though. Also note that there are no guarantees when your objects will be garbage collected - different JVMs often manage memory slightly differently, and often only do garbage collection when it needs more memory.

regarding garbage collection and references

I have a question regarding references and garbage collector in java.
When calling a method with a parameter, let say an array, it is sent a copy of the reference of the array that is considered the parameter.
Hypothesis: the garbage collector is triggered exactly after calling the method, or when executing operations inside the method to the considered array.
Is now the same reference for the array in the calling method and in the called method, regardless of the operations and moves done by the garbage collector (the garbage collector can move the reference from eden to survivor 1)?
A simpler expression for the question: can you rely on this reference copy in order to use it as a mechanism for parameters sent 'by reference'?
Thank you very much!
Roxana
If you're trying to ask whether you can fake pass by reference like this:
// We'd like to pass x by reference...
String x = "hello";
String[] array = { x };
foo(array);
x = array[0];
...
static void foo(String[] array)
{
array[0] = array[0] + "foo";
}
... then yes, that will always work, and isn't affected by garbage collection.
I'd personally try to avoid it, but yes, it'll work :)
Note that unlike with real pass-by-reference, if the method throws an exception then the assignment after the method call won't occur, so you'll lose the "change".
Garbage collector removes object that cannot be accessed by any reference. In your example there are at least 2 references that can be used to access object. Therefore it will not be removed and you can be use references to access it.
Some garbage collectors work by finding objects with no references and reclaiming the space they occupy.
Others work by finding all objects with references, and moving them to a new object space. When all objects have been moved, the old object space is reclaimed. In that case, all the references are updated.

Why does this code sample produce a memory leak?

In the university we were given the following code sample and we were being told, that there is a memory leak when running this code. The sample should demonstrate that this is a situation where the garbage collector can't work.
As far as my object oriented programming goes, the only codeline able to create a memory leak would be
items=Arrays.copyOf(items,2 * size+1);
The documentation says, that the elements are copied. Does that mean the reference is copied (and therefore another entry on the heap is created) or the object itself is being copied? As far as I know, Object and therefore Object[] are implemented as a reference type. So assigning a new value to 'items' would allow the garbage collector to find that the old 'item' is no longer referenced and can therefore be collected.
In my eyes, this the codesample does not produce a memory leak. Could somebody prove me wrong? =)
import java.util.Arrays;
public class Foo
{
private Object[] items;
private int size=0;
private static final int ISIZE=10;
public Foo()
{
items= new Object[ISIZE];
}
public void push(final Object o){
checkSize();
items[size++]=o;
}
public Object pop(){
if (size==0)
throw new ///...
return items[--size];
}
private void checkSize(){
if (items.length==size){
items=Arrays.copyOf(items,2 * size+1);
}
}
}
The pop method produces the memory leak.
The reason is that you only reduce the number of items that are in the queue, but you don't actually remove them from the queue.The references remain in the array. If you don't remove them, the garbage collector, won't destruct the objects, even if the code that produced the object is executed.
Imagine this:
{
Object o = new Object();
myQueue.add(o);
}
Now you have only one reference for this object - the one in the array.
Later you do:
{
myQueue.pop();
}
This pop doesn't delete the reference. If you don't remove the reference the Garbage collector will think that you are still thinking of using this reference and that this object is useful.
So if you fill the Queue with n number of objects then you will hold reference for these n objects.
This is a the memory leak your teachers told you about.
Hint: the leak is in the pop method. Consider what happens to the references to a popped object ...
It's not a priori true that there's a memory leak here.
I think the prof has in mind that you're not nulling out popped items (in other words, after you return items[--size], you probably ought to set items[size] = null). But when the Foo instance goes out of scope, then everything will get collected. So it's a pretty weak exercise.
This example is discussed in Effective Java by Joshua Bloch. The leak is when popping elements. The references keep pointing to objects you don't use.
The code sample doesn't produce a leak. It's true that when you call pop(), the memory isn't freed for the appropriate object - but it will be when you next call push().
It's true that the sample never releases memory. However, the unreleased memory is always re-used. In this case, it doesn't really fit the definition of memory leak.
for(int i = 0; i < 1000; i++)
foo.push(new Object());
for(int i = 0; i < 1000; i++)
foo.pop();
This will produce memory that isn't freed. However, if you ran the loop again, or a hundred thousand million times, you wouldn't produce more memory that isn't freed. Therefore, memory is never leaked.
You can actually see this behaviour in many malloc and free (C) implementations- when you free memory, it isn't actually returned to the OS, but added to a list to be given back next time you call malloc. But we still don't suggest that free leaks memory.
Memory leaks are defined as unbounded growth in allocation caused by ongoing execution.
The explanations provided explain how objects could continue to be held active through references in the stack after popping, and can certainly result in all kinds of misbehaviour (for example when the caller releases what they think is the last reference and expects finalisation and memory recovery), but can hardly be called leaks.
As the stack is used to store other object references the previous orphaned objects will become truly inaccessible and be returned to the memory pool.
Your initial skepticism is valid. The code presented would provide bounded memory use growth with convergence to a long-term state.
Hint: Imagine what happens if you use a Foo object, insert into it 10000 "heavy" items, and then remove all of them using pop() because you don't need them anymore in your program.
I'm not going to flat out give you the answer, but look at what push(Object o) does that pop() doesn't do.
In the pop() method, the item on the size (i.e, items[size-1]) is not set to NULL. As a result, there still exists reference from objects items to items[size-1], although size has been reduced by one. During GC, items[size-1] won't be collected even if there is no other object pointing to it, which leads to memory leak.
Consider this demo:
Foo f = new Foo();
{
Object o1 = new Object();
Object o2 = new Object();
f.push(o1);
f.push(o2);
}
f.pop();
f.pop();
// #1. o1 and o2 still are refered in f.items, thus not deleted
f = null;
// #2. o1 and o2 will be deleted now
Several things should be improved in Foo which will fix this:
In pop, you should set the items entry to null.
You should introduce the opposite to checkSize, something like shrinkSize, which will make the array smaller (maybe in a similar way to checkSize).

Categories