3. Bit on ArrayList, StringBuilder, etc
● Backed by Java array
● Constructor allocates initial 4-16 bytes (depends on implementation)
● When it is filled, it doubles array size and copies the memory
StringBuilder sb = new StringBuilder();
for(FooObj obj : fooList) {
if(sb.length() > 0) sb.append(", ");
sb.append(obj.getData());
}
7. StringBuilder sb = new StringBuilder();
for(FooObj obj : fooList) {
if(sb.length() > 0) sb.append(", ");
sb.append(obj.getData());
}
a b c
8. StringBuilder sb = new StringBuilder();
for(FooObj obj : fooList) {
if(sb.length() > 0) sb.append(", ");
sb.append(obj.getData());
}
a b b , _
a b c
9. StringBuilder sb = new StringBuilder();
for(FooObj obj : fooList) {
if(sb.length() > 0) sb.append(", ");
sb.append(obj.getData());
}
a b c , _ d e
10. StringBuilder sb = new StringBuilder();
for(FooObj obj : fooList) {
if(sb.length() > 0) sb.append(", ");
sb.append(obj.getData());
}
a b c , _ d e , _
11. StringBuilder sb = new StringBuilder();
for(FooObj obj : fooList) {
if(sb.length() > 0) sb.append(", ");
sb.append(obj.getData());
}
a b c , _ d e , _ f g h
12. StringBuilder sb = new StringBuilder();
for(FooObj obj : fooList) {
if(sb.length() > 0) sb.append(", ");
sb.append(obj.getData());
}
a b c , _ d e , _ f g h , _
13. StringBuilder sb = new StringBuilder();
for(FooObj obj : fooList) {
if(sb.length() > 0) sb.append(", ");
sb.append(obj.getData());
}
a b c , _ d e , _ f g h , _
o
a b c , _ d e , _ f g h , _ m n
Copy and GC
14. OkHttp old disk cache code, called by Picasso
for (int x = 0; x < valueCount; x++) {
cleanFiles[x] = new File(directory, key + "." + x);
dirtyFiles[x] = new File(directory, key + "." + x + ".tmp");
}
15. What really happens...
for (int x = 0; x < valueCount; x++) {
StringBuilder b1 = new StringBuilder();
b1.append(key);
b1.append(".");
b1.append(x);
cleanFiles[x] = new File(directory, b1.toString());
StringBuilder b2 = new StringBuilder();
b2.append(key);
b2.append(".");
b2.append(x);
b2.append(".tmp");
dirtyFiles[x] = new File(directory, b2.toString());
}
16. Optimized code
StringBuilder b = new StringBuilder(key);
b.append(".");
int truncateTo = b.length();
for (int x = 0; x < valueCount; x++) {
b.append(x);
cleanFiles[x] = new File(directory, b.toString());
b.append(".tmp");
dirtyFiles[x] = new File(directory, b.toString());
b.setLength(truncateTo);
}
22. Inner classes do not exist, extra method
$ javac Outer.java
$ ls
Outer.class
Outer.java
Outer$Inner.class
$ javap -p Outer.class
class Outer {
Outer();
private static java.lang.String displayText(…);
static java.lang.String access$000(…);
}
23. What javac did? This code does the same
public class Outer {
private static String getStatic(String name) {
return "Hello " + name;
}
}
private class Outer$Inner {
void doSomething(OtherObj other, String name) {
other.setGreeting(Outer.getStatic(name));
}
}
24. WTF is this access$000 method?
$ javap -p -c Outer
class Outer{
...
static java.lang.String access$000(…);
Code:
0: aload_0
1: invokestatic #1 // Method getStatic:…
4: areturn
}
25. Accessor method bomb
class AccessorMethodBomb{
private int count;
private Inner inner;
@Override protected void doSomething(Bundle state) {
inner = new Inner() {
@Override public void blah() {
count = 0;
++count;
--count;
count++;
count--;
...
26. 6 accessors and 2 constructors added
class AccessorMethodBomb {
AccessorMethodBomb();
Object();
protected void doSomething();
static int access$002(AccessorMethodBomb, int); // count = 0 write
static int access$004(AccessorMethodBomb); // ++count preinc
static int access$006(AccessorMethodBomb); // --count predec
static int access$008(AccessorMethodBomb); // count++ postinc
static int access$010(AccessorMethodBomb); // count-- postdec
static int access$000(AccessorMethodBomb); // count read
}
27. Common problem
● Inner classes added to compiler in Java 1.1 with accessor method trick
○ VM is not inner class aware
○ Quick and dirty addition: inner class is really a separate class
○ If there is no access, accessor method is added by javac
● Optimizers do not solve this problem
● Common problem: Facebook app has 5000 avoidable accessor methods
● Similar situation in most 3rd party libs and apps
● Slows down method lookup
● Triggers need for multidex much sooner than needed
29. Moving garbage collector
● Added in ART
● Two compaction modes:
○ Semi-space for low mem devices, Homogenous for normal
● Compacts fragmented memory
● Can’t work when process is active
● Result of it not working: fragmented memory
● Let the app rest, let the compactor compact!
31. Zygote tidbits
● Zygote system service is started by init.rc as parent of all Java apps
● Loads Android framework into memory and waits on a socket
● When app runs, zygote forks, and original one runs the app
● Copy on write - framework memory is read only, so it never changes
● All apps use same Android framework code loaded in RAM
● On PC, every JVM instance loads its own copy of Java framework
● Slow start speed and big memory footprint
● Android runs Java in similar way like servers
33. How does it influence execution speed?
int[] arr = new int[64 * 1024 * 1024];
//does 100% of work and takes measured 80ms
for (int i = 0; i < arr.Length; i++) arr[i] *= 3;
//does 6% of the work and takes measured 78ms
for (int i = 0; i < arr.Length; i += 16) arr[i] *= 3;
36. Memory reference latency
● Register reference: too fast to be measured
● L1 cache reference: 0.5 ns
● L2 cache reference: 7ns
● RAM reference: 100ns
37. As function of step
for (int i = 0; i < arr.Length; i += K) arr[i] *= 3;
38. How to minimize cache misses
● While data is being read, CPU cycles idle away
● Less read latency = less sleep cycles
● L1 cache is 200 times faster than DRAM
● Volume: 32k L1 data cache, 512k-4mb L2 cache
● L2 is shared between cores, so less mide be available
● Keep data in sequential buffers
● OOP Objects tend to scatter data in memory
● CPU reads memory in cache lines of 64 bytes
● Cache misses: bad, cache hits: good
40. NAND flash write endurance
● Stores charge behind floating gate, which degrades on writes
● TLC and MLC flash has 500-3000 write cycles
● Controller tries to spread the writes out to mitigate the effect
● When worn down, device starts getting bad sectors
● Bad sectors cause kernel to remount partition as read-only
● Application that is constantly writing to storage will wear it down
● Try to cache writes, do not flush every byte, if possible use removable
storage which can be replaced