Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Droid Devs CPH - October 2015

Droid Devs CPH - October 2015

Advanced techniques for concurrency & memory management

Nabil Hachicha

October 10, 2015
Tweet

More Decks by Nabil Hachicha

Other Decks in Programming

Transcript

  1. Agenda • Item 1: GC & Reference API • Item

    2: Asynchronous communication • Item 3: ThreadPool • Item 4: Looper & Handler • Q/A
  2. WeakHashMap aka LeakMap for some Map<Object, String> map = new

    WeakHashMap<Object, String>(); Object key = new Object(); map.put(key, "xyz"); key = null;
  3. WeakHashMap ~ Same as Map<Object, String> map = new HashMap<Object,

    String>(); WeakReference<Object> key = new WeakReference<Object>(key); map.put(key, "xyz"); key = null;
  4. WeakHashMap class Key {} class Value { Key key; }

    Map<Key, Value> map = new WeakHashMap<Key, Value>(); Key key = new Key(); Value value = new Value(); map.put(key, value); // this will leak value.key = key; pb1: leaking the key
  5. WeakHashMap pb2: defensive copy (aka copy constructor) com.google.android.gms.maps.model.Polygon polygon =

    ...; Map<List<LatLng>, Object> map = new WeakHashMap<List<LatLng>, Object>(); map.put(polygon.getPoints(), new Object());
  6. WeakHashMap pb3: literal String as Key WeakHashMap<String, Object> map =

    new WeakHashMap<String, Object>(); String key = "I'm pooled!"; map.put(key, new Object()); key = null;
  7. WeakHashMap pb3: String as Key WeakHashMap<String, Object> map = new

    WeakHashMap<String, Object>(); String safeKey1 = new String("I'm not pooled"); String safeKey2 = (new StringBuffer()).append("I'm not pooled either").toString(); String unsafeKey1 = new String("I'll be pooled"); map.put(safeKey1, new Object()); map.put(safeKey2, new Object()); unsafeKey1.intern(); map.put(unsafeKey1, new Object()); safeKey1 = null; safeKey2 = null; unsafeKey1 = null;
  8. WeakHashMap pb4: Boxed type Map<Integer, Object> map = new WeakHashMap<Integer,

    Object>(); for (int i=0; i < 128; i++) { map.put(i, new Object()); }
  9. WeakHashMap pb4: Boxed type private static class IntegerCache { static

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

    Object>(); for (int i=128; i < 512; i++) { map.put(i, new Object()); }
  11. Map<Object, String> map = new WeakHashMap<Object, String>(); Object key =

    new Object(); Object key2 = new Object(); map.put(key, "xyz"); map.put(key2, "GC'd entry"); int scannedElements = 0; for (Map.Entry<Object, String> entry : map.entrySet()) { scannedElements++; key2 = null; System.gc(); // assume GC is triggered } what's the value of scannedElements?
  12. Map<Object, String> map = new WeakHashMap<Object, String>(); Object key =

    new Object(); Object key2 = new Object(); map.put(key, "xyz"); map.put(key2, "GC'd entry"); int scannedElements = 0; for (Map.Entry<Object, String> entry : map.entrySet()) { scannedElements++; assertNotNull(entry.getKey()); key2 = null; System.gc(); } assertEquals(1, scannedElements); assertEquals(1, map.size()); GC'ed keys will not show up, "hasNext" skip them
  13. java.lang.ref.Reference • Object pointing to another Object (referent) • Used

    to observe the lifecycle of an Object • Can be GC'ed anytime • Soft, Weak & Phantom for different Use Cases
  14. java.lang.ref.Reference Object referent = new Object (); Reference<Object> reference =

    new WeakReference<Object>(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 }
  15. ReferenceQueue ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); for (int i =

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

    ReferenceQueue<Object>(); for (int i = 0 ; i < 100 ; i++) { WeakReference<Object> ref = new WeakReference<Object>(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); } }
  17. WeakReference & WeakHashMap Map<Object, StringBuilder> map = new WeakHashMap<Object, StringBuilder>();

    Object key = new Object(); StringBuilder value = new StringBuilder("leaky"); map.put(key, value); WeakReference<StringBuilder> valueWatcher = new WeakReference<StringBuilder>(value); key = null; value = null; System.gc(); // assume GC run assertNotNull(valueWatcher.get());//should be null!!
  18. Finalizer • Can resurrect the original object • Require 2

    GC cycles • Slow, one thread performing finalisation
  19. PhantomReference • Always used with a RefernceQueue • Signal that

    the Referent has been finalized • Referent is always null (prevent resurrection)
  20. 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; } }
  21. class Service { Activity.Listener listener; void registerListener (Activity.Listener listener) {

    this.listener = listener; } void unregisterListener () { this.listener = null; } }
  22. Service service = new Service(); Activity activity = new Activity(service);

    activity.onStart(); ReferenceQueue<Activity.Listener> referenceQueue = new ReferenceQueue<Activity.Listener>(); PhantomReference<Activity.Listener> reference = new PhantomReference<Activity.Listener>(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(); code here: https://gist.github.com/nhachicha/4ba780712a7ae179cc67
  23. Write ~deterministic tests Involving GC 1- Brute force for (int

    i = 0; i < 10; i++) { System.gc(); } Object object = new Object(); WeakReference<Object> weakReference = new WeakReference<Object>(object); while (weakReference.get() != null) { System.gc(); }
  24. Write ~deterministic tests Involving GC 2- Provoking an OutOfMemory byte[]

    memory; while (true) { try { memory = new byte[1024*1024*50]; // 50 megabytes } catch (OutOfMemoryError oom) { // GC triggered ... hopefully break; } }
  25. Write ~deterministic tests Involving GC 3- Using ReferenceQueue • Used

    by AOSP FinalizationTester.java & LeakCanacry lib • Prefer Runtime.getRuntime().gc(); • They also call System.runFinalization(); 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
  26. 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
  27. Activity - Service communication • Activity start service for bg

    work • Service need to communicate results
  28. Activity - Service communication Option1: using PendingIntent 1. Activity pass

    the PendingIntent as a Bundle argument (it's Parcelable) 2. Service invoke PendingIntent.send 3. onActivityResult will be called Survive configuration changes!
  29. Activity - Service communication Option2: using Messenger 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
  30. Activity - Service communication Option3: using ResultReceiver 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
  31. Activity - Service communication Option4: using LocalBroadcastManager 1. Activity register

    a BroadcastReceiver 2. LocalBroadcastManager.getInstance(this). registerReceiver(receiver, filter); 3. Service is ready to broadcast LocalBroadcastManager.getInstance(this). sendB (intent);
  32. Customisation • Number of Thread Runtime.getRuntime().availableProcessors() * 2 + 1

    • Bounded or Unbounded Queue • AbortPolicy • Setting Thread priority android.os.Process. setThreadPriority
  33. 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)