Slide 1

Slide 1 text

Advanced techniques for concurrency & memory management +NabilHachicha @nabil_hachicha Droidcon – October 2015

Slide 2

Slide 2 text

Agenda ● Item 1: GC & Reference API ● Item 2: Asynchronous communication ● Item 3: Looper & Handler ● Item 4: Synchronizers ● Q/A

Slide 3

Slide 3 text

Item 1: GC & Reference API

Slide 4

Slide 4 text

WeakHashMap aka LeakMap for some Map map = new WeakHashMap(); Object key = new Object(); map.put(key, "xyz"); key = null;

Slide 5

Slide 5 text

WeakHashMap class Key {} class Value { Key key; } Map map = new WeakHashMap(); Key key = new Key(); Value value = new Value(); map.put(key, value); // this will leak (value is a strong reference) value.key = key; leaking the key

Slide 6

Slide 6 text

WeakHashMap defensive copy (aka copy constructor) com.google.android.gms.maps.model.Polygon polygon = ...; Map, Object> map = new WeakHashMap, Object>(); map.put(polygon.getPoints(), new Object());

Slide 7

Slide 7 text

WeakHashMap literal String as Key WeakHashMap map = new WeakHashMap(); String key = "I'm pooled!"; map.put(key, new Object()); key = null;

Slide 8

Slide 8 text

WeakHashMap Boxed type private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; Map map = new WeakHashMap(); for (int i=0; i < 128; i++) { map.put(i, new Object()); }

Slide 9

Slide 9 text

java.lang.ref.Reference ● Object pointing to another Object (referent) ● Can be GC'ed anytime ● Used to observe the lifecycle of an Object ● Soft, Weak & Phantom for different Use Cases

Slide 10

Slide 10 text

java.lang.ref.Reference Object referent = new Object (); Reference reference = new WeakReference(referent);

Slide 11

Slide 11 text

java.lang.ref.Reference Object referent = new Object (); Reference reference = new WeakReference(referent); referent = null; Runtime.getRuntime().gc(); // Hint to run the GC

Slide 12

Slide 12 text

java.lang.ref.Reference Object referent = new Object (); Reference reference = new WeakReference(referent); referent = null; Runtime.getRuntime().gc(); // Hint to run the GC if (reference.get() == null) { //The garbage collector deleted my referent } else { // referent object is still here }

Slide 13

Slide 13 text

ReferenceQueue ● BlockingQueue ● GC place Reference to it when cleared

Slide 14

Slide 14 text

ReferenceQueue ReferenceQueue queue = new ReferenceQueue(); for (int i = 0 ; i < 100 ; i++) { WeakReference ref = new WeakReference(new Object(), queue); }

Slide 15

Slide 15 text

