I have this confusion going on with HashMaps. If I have a HashMap let say HashMap. Then I add keys and values to HashMap like this
HashMap.put(1,myObj1);
HashMap.put(2,myObj2);
HashMap.put(3,myObj3);
Now if I do myObj1=null, the value is still available in the HashMap the reason I believe (correct me If I am wrong) myObj1 only contains a pointer (reference) to the place in memory. So in the HashMap the reference that myObj1 had was copied. As myObj1 is now pointing nowhere (null) but the copied value in the hashMap is still pointing to the memory location. So now what kind of reference is this Weak, Strong. Will myObj1 garbage collected if so the value it was pointing to ( the value hasHashMap still points too), what happens to that.
Any Object is candidate to be garbage collected only if no references point to it.
If you do the following:
Object myObj1 = ....
myMap.put(1,myObj1);
myObj1 = null;
You have the internal references from myMap to that myObj1.
When you leave the references to the myMap if no other reference (explicit or implicit) to myObj1 exists it become a candidate to be garbage collected.
So if you explicitly put to null the reference to myMap myObj become a candidate to gc.
Object myObj1 = ....
myMap.put(1,myObj1);
myObj1 = null; // myObj1 is not candidate to be gc
...
myMap = null; // myObj1 and myMap are candidates to be gc
The value in the HashMap implementation always has strong reference. What it means, unless you remove the element/map it won't be garbage collected. There is another implementation in java called WeakHashMap which store the key as weak reference. This key will be garbage collected regardless its removed from Map or not. Please note the value in WeakHashMap still has strong reference.
Related
I've run across a piece of code that I am convinced will cause an inadvertent memory leak:
Object user = getUser("Bob");
Map<String, WeakReference<Object>> map = new HashMap<>();
map.put("Bob", new WeakReference( user ) );
The purpose of this map is to cache the Objects and to have them automatically cleared from the map by the GC when they are no longer strongly referenced.
However, the way I see it is if the key isn't a weak reference as well, then once the Object is GC'ed, there will still be an entry in the hash map with the key pointing to null. Hence the map will still contain the same number of rows, just all pointing to null values.
So in the above example, once all strong references to user are released, and the GC destroys the Object, the entry in the map will be equiv to :
map.put("Bob", null );
So unless there is a cleanup routine that flushes all keys with null values, my map will continue to grow.
So then the question becomes how to fix this? Is there a map construct that I can use which will automatically flush my entry if the value is destroyed?
I contemplated doing something like:
Object user = getUser("Bob");
Map<String, WeakReference<Object>> map = new WeakHashMap<>();
map.put(user.getUsername(), new WeakReference( user ) );
But that seems like a very limited use case where my key has to be an object retrieved from my value. With the WeakHashMap my key cannot be a String constant (ie: "Bob") or there won't be any other references to it, and the GC will clear the object from my map.
Is there some other cache construct that provides all this functionality instead?
You are right in that the collection of the referent does not remove the mapping, however, the result is not equivalent to
map.put("Bob", null );
it will be equivalent to
map.put("Bob", new WeakReference<>(null) );
so you’re not only having a dangling entry instance, but also a dangling cleared WeakReference instance.
When you use
Map<String, WeakReference<User>> map = new WeakHashMap<>();
User user = getUser("Bob");
map.put(user.getUsername(), new WeakReference( user ) );
you get the desired semantics, assuming that user.getUsername() returns a reference to the string instance stored in the User object, to ensure that it stays strongly reachable as long as the User is strongly reachable.
I don’t see any limitation here. Since the string instance stored within the User does exist, there is no overhead in referencing exactly the same string instance as map key as long as the User instance exist. You can still use string constants as lookup key ala User u = map.get("Bob");, as their equality still is determined in terms of String.hashCode() and String.equals(). If you put the mapping using the string constant "Bob" the mapping will usually persist at least as long as the code containing the constant is alive (and each other code that used the same literal during this lifetime), likely the entire application. But where’s the sense in using a different string instance as key than stored in the referent?
Note that WeakHashMap has to deal with the same issue, entries are not removed automatically. It has to use a ReferenceQueue to discover when a referent has been collected to remove its associated entry from the table. This cleanup happens whenever you invoke a method on it, so when you don’t invoke methods on it, the table won’t get cleanup, but since this cleanup happens for every insertion, you are protected against the ever-growing scenario.
if the key isn't a weak reference as well, then once the Object is GC'ed, there will still be an entry in the hash map with the key pointing to null.
With the key pointing to a non-null WeakReference which points to a null referent.
Hence the map will still contain the same number of rows, just all pointing to null values.
No, see above.
The solution is to use a WeakHashMap<String, WeakReference<Object>>, which has a background activity that spots the weak keys being collected, via a ReferenceQueue, and removes the corresponding mappings. Better still, a WeakHashMap<String, WeakReference<User>>. However:
If the keys are string literals they aren't collectable anyway.
It's a bit rich to describe this as a memory leak. The Map itself is a memory leak. If you don't want references to your objects, don't store them in a Map.
Your reasoning is correct.
Map<String,WeakReference<Object>> is used in situations when "lingering" keys do not present a problem, because they are a lot smaller than the objects stored in the map. In situations like that having lots of keys mapped to emptied weak references does not strain the memory resources of the system enough for you to notice.
You can use WeakHashMap<K,V> for situations when you would rather have keys garbage collected, but you are absolutely right about using String constants with it: this would indeed defeat the purpose. Typically, you use a custom key type that overrides equals with reference equality.
Just wondering if that when you set
obj1 = null;
the object is eligible for gc, is it also true that everything with a null value is technically eligible also?
I'm particularly wondering about arrays, say if you have something like
[obj1, obj2, null, obj3, null, obj 4, obj5]
Can the gc run and remove the null object so the elements are empty?
Garbage collection collects objects, it does not collect references to objects. The idea of garbage collecting a null therefor makes no sense. What GC does at the simplest level is notice when an object no longer has any references pointing to it, and at that time it will free up the memory. It doesn't care how many references point to null, it only cares how many incoming references an object has.
SO no, GC will never remove nulls from an array. That's not what it does. Besides which, a null value in an array is perfectly valid and removing it would break many programs.
There is no "null object", and null is the closest thing that exists to an empty reference. The key definitions are in the Java Language Specification, 4.3.1. Objects:
An object is a class instance or an array.
The reference values (often just references) are pointers to these
objects, and a special null reference, which refers to no object.
obj1 = null; changes the reference variable obj1 to be null. If it previously referred to an object it no longer does so.
There may be other references that refer to that object, so that the object remains reachable, and the garbage collector will leave it alone. If obj1 was the last reference to the object, it is no longer reachable, and is eligible for finalization.
This question already has answers here:
When would you use a WeakHashMap or a WeakReference?
(10 answers)
Closed 4 years ago.
WeakHashMap is an implementation of Map interface where the memory of the value object can be reclaimed by Grabage Collector
if the corresponding key is no longer referred by any section of program. So if key is no longer used in program. its Entry
object will be garbage collected irrespective of its usage. Its clear till here
This is different from HashMap where the value object remain in HashMap even if key is no longer referred. We need to explicitly call
remove() method on HashMap object to remove the value. calling remove will just remove the entry from map. Its readyness for GC will
depend whether it is still used somewhere in program or not.
Please find this coding example explaining above
Usage of WeakHashMap over HashMap as per mine understanding
My understanding is we should go for WeakHashMap only when we want to ensure that value object is reclaimed by Grabage Collector when
key is no longer referred by any section of program. This makes program memory efficient Is my understanding correct here?
Usage of WeakHashMap as per JavaDocs , i could spot this statement
This class is intended primarily for use with key objects whose equals
methods test for object identity using the == operator.
I did not get what above statement meant and how it contrast with mine understanding of WeakHashMap usage. Actually i did not get how this statement is related to usage of WeakHashMap?
UPDATE:- on further carefully reading below statement the javadocs
An entry in a WeakHashMap will automatically be removed when its key
is no longer in ordinary use. More precisely, the presence of a
mapping for a given key will not prevent the key from being discarded
by the garbage collector, that is, made finalizable, finalized, and
then reclaimed. When a key has been discarded its entry is effectively
removed from the map, so this class behaves somewhat differently from
other Map implementations.
i am revising my understanding for the benefit of me and others
Usage of WeakHashMap over HashMap as per mine revised understanding
We should go for WeakHashMap only when we want to ensure that key-value pair is removed from map on GC run when key is no longer in ordinary use other than map itself.
Examples are :-
WeakHashMap<Integer, String> numbers = new WeakHashMap<Integer, String>();
numbers.put(new Integer(1), "one");// key only used within map not anywhere else
numbers.put(new Integer(2), "two");
System.out.println(numbers.get(new Integer(1))); // prints "one"
System.gc();
// let's say a garbage collection happens here
System.out.println(numbers.get(new Integer(1))); // prints "null"
System.out.println(numbers.get(new Integer(2))); // prints "null"
Object key = new Object();
m1.put(key, c1);
System.out.println(m1.size());
key = null or new Object() ; // privious key only used within map not anywhere else
System.gc();
Thread.sleep(100);
System.out.println(m1.size());
This is due to the fact that objects will be garbage collected (GCed) when they are no longer have a strong reference from any other part of the program.
Given a WeakHashMap<MyObject, String> then if we do the following:
MyObject mo = new MyObject();
map.put(mo, "Test");
mo = null;
Then the entry mo -> Test will be eligible for GC. This means that if you have a custom .equals implementation that uses some property of MyObject to test for equality then you cannot later do this:
MyObject mo2 = new MyObject();
map.get(mo2);
Because even though your overridden .equals method may say that mo2.equals(mo) == true it is not the case that mo2 == mo and therefore the entry may have already been GCed.
The point is that if you keep a reference to mo and use that to retrieve the value from the Map then it is the case that that reference must == mo and therefore two things are true:
the entry mo -> Test cannot be gced
you can use an == based .equals method to retrieve the entry from the map
Basically; as the GC will use strong references to test whether an object can be GCed it is best to ensure that your .equals method does the same to avoid confusion.
The documentation means that this code is not very useful:
WeakHashMap<Integer, String> numbers = new WeakHashMap<Integer, String>();
numbers.put(new Integer(1), "one");
numbers.put(new Integer(2), "two");
System.out.println(numbers.get(new Integer(1))); // prints "one"
// let's say a garbage collection happens here
System.out.println(numbers.get(new Integer(1))); // prints "null"
System.out.println(numbers.get(new Integer(2))); // prints "null"
This would happen for any class where different instances can be equal. The javadoc is just warning you, in case you hadn't noticed already, that this is not helpful.
Run this test
Object key = new Object();
WeakHashMap m = new WeakHashMap();
m.put(key, 1);
System.out.println(m.size());
key = null;
System.gc();
Thread.sleep(100);
System.out.println(m.size());
though System.gc does not guarantee running GC but on my Oracle's JVM 7 it always runs and this test prints
1
0
which means that GC removed the entry from map because the key is not referenced from anywhere but map itself
I think that this means that the object test for equality be using the objects location in memory and not just because two different instances happen to 'equal' in their values.
If this is not the case the it is difficult to tell if the key is referenced any more and a different, but 'equal'in values, instance might be referenced and used to look up the value.
It might be clearer if it said that the keys have to have reference equality so that you know when the keys are no longer referenced by anything other that the WeakHashMap instance
Building on Evgeniy Dorofeev answer, here is an example with a few gotchas:
import java.util.Map;
import java.util.WeakHashMap;
public class WeakHashMapTest {
public static void main(String[] args) throws InterruptedException {
String key = new String();
Map<String, String> m = new WeakHashMap();
m.put(key, "value");
System.out.println(m.size());
m.put("key", "value");
System.out.println(m.size());
m.put(new String(), "value");
System.out.println(m.size());
m.put(new String("k"), "value");
System.out.println(m.size());
key = null;
System.gc();
Thread.sleep(100);
System.out.println(m.size());
}
}
Output
1
2
2
3
1
I was recently looking into freeing up memory occupied by Java objects. While doing that I got confused about how objects are copied (shallow/deep) in Java and how to avoid accidently clearing/nullifying objects while they are still in use.
Consider following scenarios:
passing a ArrayList<Object> as an argument to a method.
passing a ArrayList<Object> to a runnable class to be processed by a thread.
putting a ArrayList<Object> into a HashMap.
Now in these case, if I call list = null; or list.clear();, what happens to the objects? In which case the objects are lost and in which cases only the reference is set to null?
I guess it has to do with shallow and deep copying of objects, but in which cases does shallow copying happens and in which case does deep copy happens in Java?
Firstly, you never set an object to null. That concept has no meaning. You can assign a value of null to a variable, but you need to distinguish between the concepts of "variable" and "object" very carefully. Once you do, your question will sort of answer itself :)
Now in terms of "shallow copy" vs "deep copy" - it's probably worth avoiding the term "shallow copy" here, as usually a shallow copy involves creating a new object, but just copying the fields of an existing object directly. A deep copy would take a copy of the objects referred to by those fields as well (for reference type fields). A simple assignment like this:
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<String> list2 = list1;
... doesn't do either a shallow copy or a deep copy in that sense. It just copies the reference. After the code above, list1 and list2 are independent variables - they just happen to have the same values (references) at the moment. We could change the value of one of them, and it wouldn't affect the other:
list1 = null;
System.out.println(list2.size()); // Just prints 0
Now if instead of changing the variables, we make a change to the object that the variables' values refer to, that change will be visible via the other variable too:
list2.add("Foo");
System.out.println(list1.get(0)); // Prints Foo
So back to your original question - you never store actual objects in a map, list, array etc. You only ever store references. An object can only be garbage collected when there are no ways of "live" code reaching that object any more. So in this case:
List<String> list = new ArrayList<String>();
Map<String, List<String>> map = new HashMap<String, List<String>>();
map.put("Foo", list);
list = null;
... the ArrayList object still can't be garbage collected, because the Map has an entry which refers to it.
To clear the variable
According to my knowledge,
If you are going to reuse the variable, then use
Object.clear();
If you are not going to reuse, then define
Object=null;
Note:
Compare to removeAll(), clear() is faster.
Please correct me, If I am wrong....
It depends on how many variables are referenciating to each of your objects, to explain this it would be better some code:
Object myAwesomeObject = new Object();
List<Object> myList = new ArrayList<Object>();
myList.add(myAwesomeObject);
myList = null; // Your object hasn't been claimed by the GC just yet, your variable "myAwesomeObject" is still refering to it
myAwesomeObject = null; // done, now your object is eligible for garbage collection.
So it doesn't depend whether you pass your ArrayList as an argument to a method or the like, it depends on how many variables are still refering to your objects.
If you passed an ArrayList to a method then list = null will have no effect if there is a live reference to the list somewhere eg in the calling code. If you call list.clear() anywhere in the code the references to the objects from this list will be nulled. Passing a reference to a method is not shallow copying it is passing reference by-value
Java GC automatically claims the objects when they are not referenced anywhere. So in most cases you will have to set the reference as null explicitly
As soon as the scope of the variable ends the object becomes eligible for GC and gets freed up if no other reference points to the object.
Java is pass by value so if you set the list as null in the method then it will not affect the original reference that was passed to you in the method.
public class A{
private List<Integer> list = new ArrayList<Integer>();
public static void main(String[] args) {
A a = new A();
B b = new B();
b.method(a.list);
System.out.println(a.list.size()); //Will print 0 and not throw NullPointerException
}
}
class B{
public void method(List<Integer> list){
list = null;
//just this reference is set to null and not the original one
//so list of A will not be GCed
}
}
If you put the list into a hash map, the hash map now holds a reference to the list.
If you pass the list as an argument to a method, the method will have a reference to it for the duration of the method.
If you pass it to a thread to be manipulated, the thread will have a reference to the object until it terminates.
In all of these cases, if you set list = null, the references will still be maintained, but they will disappear after these references disappear.
If you simply clear the list, the references will still be valid, but will now point to a list that has suddenly been emptied, by means that may be unknown to the programmer and may be considered a bug, especially if you use the thread.
I was recently looking into freeing up memory occupied by java objects.
A piece of advice.
It is usually a bad idea to think about this. And it is usually a worse idea to try to "help". In 99.8% of cases, the Java garbage collector is able to do a better job of collecting the garbage if you actually just let it get on with it ... and don't waste your effort by assigning null to things. Indeed, the chances are that the fields you are nulling are in objects that are about to become unreachable anyway. And in that case, the GC is not even going to look at the fields that you've nulled.
If you take this (pragmatic) view, all your thinking about shallow versus deep copies and when it is safe to null things is moot.
There is a tiny percentage of cases where it is advisable to assign null ... to avoid medium or long term storage leaks. And if you are in one of those rare situations where it is "recycling" objects is actually a good idea, then nulling is also advisable.
All,
If I were to write a function to delete a node (given headNode and data as input parameters) from a linkedList in Java. I would find the node that has "node.data=data", and delete it by pointing its previous node to its next node *^. My question is, do we have to point the "to be deleted" node to null? to free the memory? or the GC will take care of objects no more accessed in heap.
*^: say A->B->C->D , if B.data=data, then make A->C . is B->Null necessary?
please let me know if its not clear, I will edit it. Thanks.
If you want to delete Node B you just need for A to point to C. The garbage collector will take care of your B nodes as there won't be any references left to it.
The following explanation is quoted from http://javarevisited.blogspot.com/2011/04/garbage-collection-in-java.html
An Object becomes eligible for Garbage collection or GC if its not reachable from any live threads or any static refrences in other words you can say that an object becomes eligible for garbage collection if its all references are null. Cyclic dependencies are not counted as reference so if Object A has reference of object B and object B has reference of Object A and they don't have any other live reference then both Objects A and B will be eligible for Garbage collection.
Generally an object becomes eligible for garbage collection in Java on following cases:
1) All references of that object explicitly set to null e.g. object = null
2) Object is created inside a block and reference goes out scope once control exit that block.
3) Parent object set to null, if an object holds reference of another object and when you set container object's reference null, child or contained object automatically becomes eligible for garbage collection.
4) If an object has only live references via WeakHashMap it will be eligible for garbage collection.
As everyone has said you don't need to set it to null. I just want to add that I had a similar question before which might not be obvious. If you have a doubly linked list, where each node references the previous and the next node, for example A-B-C-D and you remove C-D so that you're left with A-B. You also do not need to worry about C or D even though both of them still have a reference to them (from the other one). Apparently the GC is smart enough to take care of that case as well
No you don't have to set B to null. The Java garbage collector will free the memory for any object not reachable anymore.