I have a weakly-referenced array of weak references to further objects like so:
public class Foo{
WeakReference<WeakReference<Bar>[]> cache;
}
public class Bar{
private final WeakReference<Bar>[] ownerCache;
}
The array itself is weakly referred for reasons I will not go into right now. I want to make sure it is not garbage collected before ANY of the Bar objects reachable from it. In other words, it must exist in memory as long as any Bar object exists that can be reached from it. Then, if no Bar objects exist any more, I am better off if the array is also garbage collected. (Bar objects may or may not be strongly reachable from elsewhere.) I did this by referring to the cache array in a field inside all Bar objects. If the field is sufficient in making the array strongly reachable, it is not garbage collected. However, my code never actually uses that field and I can not make it public. (I get the "unused" warning on it.) I am afraid that the existence of such field is terminated either during compile time or run time, or it could get special treatment from the garbage collector that I am unaware of.
Is this the right solution? Does this solution achieve what I want regardless of the garbage collector or JVM implementation? If not, what would be a better method?
Here are a couple of ideas.
If you control the Bar class, and each instance is referenced by no more than one array, you could add a reference from a Bar instance to the array. Reachable Bar instances will prevent the array from being collected.
Alternatively, you could:
Construct a reference queue for weak references to Bar instances.
ReferenceQueue<Bar> m_refQueue = new ReferenceQueue<>();
Construct each WeakReference with a reference to that queue.
new WeakReference<Bar>( myBar, m_refQueue );
Periodically poll that queue for available collectable instances, and remove them from your collection.
You could make the collection itself a resizable data structure, avoiding the need to collect it.
public class Foo {
final #Nonnull List<WeakReference<Bar>> cache = new ArrayList<>();
// Or you could use an IdentityHashSet from a third-party library.
}
EDIT
As suggested by #Holger below, if an ordered list of references is not needed, your collection of WeakReference can be a java.util.WeakHashMap, used as a set. The keys are weak references; the values can be null. The map is a resizable data structure, so you can simply hold an ordinary reference to the map.
public class Foo {
final #Nonnull WeakHashMap<WeakReference<Bar>,Object> cache
= new WeakHashMap<>();
Related
There is a WeakHashMap instance initialized with, for example, 500 entries. Now, its keys have not been referenced anywhere in the running application for a day or so. Will this map's entries be removed automatically after a certain time gets passed?
My understanding is that if key is not referenced then corresponding entries will be removed from the map.
It will be removed when GC runs if your key is not referenced anywhere: (Reference)
Hash table based implementation of the Map interface, with weak keys.
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.
The removal time is unknown:
Each key object in a WeakHashMap is stored indirectly as the referent
of a weak reference. Therefore a key will automatically be removed
only after the weak references to it, both inside and outside of the
map, have been cleared by the garbage collector.
But be careful, some object like boxed Integer of small integers like -127-> 127 are cached by JVM so if you use autoboxed Integer key, it will never be removed from the Map.
Well, first we narrow down the question.
QUESTION: We have a WeakHashMap in which we have some entries. Will those entries will be garbage collected if the entries are not being used?
Ref code:
WeakHashMap<Object, Object> wkMap = new WeakHashMap<>()
Object obj1 = new Object();
Object obj2 = new Object();
Objcet obj1Meta = new Object();
Objcet obj2Meta = new Object();
wkMap.put(obj1, obj1Meta);
wkMap.put(obj2, obj2Meta);
First of all, it's not about being used, neither it has any relation with time: it's about whether the reference to the map (wkMap in this case) is in scope; if not, then the entire map is eligible for garbage collection, that's quite straightforward. But if not, then...
One thing we need to check is whether the objects which are weakly referenced by the keys of the map are already garbage collected or not. In this case obj1 and obj2.
If these objects have not been garbage collected, then their corresponding entries will be there in the map. Garbage collector is not going to reclaim. Again straightforward.
Now the tricky case: the objects referenced weakly by obj1, obj2 have been garbage collected. There is no need of their metadata present in the map wkMap. Ideally they should be garbage collected, and eventually they are. But the question is how...
Step by Step
The objects referenced weakly by obj1, obj2 become eligible for garbage collection
The garbage collector collects the objects; at this point, the garbage collector checks whether there are any weak references to the object it's collecting. In our case we have two: keys of two entries in the weak hash map wkMap.
If GC finds some weak references to the object it's collecting, it then checks whether those references have any ReferenceQueue attached to it. If there is any then GC puts the weak reference to that ReferenceQueue. GC is done.
Until now, the entries are there in the map and they are not eligible for garbage collection. And it will be there in the map until someone manually makes the keys set to null. Wait, then who does that? Let's see next:
That manual clean-up is done by WeakHashMap itself. Let us check the size() code inside WeakHashMap:
public int size() {
if (size == 0)
return 0;
expungeStaleEntries();
return size;
}
Concentrate on expungeStaleEntries(); it is the one which removes all the entries from the map which are there in the ReferenceQueue as well, and the entries become eligible for garbage collection (a single reference queue is attached to all the weak references used as a key in the map). Check expungeStaleEntries() code as well.
Now in a nutshell, if from your code you call some method on the WeakHashMap, which internally calls this expungeStaleEntries() method, only then will the entries become eligible for garbage collection.
List of methods which call expungeStaleEntries()
size()
reSize()
isEmpty()
putAll()
etc...
Hope this makes things clear.
Will this map's entries be removed automatically after a certain time gets passed?
It depends on when the Garbage Collector comes. There is no guarantee that it reclaims "garbage" even once a day.
The behavior of the WeakHashMap class depends in part upon the actions of the garbage collector, so several familiar (though not required) Map invariants do not hold for this class. Because the garbage collector may discard keys at any time, a WeakHashMap may behave as though an unknown thread is silently removing entries.
JDK 10 - WeakHashMap
In this code
WeakHashMap<Group,String> map = new WeakHashMap<>();
Group group1 = new Group();
Group group2 = new Group();
map.put(group1,"one");
map.put(group2,"two");
System.out.println(map);
group1 = null;
System.gc();
System.out.println(map);
In the first print statement , you will see that hashmap has two elements and in the second print statement it will have only one element. Because the reference to the first element is now null. So yes all keys whose references point to null will get removed the next time GC runs.
{Group#53e25b76=one, Group#73a8dfcc=two}//First print
{Group#73a8dfcc=two}//Second print
I hava a test application with one class that stores a map and a button that invokes method of that class:
Map<Object, Object> weakMap = new WeakHashMap<Object, Object>();
The button does this:
public void fillWeakHashMap(int size) {
List<String> createObjects = (List<String>) createObject(size);
for (Object ob : createObjects) {
weakMap.put(ob, ob);
}
}
So the main class with the map lives but objects which I put to it are created in separate method and since it is a WeakHashMap I thought that after exit from the method fillWeakHashMap the keys / objects in map would be cleared by the GC.
I opened the VisualVM->VisualGC and clicked Monitor->PerformGC 10 times and the Old space is almost full meaning that GC isnt clearing those objects. So what am I doing wrong?
The first sentence of WeakHashMap's javadoc says:
Hash table based implementation of the Map interface, with weak keys. 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.
and somewhat further down, it writes:
The value objects in a WeakHashMap are held by ordinary strong references. Thus care should be taken to ensure that value objects do not strongly refer to their own keys, either directly or indirectly, since that will prevent the keys from being discarded.
That is, only the keys are weakly referenced, but the values are strongly referenced. In your code, each key is also used a value, therefore strongly referenced, and therefore not garbage collected.
You are keeping a reference for each object in the map like ob is still referring itself that is why nothing is being cleared, since GC cant claimed that those objects are not is use and can be cleaned.
I have the following code:
result = binding.downloadData(sourceURLString.replace("{CAT_ID}", catId), Data.class);
ArrayList<Data> mAllProducts = result.getProducts();
cloneList(mAllProducts);
System.gc();
And here is the deep copy of the mAllProducts ArrayList
static List<Data> clone;
public static void cloneList(ArrayList<Data> list) {
clone = new ArrayList<Data>();
for(Data item: list){
clone.add(new Data(item));
}
}
Data Constructor:
public Data(Data item2) {
this.imageUrl = item2.imageUrl;
*
*
}
My questions are:
Will the mAllProducts arraylist collected by the garbage collector?
Is the clone list a passed by value ArrayList?
If the answer at the 2nd question is yes, that means that the clone arraylist doesn't have a reference to the memory?
And finally, if the answer at the second question is yes, that means that will stay at the memory only for the time is being used by the system and then will be garbage collected?
1) No way to know, your gc call is merely a suggestion that the JVM try to perform a collection.
2) Everything in Java is pass by value.
3) I don't know what you mean. But your clone, assuming it creates new items for the list, and the items don't share references to any objects, is completely separate from the original list. Primitive values like ints are immutable, it's only object instances you have to worry about. It seems you are using a copy constructor, so be extra careful you copy any objects each item contains, as well as any items those children might contain; your copy needs to be deep.
4) I don't know what you mean. If you don't have any references to the original it will be eligible for collection the next time the GC runs.
Will the mAllProducts arraylist collected by the garbage collector?
Only when 1) The garbage collector decides to do so and 2) When it falls out of scope
Is the clone list a passed by value ArrayList?
Yes
If the answer at the 2nd question is yes, that means that the clone arraylist doesn't have a reference to the memory?
Definitely needs a reference to some point in memory, else it can't exist in a logical system i.e. a computer.
And finally, if the answer at the second question is yes, that means that will stay at the memory only for the time is being used by the system and then will be garbage collected?
Again the garbage collector will collect it when it is deemed fit to do so.
When an array of objects is not referenced anymore, does the objects in that array are garbage collected too? (assuming no variables are referencing the elements)
In this page, http://java.sys-con.com/node/37613
it says -
"The biggest danger is placing an object into a collection and forgetting to remove it. The memory used by that object will never be reclaimed."
If you make sure to nullify the references, why will that memory be unclaimed?
Thanks
When an array of objects is not referenced anymore, does the objects
in that array are garbage collected too? (assuming no variables are
referencing the elements)
Yes.
"The biggest danger is placing an object into a collection and
forgetting to remove it. The memory used by that object will never be
reclaimed."
This is when you are holding a reference to the collection. For example, if you have a Map in which you put a key-value and then forget to remove then it stays there for ever. Think http sessions, if you use something in ServerContext or some such at start of request using session id as key but fail to remove it at end of the request processing..
For the first question, the answer is yes, absolutely: the objects inside non-referenced array and no other references do get garbage collected.
As for the second question, the document talks about placing forgetting an object inside a referenced collection, for example a cache of some sort, a static field, a thread-local store, etc.
It won't be unclaimed if nothing references it. The article says that if you nullify a reference, but the object is still in a referenced collection (hence referenced), it won't be collected.
Generally speaking, anything that's not referenced is garbage collected. So, yes, those objects would be garbage collected.
Also, note that:
An array is not a collection.
I think that what the person that wrote that meant was make sure you
remember all of the places you're referencing an object, so that if
you intend to remove it, it gets removed (and there are no lingering
references to it).
Below is the code
ArrayList arList = someMethod();// returning ArrayList with customDO objects
Now somewhere in different class I am getting data from this arList
CustomDo custDO= (CustomDO)arList.get(0);
Will the arList be alive as long as custDO is alive ? If yes, will below piece of code help
CustomDO custDO = ((CustomDO)arList.get(0)).cloneMe();
// where cloneMe has defintion as return ((CustomDO)super.clone());
// CustomDo implements Cloneable
Is there a better way to keep a copy of only the first element of arList and discard the list so that it can be collected by garbage collector ?
Is there a better to keep a copy of only the first element of arList and discard the list so that it can be collected by garbage collector ?
You don't have to make a copy of the list element. As long as you have another reference to it, it will not be garbage-collected, even if the list you got it from is. And the list will be garbage-collected as soon as you remove all references to it.
There is no need in Java to clone anything just to make sure that the object does not disappear. In Java a reference to an object is always valid. It cannot happen that the data for a live reference gets invalid.
You only want to make a copy (clone) if you are afraid that other people who reference the same object might change its contents (calling some setter on it) in ways that would cause trouble for you (or you want to have a private copy to change it without affecting others).
// reference to first object
CustomDO custDO = ((CustomDO)arList.get(0));
// let arList be garbage collected
arList = null;
Another thing you should know is that Collections clone() methods do a shallow (flat) copy. Sometimes you need to have deep copies (to allow modifing them independedly)
As long as you have access to CustomDO custDO object, it will not be garbage collected. The list can be garbage collected if there is no reference to it.
The ArrayList is an ordinary Object, and only references to this object will keep the list alive. Of course, as long as the list is alive, all its elements are also alive, but the converse does not hold.