ReferenceQueue ReferenceQueue queue = new ReferenceQueue(); for (int i = 0 ; i < 100 ; i++) { WeakReference ref = new WeakReference(new Object(), queue); Reference r; while ((r = queue.poll()) != null) { // polling to discover GC'ed referent // reference 'r' cleared } }

Slide 16

Slide 16 text

ReferenceQueue Set> refs = new HashSet>(); ReferenceQueue queue = new ReferenceQueue(); for (int i = 0 ; i < 100 ; i++) { WeakReference ref = new WeakReference(new Object(), queue); refs.add(ref); Reference r; while ((r = queue.poll()) != null) { // polling to discover GC'ed referent // reference 'r' cleared refs.remove(r); } }

Slide 17

Slide 17 text

Finalizer ● Can resurrect the original object ● Require 2 GC cycles ● Slow, one thread performing finalisation

Slide 18

Slide 18 text

PhantomReference ● Always used with a ReferenceQueue ● Signal that the Referent has been finalized ● Referent is always null (prevent resurrection)

Slide 19

Slide 19 text

PhantomReference Scenario: detect a memory leak

Slide 20

Slide 20 text

class Activity { interface Listener {} Service service; Activity(Service service) { this.service = service; } void onStart() { service.registerListener(new Listener() {});//Listener hold a reference to Activity } void onStop () { service.unregisterListener(); service = null; } }

Slide 21

Slide 21 text

class Service { Activity.Listener listener; void registerListener (Activity.Listener listener) { this.listener = listener; } void unregisterListener () { this.listener = null; } }

Slide 22

Slide 22 text

Service service = new Service(); Activity activity = new Activity(service); activity.onStart();

Slide 23

Slide 23 text

Service service = new Service(); Activity activity = new Activity(service); activity.onStart(); ReferenceQueue referenceQueue = new ReferenceQueue(); PhantomReference reference = new PhantomReference(service. listener, referenceQueue);

Slide 24

Slide 24 text

Service service = new Service(); Activity activity = new Activity(service); activity.onStart(); ReferenceQueue referenceQueue = new ReferenceQueue(); PhantomReference reference = new PhantomReference(service. listener, referenceQueue); activity.onStop(); activity = null; // at this point we removed the strong reference to our Activity, service should not leak the Activity & Activity should be GC'd Runtime.getRuntime().gc();

Slide 25

Slide 25 text

Service service = new Service(); Activity activity = new Activity(service); activity.onStart(); ReferenceQueue referenceQueue = new ReferenceQueue(); PhantomReference reference = new PhantomReference(service. listener, referenceQueue); activity.onStop(); activity = null; // at this point we removed the strong reference to our Activity, service should not leak the Activity & Activity should be GC'd Runtime.getRuntime().gc(); Reference ref = referenceQueue.remove(TimeUnit.SECONDS.toMillis(10)); assertNotNull(ref); ref.clear();

Slide 26

Slide 26 text

Write ~deterministic tests Involving GC ● Used by AOSP FinalizationTester.java & LeakCanary lib ● Prefer Runtime.getRuntime().gc(); https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/java/lang/ref/FinalizationTester.java https://github.com/square/leakcanary/blob/master/leakcanary-watcher/src/main/java/com/squareup/leakcanary/GcTrigger.java https://github.com/square/leakcanary/blob/master/leakcanary-watcher/src/main/java/com/squareup/leakcanary/RefWatcher.java

Slide 27

Slide 27 text

IdentityHashMap ● Regular Map

Slide 28

Slide 28 text

IdentityHashMap ● Regular Map ● Reference-equality in place of object-equality for key & value ● Faster if you have a complex hashCode it uses System.identityHashCode()

Slide 29

Slide 29 text

IdentityHashMap ● Regular Map ● Reference-equality in place of object-equality for key & value ● Faster if you have a complex hashCode it uses System.identityHashCode() ● Support mutable key

Slide 30

Slide 30 text

Item 2: Asynchronous models

Slide 31

Slide 31 text

Activity - Service communication ● Activity start service for background work ● Service need to communicate results ● Service may be in another process

Slide 32

Slide 32 text

PendingIntent

Slide 33

Slide 33 text

1. Activity pass the PendingIntent as a Bundle argument (it's Parcelable)

Slide 34

Slide 34 text

1. Activity pass the PendingIntent as a Bundle argument (it's Parcelable) 2. Service invoke PendingIntent.send

Slide 35

Slide 35 text

1. Activity pass the PendingIntent as a Bundle argument (it's Parcelable) 2. Service invoke PendingIntent.send 3. onActivityResult will be called

Slide 36

Slide 36 text

Messenger

Slide 37

Slide 37 text

1. Activity create a Handler

Slide 38

Slide 38 text

1. Activity create a Handler 2. create a messenger from a Handler new Messenger(handler)

Slide 39

Slide 39 text

1. Activity create a Handler 2. create a messenger from a Handler new Messenger(handler) 3. pass the Messenger as a Bundle argument

Slide 40

Slide 40 text

1. Activity create a Handler 2. create a messenger from a Handler new Messenger(handler) 3. pass the Messenger as a Bundle argument 4. Service invoke Messenger.send

Slide 41

Slide 41 text

1. Activity create a Handler 2. create a messenger from a Handler new Messenger(handler) 3. pass the Messenger as a Bundle argument 4. Service invoke Messenger.send 5. Handler will receive the Message

Slide 42

Slide 42 text

ResultReceiver

Slide 43

Slide 43 text

1. Activity create a Handler

Slide 44

Slide 44 text

1. Activity create a Handler 2. create a ResultReceiver using the Handler new ResultReceiver(handler)

Slide 45

Slide 45 text

1. Activity create a Handler 2. create a ResultReceiver using the Handler new ResultReceiver(handler) 3. override onReceiveResult to receive msg

Slide 46

Slide 46 text

1. Activity create a Handler 2. create a ResultReceiver using the Handler new ResultReceiver(handler) 3. override onReceiveResult to receive msg 4. pass the ResultReceiver as a Bundle args

Slide 47

Slide 47 text

1. Activity create a Handler 2. create a ResultReceiver using the Handler new ResultReceiver(handler) 3. override onReceiveResult to receive msg 4. pass the ResultReceiver as a Bundle args 5. Service invoke ResultReceiver.send

Slide 48

Slide 48 text

1. Activity create a Handler 2. create a ResultReceiver using the Handler new ResultReceiver(handler) 3. override onReceiveResult to receive msg 4. pass the ResultReceiver as a Bundle args 5. Service invoke ResultReceiver.send 6. onReceiveResult will be called

Slide 49

Slide 49 text

EventBus ● Don't solve everything with EventBus ● Global state ● YoYo problem ● No IPC https://en.wikipedia.org/wiki/Yo-yo_problem

Slide 50

Slide 50 text

Item 3: Looper/Handler

Slide 51

Slide 51 text

Test Runner ● Test run inside an InstrumentationThread ● InstrumentationThread prepare a Looper but doesn't start looping ● Can't use it with Handler (Message will not be processed)

Slide 52

Slide 52 text

Looper & Unit test Code examples full code here: https://gist.github.com/nhachicha/e993fe9b5f09f0595ccd

Slide 53

Slide 53 text

Item 4: Synchronizers https://gist.github.com/nhachicha/7596e912c81aae38d721

Slide 54

Slide 54 text

CountDownLatch ● Initialize counter with # participants ● await() sleep until counter = 0 ● every thread call countDown()

Slide 55

Slide 55 text

CyclicBarrier ● Similar to CountDownLatch ● await() sleep until all participants reaches the barrier

Slide 56

Slide 56 text

Phaser ● Thread register() to increment # of participants ● Thread arrive() similar to countDown() ● Barrier/Phase is crossed when Number(registered thread) = Number(arrived thread)

Slide 57

Slide 57 text

Conclusion ● Understand your problem, "your architecture is not the list of tools/lib" - Uncle Bob

Slide 58

Slide 58 text

Conclusion ● Understand your problem, "your architecture is not the list of tools/lib" - Uncle Bob ● Understanding implementation & Time/Space complexity

Slide 59

Slide 59 text

Conclusion ● Understand your problem, "your architecture is not the list of tools/lib" - Uncle Bob ● Understanding implementation & Time/Space complexity ● Be opinionated don't follow trends

Slide 60

Slide 60 text

We are hiring https://realm.io/jobs/ Q/A +NabilHachicha @nabil_hachicha