From c3839d3242f6c1b38a83268dd03b78e40e6a51ab Mon Sep 17 00:00:00 2001
From: Elliott Hughes
Object creation is never free. A generational GC with per-thread allocation pools for temporary objects can make allocation cheaper, but allocating memory is always more expensive than not allocating memory.
If you allocate objects in a user interface loop, you will force a periodic -garbage collection, creating little "hiccups" in the user experience.
+garbage collection, creating little "hiccups" in the user experience. The +concurrent collector introduced in Gingerbread helps, but unnecessary work +should always be avoided.Thus, you should avoid creating object instances you don't need to. Some examples of things that can help:
A somewhat more radical idea is to slice up multidimensional arrays into @@ -119,7 +122,7 @@ parallel single one-dimension arrays:
generally much better than a single array of custom (Foo,Bar) objects. (The exception to this, of course, is when you're designing an API for other code to access; in those cases, it's usually better to trade - correct API design for a small hit in speed. But in your own internal + good API design for a small hit in speed. But in your own internal code, you should try and be as efficient as possible.) @@ -127,6 +130,7 @@ parallel single one-dimension arrays: can. Fewer objects created mean less-frequent garbage collection, which has a direct impact on user experience. +(See also Effective Java item 46.)
- -Enums are very convenient, but unfortunately can be painful when size -and speed matter. For example, this:
- -public enum Shrubbery { GROUND, CRAWLING, HANGING }
-
-adds 740 bytes to your .dex file compared to the equivalent class -with three public static final ints. On first use, the -class initializer invokes the <init> method on objects representing each -of the enumerated values. Each object gets its own static field, and the full -set is stored in an array (a static field called "$VALUES"). That's a lot of -code and data, just for three integers. Additionally, this:
- -Shrubbery shrub = Shrubbery.GROUND;- -
causes a static field lookup. If "GROUND" were a static final int, -the compiler would treat it as a known constant and inline it.
- -The flip side, of course, is that with enums you get nicer APIs and -some compile-time value checking. So, the usual trade-off applies: you should -by all means use enums for public APIs, but try to avoid them when performance -matters.
- -If you're using Enum.ordinal, that's usually a sign that you
-should be using ints instead. As a rule of thumb, if an enum doesn't have a
-constructor and doesn't define its own methods, and it's used in
-performance-critical code, you should consider static final int
-constants instead.
Consider the following class definition:
public class Foo {
+ private class Inner {
+ void stuff() {
+ Foo.this.doStuff(Foo.this.mValue);
+ }
+ }
+
private int mValue;
public void run() {
@@ -313,24 +292,19 @@ constants instead.
private void doStuff(int value) {
System.out.println("Value is " + value);
}
-
- private class Inner {
- void stuff() {
- Foo.this.doStuff(Foo.this.mValue);
- }
- }
}
-The key things to note here are that we define an inner class (Foo$Inner) -that directly accesses a private method and a private instance field -in the outer class. This is legal, and the code prints "Value is 27" as -expected.
+The key things to note here are that we define a private inner class
+(Foo$Inner) that directly accesses a private method and a private
+instance field in the outer class. This is legal, and the code prints "Value is
+27" as expected.
The problem is that the VM considers direct access to Foo's private members -from Foo$Inner to be illegal because Foo and Foo$Inner are different classes, -even though the Java language allows an inner class to access an outer class' -private members. To bridge the gap, the compiler generates a couple of -synthetic methods:
+The problem is that the VM considers direct access to Foo's
+private members from Foo$Inner to be illegal because
+Foo and Foo$Inner are different classes, even though
+the Java language allows an inner class to access an outer class' private
+members. To bridge the gap, the compiler generates a couple of synthetic
+methods:
/*package*/ static int Foo.access$100(Foo foo) {
return foo.mValue;
@@ -339,22 +313,19 @@ synthetic methods:
foo.doStuff(value);
}
-The inner-class code calls these static methods whenever it needs to -access the "mValue" field or invoke the "doStuff" method in the outer -class. What this means is that the code above really boils down to a case -where you're accessing member fields through accessor methods instead of -directly. Earlier we talked about how accessors are slower than direct field +
The inner class code calls these static methods whenever it needs to
+access the mValue field or invoke the doStuff method
+in the outer class. What this means is that the code above really boils down to
+a case where you're accessing member fields through accessor methods.
+Earlier we talked about how accessors are slower than direct field
accesses, so this is an example of a certain language idiom resulting in an
"invisible" performance hit.
We can avoid this problem by declaring fields and methods accessed -by inner classes to have package scope, rather than private scope. -This runs faster and removes the overhead of the generated methods. -(Unfortunately it also means the fields could be accessed directly by other -classes in the same package, which runs counter to the standard -practice of making all fields private. Once again, if you're -designing a public API you might want to carefully consider using this -optimization.)
+If you're using code like this in a performance hotspot, you can avoid the +overhead by declaring fields and methods accessed by inner classes to have +package access, rather than private access. Unfortunately this means the fields +can be accessed directly by other classes in the same package, so you shouldn't +use this in public API.