Java Virtual Machine a fost inițial percepută drept lentă. Totuși, de la începtul anilor 90’ a evoluat și și-a maturizat ecosistemul, platforma și însăși componenta JVM. Fiind foarte populară, este natural ca în jurul acesteia să se dezvolte tooluri bune. Când alegem Java ca limbaj sau JVM ca platformă avem multe avantaje, cel mai important fiind faptul că “nu alergăm bezmetic” să identificăm cauza problemelor.
Acest articol va prezenta o serie de tooluri disponibile care strâng date comportamentale legate de o aplicație, date care ne ajută să găsim cauza blocajelor. Java este o platformă ce oferă diverse opțiuni de profilare și chiar Flight Recording în mod nativ pe versiunea JVM standard, începând cu versiunea 11. În funcție de tipul de tool de profilare (profiler), există și opțiunea de a colecta date și de a le vizualiza sub formă Flame Graph, ceea ce facilitează și mai mult capacitatea noastră de a interpreta rezultatele și de a identifica blocajele.
Toolurile prezentate aici pot fi o alternativă bună la mediile de dezvoltare-expunere ce au grad redus de observabilitate/vizualizare sau care au sensibilitate în ceea ce privește performanța. De multe ori, atașarea unui agent sau prezența unui Application Performance Monitor nu este posibilă sau viabilă pe termen scurt.
Când căutăm cauza unei probleme (troubleshooting), este interesant să cunoaștem câteva aspecte:
Cum sunt utilizate resursele calculatorului de-a lungul timpului?
Cum au fost distribuite resursele?
Care este comportamentul unei aplicații care deservește prea mulți clienți?
Resursele calculatorului sunt, de obicei, memoria și CPU. Este important de știut dacă sunt suficiente resurse și cât de mult este nevoie dintr-o resursă. Componentele de profilare ne pot ajuta în acest sens arătându-ne grafice de utilizare a memoriei sau a CPU, date legate de garbage collection behavior etc.
Colectarea de thread stack traces, de-a lungul timpului ne poate arăta cum se desfășoară procesele în timp.
În ceea ce privește deservirea a prea mulți clienți, acest lucru este realizat de măsurători efectuate când gradul de throughput este la cote ridicate. În funcție de aplicație, poate fi dificil de reprodus comportamentul din producție pentru cele mai multe dintre scenarii.
De obicei, timpul de răspuns, numit latență, va fi influențat de gradul de saturare al mașinii. Un scenariu posibil de saturare este atunci când se folosește memoria intensiv, ceea ce va crește rata de garbage collection. Drept consecință, latența va avea de suferit.
Toolurile de profilare (profiler) pot arăta informație și mai utilă precum: prezența unui thread contention, informații interne ale JVM, informație stack trace după un anumit interval de timp și așa mai departe. În timpul activității de troubleshooting, aceste informații fac mai ușoară înțelegerea comportamentului unei aplicații.
Colectarea naturală a informațiilor de profilare vine cu costuri în ceea ce privește performanța aplicației, a fișierelor exportate și nu numai. De obicei, nu este o idee bună să lăsăm pornită partea de profilare, dar când trebuie să identificăm un blocaj, această activitate ne oferă informații prețioase.
Inițial, Java a fost un limbaj de programare axat pe platforme embedded. Ulterior, limbajul s-a orientat către domeniul severelor. Definirea de Virtual Machine și de instrucțiuni derivate din stack a facilitat accesul compiler writers la VM. În timp, datorită popularității sale, blocajele au fost identificate și rezolvate. Deoarece Java a avut specificații solide în setul de instrucțiuni, și alți furnizori au reușit să dezvolte propria lor versiune de Java Virtual Machine.
Unii furnizori, alții decât Sun Microsystems, au avut propriul lor JVM, inclusiv IBM, Microsoft printre alții. Obiectivul principal a fost menținerea compatibilității, ceea ce s-a întâmplat în majoritatea timpului. O companie suedeză numită Appeal Virtual Machines a creat un JVM numit JRockit. Acest JVM a devenit renumit pentru viteză. Într-o succesiune interesantă de evenimente, această companie a fost achiziționată de BEA Systems în 2002.
BEA systems a achiziționat o serie de companii și a crescut în popularitate datorită platformei sale SOA, având în vedere numărul mare de servere solid application și toolurile de dezvoltare adunate pe parcursul timpului. BEA a fost apoi achiziționată de Oracle în 2008. Cu această ocazie, Oracle a devenit mai puternic în zona de ecosisteme Java, pentru ca în 2009 Oracle să achiziționeze Sun Microsystems. Era momentul când Oracle a devenit proprietar a două dintre cele mai performante JVM. În 2010, Oracle și-a declarat intenția de a reuni cele 2 JVM, o idee îndrăzneață.
Ceea ce este disponibil azi, pe Hotspot JVM standard este rezultatul a mai bine de două decade de evoluție continuă și a cunoștințelor mai multor companii. Funcționalitățile JRockit au fost adăugate incremental pe JVM standard.
Cea mai bună metodă de a face optimizări de performanță constă în a avea măsurători bune. Conștienți de această metodă, Appeal a dezvoltat propriul tool de profilare pentru a măsura propriul JVM și a-l optimiza. JRockit Flight Recorder s-a dovedit atât de bun, încât a fost distribuit cu JVM, astfel încât utilizatorii săi să aibă funcționalitate de troubleshooting performance în cadrul aplicațiilor.
Acest tool nou adăugat a fost suficient pentru promovarea acestui JVM companiei Sun care, la acel moment, cerea ca fiecare JVM să aducă “valoare adăugată”. Toolul JRockit Mission Control a reușit să adune informația Flight Recorder și să ofere o bună analiză bazată pe grafice și nu numai.
Acest tool a fost gândit pentru a fi folosit în producție în varianta “always on” (mereu pornit). A trebuit acordată o grijă mare, astfel încât să nu se interfereze prea mult cu performanța aplicației care rula. Cele mai multe dintre toolurile de profilare populare folosesc safe points (borne de securitate), ceea ce interferează cu performanța aplicației. Un exemplu de tool de profilare din această categorie este VisualVM.
Deși aceste tooluri de profilare sunt utile pentru activități de troubleshooting pe mașina programatorilor, pentru mediul de producție, toolurile de profilare care folosesc AsyncCallTrace internal API sunt mai puțin intruzive. Această categorie include atât Flight Recorder, cât și Honest Profiler.
Una din funcționalitățile Flight Recorder este de a extrage informații dintr-un JVM care rulează pentru a putea analiza , ulterior, datele. Prin urmare, poate fi pornit atunci când e necesar. Această funcționalitate nu este unică pentru Flight Recorder, dar ceea ce o face să fie o opțiune cu adevărat interesantă este faptul că se livrează la pachet cu JVM. (Începând cu versiunea 11, aceasta este disponibilă chiar și pentru utilizatorii care utilizează aplicația gratuit). Din perspectiva operațiunilor, este mai ușor să se ruleze comenzi deja instalate pe mașinile de producție decât să se instaleze componente adiționale.
Pentru aplicații ce rulează pe JVM mai vechi, o opțiune viabilă pentru a aduna informație Flight Recording pe versiunea 8 este Liberica JVM care a portat acest suport retroactiv, de la OpenJDK 11 la Java 8.
Autorul acestui articol a trebuit să identifice motivul pentru care sistemul funcționa foarte încet exact când sistemul unui client avea utilizare de vârf. Am avut dificultăți în a identifica metodele care erau cauza problemei. La acel moment, JVM-ul folosit era, din fericire, BEA JRockit, reușindu-se adunarea informației cu Flight Recorder. În câteva minute, doar privind graficele JRockit Mission Control, am reușit să identific punctele slabe. De asemenea, pe baza informației din JVM am demarat o optimizare simplă a interogărilor. Acest lucru s-a întâmplat acum zece ani, când APM și capacitatea de a observa/vizualiza nu erau la modă.
BEA JRockit Mission Control 1.0 la începuturile sale (prezentare din 2019 a Code One)
În zilele noastre, înregistrarea informației de tip flight recording este ușoară, operațiunea putând fi inițiată odată cu deschiderea aplicației sau asociind-o cu aplicația în timp ce aplicația rulează. O folosesc de multe ori pentru a înregistra execuția în timpul dezvoltării și a testelor, pentru analiză ulterioară.
VisualVM oferă suport pentru citirea fișierelor Flight Recorder
Brendan Gregg este un inginer australian, cu vaste contribuții în observabilitate/vizualizare, încă din timpurile când lucra la Sun Microsystems, care, actualmente, lucrează la Netflix. Preocupat de tema
Performanței, acesta a scris câteva cărți foarte apreciate pe subiectul analizei de performanță, pe care vi le recomand cu căldură.
Una dintre cele mai bune contribuții în acest domeniu este un tool de vizualizare numit Flame Graphs. Este o altă metodă de a vizualiza un set de stack traces colectate pe parcursul timpului. De exemplu, prin capturi (snapshots) efectuate pe parcursul timpului, devine evident că, pe măsură ce crește numărul de capturi, datele vor fi mai greu de analizat.
Flame Graphs realizează agregarea acestor capturi, astfel încât părțile comune ale acestora să fie grupate împreună. Dacă pur și simplu ne uităm la zonele aglomerate, există o șansă mare să găsim și cauza blocajului.
Când avem cod complex, este ușor să dăm vina pe “părțile urâte” și să refactorizăm lucruri ce, aparent, nu au sens. Refactorizarea este o practică bună, iar codul ar trebui păstrat pe cât de curat posibil. Totuși, în timpul în care se face troubleshooting în performanță este dificil să prezicem comportamentul codului în cauză sau chiar să prezicem cum se va comporta codul refactorizat.
Am reușit să identific mai multe blocaje de performanță folosind Flame Graphs pe date de profilare colectate. Am avut o satisfacție și mai mare când am împărtășit această informație cu membrii echipei, ceea ce a dus la îmbunătățiri și mai mari ale performanței, având în vedere că echipa a reușit să identifice blocajele pe cont propriu.
De multe ori, schimbarea de cod ghidată de datele Flame Graph are un grad mai mare de acuratețe. De exemplu, o metodă care apare, cu precădere, pe Flame Graph s-ar putea să nu fie utilă deloc sau ar putea fi cached pentru a nu fi apelată repetat.
În această imagine, de pe blogul Nextflix Java in Flames, se poate observa că fișierul IO este responsabil pentru blocajul acestor date de profilare. Pachetele în verde de pe partea dreaptă (începând cu “Lorg/Mozilla/javascript/gen/file”) sunt, în mod evident, cei mai mari consumatori de timp. Datorită acestui feedback, pasul următor este evitarea acestor apelări, implementarea cache a rezultatelor sau optimizarea acestei operații.
Este extrem de util să ne familiarizăm cu Flight Recording și Flame Graphs. Cel mai bun mod de a face acest lucru este să vă murdăriți mâinile și să experimentați. Unele dintre referințele de mai jos sunt în același timp sursă și referință pentru unele din comenzile pe care le puteți folosi.
În ceea ce privește Flight Recording, un exercițiu pentru cei care citesc articolul este să obțină informația Flight Recorder prin java arguments sau jcmd. Este un pas înainte să puteți deschide informația înregistrată pe un tool de profilare, pentru ca apoi să o înțelegeți.
Referitor la Flame Graphs, este foarte util să parcurgeți materialele de pe site-ul lui Brendan Gregg, și, ulterior, să învățați cum să faceți export fie de agenți de profilare, fie după ce ați colectat informație din Flight Recorder. A experimenta lucruri în timpul optimizărilor de performanță vă creează abilități fantastice.
Toolurile prezentate în acest articol vă permit să porniți sau să opriți toolurile de observare la cerere, să colectați date mai complete decât cele disponibile de obicei prin intermediul APM, cu impact minor asupra performanței. Când este folosită și înțeleasă corespunzător, informația ajută echipele de dezvoltare să elimine cauzele pentru care mașinile sunt lente în producție.
Java (software platform) - Wikipedia
https://bell-sw.com/announcements/2020/06/15/Liberica-JDK-8-with-JDK-Flight-Recorder/
JDK Mission Control: Where We Are, Where We Are Going [Code One 2019] (slideshare.net)
Java in Flames. mixed-mode flame graphs provide a… | by Netflix Technology Blog | Netflix TechBlog
Java Flame Graphs (brendangregg.com)
https://medium.com/graalvm/jfr-support-in-visualvm-62fddd4f0795
https://docs.oracle.com/javacomponents/jmc-5-4/jfr-runtime-guide/run.htm#JFRUH164
https://github.com/jvm-profiling-tools/honest-profiler/wiki/How-to-Run
de Ovidiu Mățan
de Ovidiu Mățan
de Ovidiu Mățan
de Ovidiu Mățan