Perfug Guide de survie du développeur dans une application Java qui rame
1. Guide de survie du
développeur dans une
application (Java) qui rame
@blep
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. About me
• Brice LEPORINI
• Développeur Java / Scala
@blep
http://the-babel-tower.github.io/ @blep
11. GC: la master class
http://www.infoq.com/presentations/Understanding-Java-Garbage-Collection
@blep
12. Les GC Stop The World
• Serial GC: 1 thread
• Throughput Collector ou Parallel Collector:
• multi threads young et old
@blep
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. 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
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. 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
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. 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. 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. 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
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. 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. 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. 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
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. 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. 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
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. 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
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. 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
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. 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. 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. 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
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. Et la virtualisation?
• Réservation de ressources!
@blep
https://www.youtube.com/watch?v=XK2sG7AiEY8
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