3

I work with large HashMap's and ArrayLists. When not needing them to be in memory any longer, I use myArray.clear(); to free up the memory.

When my colleague saw that line, he changed it to myArray = new ArrayList<>();. He agreed when I asked if he was doing it to let the garbage collector take care of it.

  1. Although I feel it's nice, I felt it decreases readability. Somehow, clear() lets the maintainer know that the array is being cleared. A cursory glance at new ArrayList<>() might make a person think an array is newly being initialized there.
  2. Is the performance improvement really worth it? I saw the source code of ArrayList, and the fact that they are iterating over the list of elements to assign null to them, made me wonder why they couldn't clear the memory by a quicker technique.

Implementation of "clear":

 public void clear() {
     modCount++;
     // clear to let GC do its work
     for (int i = 0; i < size; i++) {elementData[i] = null;}
     size = 0;
 }

The one downside I see to using new ArrayList<>() is that a new set of contiguous locations would have to be allocated in memory. Perhaps that would pose a problem only if there is not enough of memory remaining, before the garbage collector can clear up the

  • Why don't you just assign null if you're done with the array: myArray = null;. That's even more clear. FWIW, you don't even have to assign null unless you're releasing the array and keeping whatever holds the variable. – Erik Eidt Apr 21 '16 at 15:57
  • The cleared array would be filled again with new content. That's why null wasn't used. – Nav Apr 21 '16 at 17:19
4

It seems you missed one important difference between myArray.clear() and myArray = new ArrayList<>(): the first one preserves the capacity of the array, thus not freeing the array memory itself. Only the memory of the objects your array elements were referencing to will be freed (as long as the array holded the only reference to those objects).

So if you want to let the GC free the whole memory, better use myArray = new ArrayList<>(). Of course, the difference will probably be negligible if you immediately fill myArray with a similar number of elements than it was filled before.

Is the performance improvement really worth it?

Well, which performance improvement? It is not inherently clear which of the two approaches will be faster in your use case. The clear method may contain a loop, but if you create a new arraylist which grows over time, not having a preallocated capacity will cause some reallocation, which might result in a measureable performance hit. So without measuring, you cannot tell beforehand which of the two approaches will be faster. For most real world situations, I would expect the difference to be irrelevant, but we do not know your use case, and if performance is important for your case, profile where the bottleneck is, try different approaches, measure and compare them.

| improve this answer | |
2

An important difference between both approaches: If references to the array are stored somewhere else, then after myArray.clear everyone holding a reference will now hold a reference to an empty array. After assigning a new array, everyone still holds a reference to the original array.

| improve this answer | |
1

If the myArray variable will go out of scope soon, don't bother with either approach. If you actually need to clear it, set it to null:

myArray = null

If you are going to reuse the list, clearing it has advantages over creating a new list, because if the list was long once it might likely be long a second time, and clearing it will avoid some internal resizing of the list on future uses. This is a simple time/space trade-off.

I had a memory tight HashMap and ArrayList based data structure before, I found that I could save memory significantly by representing lists (and maps) of length 0, 1, and larger differently.

For zero-length list, I just use the Collections.emptyList() instance. For one-length list, I use Collections.singletonList(); For two or more list, I use ArrayList.

Then, when things are added or removed from the lists, I change the type of list accordingly. Same for maps. I have wrapped-up this behavior into an API, btw, which allows for much tighter memory usage when many maps or lists are empty or only contain a single instance:

Relevant Docs:

https://www.aoindustries.com/docs/aocode-public/com/aoindustries/util/MinimalList.html

https://www.aoindustries.com/docs/aocode-public/com/aoindustries/util/MinimalMap.html

Source JARs (aocode-public): https://www.aoindustries.com/src/

Each manipulation of a list is done through a static method so it can change instances as-needed:

List<T> myList = Collections.emptyList();
// Add to the list
myList = MinimalList.add(myList, elem);

// All uses of the list can seem normal:
for(T elem : myList) { ... }

// Only changes to the list go through the static methods:
myList = MinimalList.remove(index);
| improve this answer | |
1

If you deal with potentially large lists of vastly different sizes that grow and shrink a lot and you are worried about memory allocation, maybe it's a better option to use a LinkedList and clear it at the end.

If your lists mainly have the same size, just clear the ArrayList. That has the slight performance advantage over creating a new list of not having to increase the array size while you're filling it up, which also creates new arrays for the GC to remove. Plus, clearing the list gives the GC slightly less to do as it only has to collect the elements but not the list itself.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.