3

I am currently watching a video regarding android code optimization (https://www.youtube.com/watch?v=w9taB0yUwjs)

In this video, he is optimizing the following code:

List<Contact> contacts = new ArrayList<Contact>();

if (cursor.moveToFirst()) {
    do {
        Contact contact = new Contact(...);
        contacts.add(contact);
    while(cursor.moveToNext());
}

He suggests that the following would free up memory.

List<Contact> contacts = new ArrayList<Contact>();

if (cursor.moveToFirst()) {
    do {
        contacts.add(new Contact(...));
    while(cursor.moveToNext());
}

I do not quite understand why this would free up memory. My (limited) understanding is that the contact variable would just be an object reference stored on the stack. Would creating the objects anonymously actually produce a significant reduction in memory usage? From reading this answer it seems an object reference takes up a mere 4 - 8 bytes.

Am I missing something here?

Community
  • 1
  • 1
user184994
  • 17,791
  • 1
  • 46
  • 52
  • 1
    I don't think that can be called an optimization, but just reducing the code. – Luiggi Mendoza Jun 20 '14 at 21:20
  • I think you're right. I can't see how that would help memory usage, either. `contact` is scoped locally to the loop, so the variable reference would get thrown away and the object kept in both cases. – azurefrog Jun 20 '14 at 21:22
  • As you noted, an object reference takes up 4 or 8 bytes, which is insignificant however you look at it for today's electronics. Whoever made the video has no idea what he is talking about in this case. – awksp Jun 20 '14 at 21:25
  • Thanks. I've only been teaching myself to code for about a year, and up to this point I have made no effort to optimize my code (I've just been focusing mostly on object-oriented principles). It seemed to make little sense, but I didn't want to discount what he was saying without checking first. – user184994 Jun 20 '14 at 21:29
  • Generally optimizing before you take measurements to figure out what you *actually* need to optimize isn't a good idea. Of course, there are some caveats -- try to always pick good algorithms/data structures for your problem to avoid performance problems -- but those are more design decisions that happen to have performance impacts. If you find yourself worrying about this kind of minor thing, try to take a step back, perhaps profile a run or two, and see if it's really necessary. – awksp Jun 20 '14 at 22:53
  • @user3580294 I started writing a new Android application about 6 weeks ago. It's coming along really well, but I did a profile on it earlier, and noticed that CPU usage was far higher than I would've liked (about 15-20%), which lead me to this video (although it's mostly about memory usage, not CPU usage). I think some degree of optimization is in order, but as I say, having spent no effort in learning these design decisions, I'm not really sure where to start, or which parts are based on bad decisions. – user184994 Jun 20 '14 at 23:03
  • When you profiled your code, did the profiler tell you what methods ate up the most CPU? Also, most decent profilers can break down the heap and tell you what objects are in memory. – awksp Jun 20 '14 at 23:08

4 Answers4

4

This might be oversimplified, but it'll give you the basic idea.

Here's a test class comparing a method with an extra reference and an equivalent method with an inlined call:

public class Test {
    static List<String> list = new ArrayList<>();

    public static void extraReference() {
        String s = new String();
        list.add(s);
    }

    public static void noReference() {
        list.add(new String());
    }
}

Here's the bytecode for the methods, in the same order they were declared:

public static void extraReference();
    Code:
       0: new           #2                  // class java/lang/String
       3: dup           
       4: invokespecial #3                  // Method java/lang/String."<init>":()V
       7: astore_0      
       8: getstatic     #4                  // Field list:Ljava/util/List;
      11: aload_0       
      12: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      17: pop           
      18: return        

  public static void noReference();
    Code:
       0: getstatic     #4                  // Field list:Ljava/util/List;
       3: new           #2                  // class java/lang/String
       6: dup           
       7: invokespecial #3                  // Method java/lang/String."<init>":()V
      10: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      15: pop           
      16: return 

If you look carefully, the only difference is an extra reference store/load instruction in the bytecode.

Now, if this code were executed as-is, you might notice a difference after a lot of calls -- such as in a loop. Extra CPU cycles might be burned, and you have to use a place on the stack to store the reference (which shouldn't bother the GC at all since the GC deals only with the heap, and items on the stack are freed automatically, from this answer). But I wouldn't call the cost significant.

However, there's the magical entity known as the JIT compiler present on virtually every JVM (and the Dalvik VM that Android uses, if memory serves). The JIT compiler has the ability to inline the extra reference, so the code with the extra reference essentially becomes exactly the same as the code without the extra reference. This should be an relatively easy optimization for the JIT compiler to execute, especially for more modern VMs.

So in the end, if there is a difference at all you can safely ignore it due to the JITC. You should pick the code style that is more readable to you, in this case.

awksp
  • 11,764
  • 4
  • 37
  • 44
  • Thanks, by far the most in-depth explanation. I'm actually reading [about the Dalvik JIT compiler](http://android-developers.blogspot.co.uk/2010/05/dalvik-jit.html) now – user184994 Jun 20 '14 at 23:26
  • @user184994 No problem! Minor update for a reference as to how items on the stack are deal with, but otherwise it should be good. I'll be checking out that article too, as I'm curious how much a JIT compiler can do would work without the resources that a desktop computer has. – awksp Jun 20 '14 at 23:31
1

In the video he mentions getting rid of the reference of the object, changing from

if (cursor.moveToFirst()) {
    do {
        Contact contact = new Contact(...);
        contacts.add(contact);
    while(cursor.moveToNext());
}

to

Contact contact = null;
if (cursor.moveToFirst()) {
    do {
        contact = new Contact(...);
        contacts.add(contact);
    while(cursor.moveToNext());
}

and then to

if (cursor.moveToFirst()) {
    do {
        contacts.add(new Contact(...));
    while(cursor.moveToNext());
}

In any case, I think he meant (I can't be sure) that it was an improvement on readability, since the memory would be consumed on the creation of the object, not on the variable allocation.

Alexandre Santos
  • 8,170
  • 10
  • 42
  • 64
0

With that change the code looks more 'optimized' to some, but no it will not significantly reduce memory usage.

Tom McGee
  • 136
  • 6
0

Optimization may occur also on compiler side, which is not revealed by reading the code. Any form of the code here doesn't change the object life cycle, so for me no optimization, just less code.

tonioc
  • 111
  • 4