AndroidEx
AndroidEx

Reputation: 15824

Overhead of short-lived references to existing objects in Java / Android

Recently I have come across an article about memory optimization in Android, but I think my question is more of a general Java type. I couldn't find any information on this, so I will be grateful if you could point me to a good resource to read.

The article I'm talking about can be found here.

My question relates to the following two snippets:

Non-optimal version:

List<Chunk> mTempChunks = new ArrayList<Chunk>();
for (int i = 0; i<10000; i++){
    mTempChunks.add(new Chunk(i));
}

for (int i = 0; i<mTempChunks.size(); i++){
    Chunk c = mTempChunks.get(i);
    Log.d(TAG,"Chunk data: " + c.getValue());
}

Optimized version:

Chunk c;
int length = mTempChunks.size();
for (int i = 0; i<length; i++){
    c = mTempChunks.get(i);
    Log.d(TAG,"Chunk data: " + c.getValue());
}

The article also contains the following lines (related to the first snippet):

In the second loop of the code snippet above, we are creating a new chunk object for each iteration of the loop. So it will essentially create 10,000 objects of type ‘Chunk’ and occupy a lot of memory.

What I'm striving to understand is why a new object creation is mentioned, since I can only see creation of a reference to an already existing object on the heap. I know that a reference itself costs 4-8 bytes depending on a system, but they go out of scope very quickly in this case, and apart from this I don't see any additional overhead.

Maybe it's the creation of a reference to an existing object that is considered expensive when numerous?

Please tell me what I'm missing out here, and what is the real difference between the two snippets in terms of memory consumption.

Thank you.

Upvotes: 0

Views: 527

Answers (1)

Yash Sampat
Yash Sampat

Reputation: 30611

There are two differences:

Non-optimal:

  • i < mTempChunks.size()
  • Chunk c = mTempChunks.get(i);

Optimal:

  • i < length
  • c = mTempChunks.get(i);

In the non-optimal code, the size() method is called for each iteration of the loop, and a new reference to a Chunk object is created. In the optimal code, the overhead of repeatedly calling size() is avoided, and the same reference is recycled.

However, the author of that article seems to be wrong in suggesting that 10000 temporary objects are created in the second non-optimal loop. Certainly, 10000 temp objects are created, but in the first, not the second loop, and there's no way to avoid that. In the second non-optimal loop, 10000 references are created. So in a way it is less than optimal, although the author mistakes the trees for the forest.

Further References:

1. Avoid Creating Unnecessary Objects.

2. Use Enhanced For Loop Syntax.

EDIT:

I have been accused of being a charlatan. For those who say that calling size() has no overhead, I can do no better than quoting the official docs:

3. Avoid Internal Getters/Setters.

EDIT 2:

In my answer, I initially made the mistake of saying that memory for references is allocated at compile-time on the stack. I realize now that that statement is wrong; that's actually the way things work in C++, not Java. The world of Java is C++ upside down: while memory for references is indeed allocated on the stack, in Java even that happens at runtime. Mind blown!

References:

1. Runtime vs compile time memory allocation in java.

2. Where is allocated variable reference, in stack or in the heap?.

3. The Structure of the Java Virtual Machine - Frames.

Upvotes: 2

Related Questions