Perfug Guide de survie du développeur dans une application Java qui rame

2,091 views

Published on

Slides du PerfUG du 16/06/2016

Published in: Software
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,091
On SlideShare
0
From Embeds
0
Number of Embeds
257
Actions
Shares
0
Downloads
21
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Perfug Guide de survie du développeur dans une application Java qui rame

  1. 1. Guide de survie du développeur dans une application (Java) qui rame @blep
  2. 2. Les ordres de grandeur importent • Salaire: 1000 --> 1300€ • Page speed: 30s --> 20s 😭 • Seuls les changements d'ordre de grandeur ( >300%) changent la perception de la performance de l'application @blep
  3. 3. About me • Brice LEPORINI • Développeur Java / Scala @blep http://the-babel-tower.github.io/ @blep
  4. 4. Lenteurs ou performance ? @blep
  5. 5. Scott Oaks - Java Performance The definitive guide There is no magical -XX:+RunReallyFast option @blep
  6. 6. Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution @blep
  7. 7. Collecter des données sur l'environnement concerné! @blep
  8. 8. Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? @blep
  9. 9. Les problèmes liés à la mémoire • Terminal avec jstat -gcutil @blep
  10. 10. Young Generation Heap Eden From Space To Space Old Generation Survivor 1: new 2: minor 1 3: n minors 4: promotion @blep
  11. 11. GC: la master class http://www.infoq.com/presentations/Understanding-Java-Garbage-Collection @blep
  12. 12. Les GC Stop The World • Serial GC: 1 thread • Throughput Collector ou Parallel Collector: • multi threads young et old @blep
  13. 13. Les GC concurrents: CMS • Concurrent Mark Sweep • Multi thread sur la young et les phases concurrentes, mono thread sur les Full GC • STW sur les minor GC • 6 Phases: 1. Initial mark (STW) 2. Concurrent Mark 3. Pre clean 4. Remark (STW) 5. Sweep 6. Reset • Ne compacte pas de façon concurrente @blep
  14. 14. Les GC concurrents : G1 • G1 pour “Garbage first” • Multi thread sur la young et les phases concurrentes, mono thread sur les Full GC • STW sur les minor GC • Gère la heap en zones discrètes • Phases: 1. Initial Mark (STW) 2. Root Region Scanning 3. Concurrent Marking 4. Remark (STW) 5. Cleanup / Copying Eden Survi vor Old Old EdenEden Old Survi vor OldEden Eden @blep
  15. 15. Le log du GC 90,652: [ GC (Allocation Failure) [ PSYoungGen: 130768K->1088K(152576K) ] 381293K->253237K(502272K), 0,0044329 secs ] [Times: user=0,02 sys=0,00, real=0,01 secs] 101,003: [ Full GC (Ergonomics) [ PSYoungGen: 3687K->0K(150528K) ] [ ParOldGen: 344000K->132146K(349696K) ] 347687K->132146K(500224K), [ Metaspace: 76887K->76887K(1118208K) ], 0,1998650 secs ] [Times: user=1,03 sys=0,01, real=0,20 secs] Type de collecte -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGClogFiles=10 -XX:GCLogFileSize=1000K @blep
  16. 16. GCViewer https://github.com/chewiebug/GCViewer @blep
  17. 17. Maintenant qu'on en sait un peu plus sur les GC, lequel choisir? @blep
  18. 18. Profils applicatifs • Transactionnels • Batches @blep
  19. 19. Choix du GC • Batch: • Throughtput collector • Application transactionnelle: • GC concurrent • CMS heap < 4GB • G1 heap >= 4GB @blep
  20. 20. Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? Prélever un heap dump @blep
  21. 21. Heap dump $ jmap -dump:file=heap.hprof,format=b,live 4695 Dumping heap to heap.hprof ... Heap dump file created $ du -sh heap.hprof 462M heap.hprof $ java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=myApp.hprof ... @blep
  22. 22. Retained size et dominateur User age: 42 8o name: 4/8o String B i l lvalue: 4/8o Group name: 4/8o a d m i n group: 4/8o User age: 42 8o group: 4/8o name: 4/8o String J o h nvalue: 4/8o Shallow size Retained size deep size @blep
  23. 23. Memory Analyzer Tool (MAT) http://www.eclipse.org/mat/ 1 2 3 @blep
  24. 24. Tester Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? Prélever un heap dump Fuite ou utilisation abusive? Analyser le dump et identifier les dominateurs Augmenter la mémoire Optimisation du code @blep
  25. 25. Et le tuning mémoire et GC? • Définition des ratios pour tailler les zones Eden, Survivor et Old • Définition d’objectifs de latence • Nombre de threads alloués au GC • Java Ergonomics depuis 1.5 -XX:+UseAdaptiveSizePolicy $ java -XX:+PrintFlagsFinal -version |wc -l java version "1.8.0_66" Java(TM) SE Runtime Environment (build 1.8.0_66-b17) Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode) 718 @blep
  26. 26. Tester Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? Prélever un heap dump Fuite ou utilisation abusive? Analyser le dump et identifier les dominateurs Augmenter la mémoire Prélever un ou plusieurs thread dumps OU Analyser l’activité des threads avec un profiler Optimisation du code @blep
  27. 27. Thread dump $ jstack 4695 2016-03-06 09:47:03 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.66-b17 mixed mode): "http-bio-8081-exec-74" #305 daemon prio=5 os_prio=31 tid=0x00007fa2de53b000 nid=0xf11f runnable [0x00000001202e7000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:170) at java.net.SocketInputStream.read(SocketInputStream.java:141) at java.io.BufferedInputStream.fill(BufferedInputStream.java:246) at java.io.BufferedInputStream.read(BufferedInputStream.java:265) - locked <0x00000007a53d8a00> (a java.io.BufferedInputStream) at java.io.DataInputStream.readByte(DataInputStream.java:265) at org.hsqldb.result.Result.newResult(Unknown Source) at org.hsqldb.ClientConnection.read(Unknown Source) at org.hsqldb.ClientConnection.execute(Unknown Source) - locked <0x00000007a53d7910> (a org.hsqldb.ClientConnection) at org.hsqldb.ClientConnection.getAttribute(Unknown Source) - locked <0x00000007a53d7910> (a org.hsqldb.ClientConnection) at org.hsqldb.ClientConnection.isAutoCommit(Unknown Source) - locked <0x00000007a53d7910> (a org.hsqldb.ClientConnection) at org.hsqldb.jdbc.JDBCConnection.getAutoCommit(Unknown Source) - locked <0x00000007a53d75f0> (a org.hsqldb.jdbc.JDBCConnection) at org.apache.commons.dbcp.DelegatingConnection.getAutoCommit(DelegatingConnection.java:337) […] "http-bio-8081-exec-79" #310 daemon prio=5 os_prio=31 tid=0x00007fa2e00e7800 nid=0xe30b waiting for monitor entry [0x0000000123aa0000] java.lang.Thread.State: BLOCKED (on object monitor) at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:216) - waiting to lock <0x00000007a7be3e38> (a java.lang.Object) @blep
  28. 28. Les états du thread @blep
  29. 29. java.lang.Thread.State#BLOCKED • Un thread bloqué est dans l’attente de l’acquisition d’un moniteur pour entrer dans un bloc synchronisé • La synchronisation d’un bloc permet de garantir que les instructions ne sont exécutées exclusivement que par un et un seul thread @Test public void lockMe() throws InterruptedException { final Thread thread1 = new Thread(this::intenseLockingComputation); final Thread thread2 = new Thread(this::intenseLockingComputation); thread1.start(); thread2.start(); thread2.join(); } private final Object monitor = new Object(); private String intenseLockingComputation() { synchronized (monitor) { return { ... } } } Thread1 verrouille monitor Thread2 attend monitor Thread2 verrouille monitor @blep
  30. 30. Lire le thread dump "Thread-2" #21 prio=5 os_prio=31 tid=0x00007fed9c8b3800 nid=0x6803 waiting for monitor entry [0x000000012d3b1000] java.lang.Thread.State: BLOCKED (on object monitor) at blep.LockTest.intenseLockingComputation(LockTest.java:36) - waiting to lock <0x00000006c0012178> (a java.lang.Object) at blep.LockTest$$Lambda$9/204349222.run(Unknown Source) at java.lang.Thread.run(Thread.java:745) "Thread-1" #20 prio=5 os_prio=31 tid=0x00007fed9bb03000 nid=0x6603 runnable [0x000000012d2ae000] java.lang.Thread.State: RUNNABLE at sun.nio.cs.UTF_8$Decoder.decode(UTF_8.java:456) at java.lang.StringCoding$StringDecoder.decode(StringCoding.java:153) at java.lang.StringCoding.decode(StringCoding.java:193) at java.lang.StringCoding.decode(StringCoding.java:254) at java.lang.String.<init>(String.java:534) at java.lang.String.<init>(String.java:554) at blep.LockTest.intenseLockingComputation(LockTest.java:39) - locked <0x00000006c0012178> (a java.lang.Object) at blep.LockTest$$Lambda$8/1100439041.run(Unknown Source) at java.lang.Thread.run(Thread.java:745) Thread-2 est bloqué là en attente du moniteur de la classe Thread-1 exécute la ligne et a verrouillé le moniteur là @blep
  31. 31. Un cas concret de verrouillage "http-bio-8081-exec-172" #454 daemon prio=5 os_prio=31 tid=0x00007fa2e00f2000 nid=0xe12f waiting for monitor entry [0x0000000122acc000] java.lang.Thread.State: BLOCKED (on object monitor) at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:216) - waiting to lock <0x00000007b11190c0> (a java.lang.Object) at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:108) at ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(UnsynchronizedAppenderBase.java:88) at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:64) at ch.qos.logback.classic.Logger.appendLoopOnAppenders(Logger.java:285) at ch.qos.logback.classic.Logger.callAppenders(Logger.java:272) at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:473) at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:427) at ch.qos.logback.classic.Logger.debug(Logger.java:534) "http-bio-8081-exec-173" #457 daemon prio=5 os_prio=31 tid=0x00007fa2e275e800 nid=0xf61b runnable [0x0000000122de8000] java.lang.Thread.State: RUNNABLE at java.io.FileOutputStream.writeBytes(Native Method) at java.io.FileOutputStream.write(FileOutputStream.java:326) at java.io.BufferedOutputStream.write(BufferedOutputStream.java:122) - locked <0x00000007a060c310> (a java.io.BufferedOutputStream) at java.io.PrintStream.write(PrintStream.java:480) - locked <0x00000007a060c2f0> (a java.io.PrintStream) at java.io.FilterOutputStream.write(FilterOutputStream.java:97) at org.apache.tomcat.util.log.SystemLogHandler.write(SystemLogHandler.java:169) at ch.qos.logback.core.joran.spi.ConsoleTarget$1.write(ConsoleTarget.java:36) at ch.qos.logback.core.encoder.LayoutWrappingEncoder.doEncode(LayoutWrappingEncoder.java:103) at ch.qos.logback.core.OutputStreamAppender.writeOut(OutputStreamAppender.java:193) at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:217) - locked <0x00000007b11190c0> (a java.lang.Object) at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:108) at ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(UnsynchronizedAppenderBase.java:88) at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:64) @blep
  32. 32. Mon Thread Dump Analyzer http://the-babel-tower.github.io/tda.html Analyse des moniteurs soumis à concurrence Stats Recherche dans les piles d’appels Lien vers GrepCode (enfin quand ça marche…) Regroupements par état @blep
  33. 33. Thread history @blep
  34. 34. Ca dépend ! Dimensionner un pool de threads @blep
  35. 35. Sync vs Async RUN RUNWAIT Traitement 2 x Traitement @ 1 thread : RUN11 RUN12WAIT1 RUN21 RUN22WAIT2Sync Async RUN11 RUN12 WAIT1 RUN21 RUN22 WAIT2 Conception séquentielle et bloquante Conception non bloquante basée sur la composition de callbacks @blep
  36. 36. Dimensionner un pool de threads • Conception asynchrone: autant que de threads physiques (@see /proc/cpuinfo) • Conception synchrone: ça dépend! • Batch: autant que de threads physiques • Transactionnel : plus (à voire avec la charge et la proportion d’attente) @blep
  37. 37. Tester Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? Prélever un heap dump Fuite ou utilisation abusive? Analyser le dump et identifier les dominateurs Augmenter la mémoire Prélever un ou plusieurs thread dumps OU Analyser l’activité des threads avec un profiler Identifier les segments de code applicatifs concernés Optimisation du code Locks? Identifier les moniteurs soumis à concurrence @blep
  38. 38. Identifier les consommateurs avec un profiler • Sampling ou profiling? @blep
  39. 39. Profiler gratuit ou payant? • Profilers gratuits: • Gratuit! • Pas de choix • Peu performant • Télémétrie • Léger • Profilers payants: • Coût décent (500/600€) • Peu de choix • Performants • Précis • Sondes de haut niveau • Intégrés à l’IDE @blep
  40. 40. Tester Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? Prélever un heap dump Fuite ou utilisation abusive? Analyser le dump et identifier les dominateurs Augmenter la mémoire Prélever un ou plusieurs thread dumps OU Analyser l’activité des threads avec un profiler Identifier les segments de code applicatifs concernés Optimisation du code Locks? Identifier les moniteurs soumis à concurrence IO bound? Analyser l’activité I/O @blep
  41. 41. Monitorer les I/O @blep
  42. 42. Identifier les contentions I/O @blep
  43. 43. Optimisation IO • Utilisation de cache: • Applicatif • cache FS du kernel @blep
  44. 44. Tester Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? Prélever un heap dump Fuite ou utilisation abusive? Analyser le dump et identifier les dominateurs Augmenter la mémoire Prélever un ou plusieurs thread dumps OU Analyser l’activité des threads avec un profiler Identifier les segments de code applicatifs concernés Optimisation du code Locks? Identifier les moniteurs soumis à concurrence IO bound? Analyser l’activité I/O Optimiser les IO (cache?) @blep
  45. 45. Tester Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? Prélever un heap dump Fuite ou utilisation abusive? Analyser le dump et identifier les dominateurs Augmenter la mémoire Prélever un ou plusieurs thread dumps OU Analyser l’activité des threads avec un profiler Identifier les segments de code applicatifs concernés Optimisation du code Locks? Identifier les moniteurs soumis à concurrence IO bound? Analyser l’activité I/O Optimiser les IO (cache?) Prélever un ou plusieurs thread dumps pour identifier les threads en wait @blep
  46. 46. Exemple du pool sous dimensionné @blep
  47. 47. Dimensionner un pool JDBC • x connexions pour y utilisateurs? • 42? • Autant que de requêtes HTTP? • initial = max = maxIdle ! Expérimenter pour réduire le temps d’acquisition et obtenir la meilleure cadence @blep
  48. 48. Tester Pool sous dimensionné? Redimensionner Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? Prélever un heap dump Fuite ou utilisation abusive? Analyser le dump et identifier les dominateurs Augmenter la mémoire Prélever un ou plusieurs thread dumps OU Analyser l’activité des threads avec un profiler Identifier les segments de code applicatifs concernés Optimisation du code Locks? Identifier les moniteurs soumis à concurrence IO bound? Analyser l’activité I/O Optimiser les IO (cache?) Prélever un ou plusieurs thread dumps pour identifier les threads en wait @blep
  49. 49. Tester Pool sous dimensionné? Redimensionner Analyser l’activité/l’utilisation des systèmes externes Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? Prélever un heap dump Fuite ou utilisation abusive? Analyser le dump et identifier les dominateurs Augmenter la mémoire Prélever un ou plusieurs thread dumps OU Analyser l’activité des threads avec un profiler Identifier les segments de code applicatifs concernés Optimisation du code Locks? Identifier les moniteurs soumis à concurrence IO bound? Analyser l’activité I/O Optimiser les IO (cache?) Prélever un ou plusieurs thread dumps pour identifier les threads en wait @blep
  50. 50. Always blame the database! • Analyse des plans d’exécution / query trace • Ajout d’indexes • Fraîcheur des statistiques • Purge de données / partitionnement / sharding (NoSQL) • Rationalisation des échanges (batches, round trips, fetch size) • Dénormalisation @blep
  51. 51. Optimisation IO • Utiliser les Buffered(In|Out)putStreams @blep
  52. 52. Tester Pool sous dimensionné? Redimensionner Analyser l’activité/l’utilisation des systèmes externes Optimiser les IO (batches / buffers / …?) Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? Prélever un heap dump Fuite ou utilisation abusive? Analyser le dump et identifier les dominateurs Augmenter la mémoire Prélever un ou plusieurs thread dumps OU Analyser l’activité des threads avec un profiler Identifier les segments de code applicatifs concernés Optimisation du code Locks? Identifier les moniteurs soumis à concurrence IO bound? Analyser l’activité I/O Optimiser les IO (cache?) Prélever un ou plusieurs thread dumps pour identifier les threads en wait @blep
  53. 53. Et la virtualisation? • Réservation de ressources! @blep https://www.youtube.com/watch?v=XK2sG7AiEY8
  54. 54. Optimisation du code Identifier les segments de code applicatifs concernés Tester Pool sous dimensionné? Redimensionner Analyser l’activité/l’utilisation des systèmes externes Optimiser les IO (batches / buffers / …?) Lenteurs avec 1 utilisateur? Ouvrir un shell sur la plateforme d’exécution CPU bound? GC? Prélever un heap dump Fuite ou utilisation abusive? Analyser le dump et identifier les dominateurs Augmenter la mémoire Prélever un ou plusieurs thread dumps OU Analyser l’activité des threads avec un profiler Identifier les segments de code applicatifs concernés Optimisation du code Locks? Identifier les moniteurs soumis à concurrence CPU bound? Placer des marqueurs de mesure dans les log OU Analyser l’activité avec un profiler IO bound? Analyser l’activité I/O Optimiser les IO (cache?) Tester Prélever un ou plusieurs thread dumps pour identifier les threads en wait @blep

×