0

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.

Yash Sampat
  • 30,051
  • 12
  • 94
  • 120
AndroidEx
  • 15,524
  • 9
  • 54
  • 50
  • 1
    I can't answer this comprehensively, but the quote is definitely bogus. Like you said it doesn't create any new objects. As to how expensive references are? I have never worried about that, I don't think it's a huge deal. – ci_ Apr 02 '15 at 20:20
  • 1
    The article states "*the second loop of the code snippet (first example), we are creating a new chunk object for each iteration of the loop*" I don't see any object creation in the second loop. The only difference is that in the second example, you're re-using the same variable and stored `size()` in it's own variable (attempt to reduce method overhead im guessing?). I don't see any object creation in the 2nd loop of *either* examples. I would definitely question that source. – Vince Apr 02 '15 at 20:21

1 Answers1

2

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.

Community
  • 1
  • 1
Yash Sampat
  • 30,051
  • 12
  • 94
  • 120
  • 1
    "*the overhead of repeatedly calling size() is avoided*" That's rarely a problem anymore (for anyone who stumbled across this answer and is wondering). `ArrayList#size` is simply a getter, so the value will be inlined anyways, removing the method call without extra syntax (as long as the compiler used is up to date). – Vince Apr 02 '15 at 20:40
  • I feel sorry for misleading you and other commenters with that optimized loop. At first I was thinking of removing this difference between the two snippets because this part was not questioned by me, but then I left it since it was a quote from the article. Your answer definitely clears the whole thing up, the only part I didn't completely understand is about the compile-time memory allocation on the stack for references. Could you please elaborate this a little bit? How is it possible when for example a number of references is not known at the compile time? – AndroidEx Apr 02 '15 at 20:49
  • Thank you, accepted. @Vince thank you for pointing out this info on inlining, I haven't looked from this perspective on what's effectively being left of optimized loops after proguard's pass. – AndroidEx Apr 02 '15 at 21:27