Omegapoint

2009-06-01

Registrering och prestandatrimmning i java

Första dagen på JavaOne präglades av registreringsproblem för mig och sedan en mycket bra halvdag på Java University med "Extreme Performance: Tuning Java Platform, Standard Edition (Java SE Platform) for Throughput and Latency". Till Suns försvar ska sägas att det var mitt eget fel att det strulade vid registreringen: jag försökte få till att förutom hela JavaOne-konferensveckan, så skulle jag också gå på Suns CommunityOne-dag på måndag. Konferensens datamodell klarade dock inte av hörnfallet där en deltagare både ville gå på JavaOne och CommunityOne samtidigt. Många missförstånd och stor underhållning uppstod med resultatet att jag fortfarande inte är inskriven på CommunityOne, men däremot har två stycken OpenSolaris-dvd:er ...

Prestanda och skräp

Efter lunch började första Java Univerisity-passet. Vi hade bestämt oss för att starta med en smäll och valde det tekniska alternativet Extreme Performance: Tuning Java Platform med Simon Roberts (som bland annat gjort ett antal av Suns utvecklar- och arkitekt-certifieringsprov) och Charlie Hunt (Java Performance Engineer på Sun och ansvarig för prestandan i HotSpot-jvm:en och Java SE:s klassbibliotek). Enligt beskrivningen skulle det handla mycket om hur de olika garbage collection-metoderna fungerar och vilka parametrar man ska skruva på vid vilket tillfälle. Den främsta anledningen till att jag ville gå var dock att det stod att de skulle prata om den nya G1-skräpsamlaren.

Simon Roberts började passet. Han visade sig vara en mycket begåvad föreläsare, antagligen för att han numera arbetar som lärare. Rolig, pedagogisk, engagerande och visste vad han pratade om. I snabb takt började han gå igenom exakt hur skräpinsamlingen växt fram historiskt i java. Mycket var sådant man känner igenom, men jag kände starkt att det var otroligt nyttigt att höra det igen och på ett bra förklarat sätt. Det tillhör de där kunskaperna som det känns som att alla låtsas som att de kan, men egentligen är det rätt dimmigt och man kan knappt minnas något white paper man läste för fem år sedan.

Att städa sitt rum när man inte vill

I java idag fungerar skräpinsamlingen i korthet ungefär så här: Det finns fyra områden i heapminnet: Eden, Survivor In, Survivor Out och Tenured. När ett objekt först skapas läggs det i Eden. Survivor-områdena innehåller unga objekt som klarat minst en skräpinsamling och Tenured-platsen är för riktigt långlivade objekt. Exakt vad ung och gammal här innebär är olika, men ett gammalt objekt skulle mellan tummen och pekfingret överlevt åtta stycken skräpinsamlingar (alla sådana där tröskelvärden går att ställa in som parametrar till den virtuella maskinen).

Skräpinsamlingen börjar i Eden och går igenom vilka objekt som ska sparas (alltså de objekt som har referenser till sig). Dessa objekt flyttas till Survivor In-området. På samma sätt skräpinsamlas det sedan i Survivor Out. Unga objekt som ska sparas flyttas till Survivor In medan objekt som uppnått en viss ålder flyttas till Tenured. Direkt när detta är klart byter de två Survivor-områdena namn med varandra. När Tenured börjar bli fullt, skräpinsamlas detta på det ursprungliga, långsamma sättet. Man får igenom hela området och försöker flytta alla objekt som ska sparas till ett enda kontinuerligt område i minnet. Roberts hade en bra metafor för det där då han påpekade att detta är samma sak som att försöka städa sitt rum när hela rummet är helt nerstökat. Det är då väldigt svårt att veta vart man ska lägga den sak man plockat upp för att antingen kasta eller städa undan. Han påpekade också att detta beteende är väldigt likt hur defragmenteringsprogrammet i Windows beter sig när det står i timmar och verkar flytta bitar på disken fram och tillbaka.

Tanken är alltså att utnyttja att de flesta javaprogram uppvisar ett tudelat beteende med avseende på minnesutnyttjande: de flesta objekt är kortlivade och ett fåtal objekt är väldigt långlivade.

Garanterat ung

Ett problem som kan uppstå med denna strategi är att Survivor-utrymmet är för litet för objekten som flyttas från Eden. I detta fall stoppar den virtuella maskinen dem i Tenured-utrymmet. Alltså måste Tenured kunna ha tillräckligt mycket kontinuerligt minnesutrymme för att klara av det värsta scenariot av översväming från Survivor. Detta kallas för "the young generation guarantee".

Om jvm:en får problem att tillgodose "the young generation guarantee" börjar den göra en total skräpinsamling à la java 1.0 (städa det totalt nerskräpade rummet ... ) Mycket kostsamt.

Totalt är denna skräpinsamlingsstrategi omkring 200 gånger snabbare än att göra en total kontinuerlig skräpgenomgång av minnesutrymmet.

The cunning use of flags

Det finns ett stort antal olika inställningar angående skräpinsamlingen och minnet till jvm:en, men ett av huvudmålen är att se till att så många objekt som möjligt hamnar i den unga generationen och inte svämmar över till den gamla. Några switchar är -XX:NewSize=m (initiella storleken på den unga generationen), -XX:MaxNewSize=m (maxstorleken på den unga generationen), -Xmn=m (sätter både NewSize och MaxNewSize) och -XX:NewRatio= (förhållandet av storleken på utrymmet för gamla objekt mot de unga). För att se till att Survivor-utrymmet inte svämmar över kan man sätta -XX:TargetSurvivorRatio=. Om man sätter det till 50% borde man klara av plötsliga spikar i minnesbehov.

Tillbaka till realtiden

Roberts pratade sedan en hel del om Suns Java Real-Time System (Java RTS). Real Time betyder alltså inte att det går fort utan att den virtuella maskinen lovar att en viss undre tidsgräns aldrig kommer passeras. Detta verkade intressant och de har en kool icke-pausande skräpinsamlingsalgoritm. Dock kostar det pengar att använda den och man inte bara ska använda den för utbildningssyfte. Skräpinsamlingen är långsam, men är alltid igång i en egen tråd i bakgrunden och behöver inte pausa exekveringen för att städa. Suns team bakom Real-Time var med i salen och jag misstänker att det var anledningen till att Roberts ägnade så mycket tid åt att prata om detta.

That dog won't Hunt

Så var det Charlie Hunts tur. En betydligt mindre begåvad föreläsare tyvärr och han började med att berätta att han — trots vad som utlovats i programmet — inte skulle prata om Suns nya skräpinsamlare G1. Detta gjorde mig sur och betydligt mindre vänligt inställd till hans fortsatta utläggningar. Att dessa dessutom till stor del utgjordes av att förklara hur bra Suns nya processorarkitektur T1/T2 är, tja, det gjorde inte saken bättre.

Ett par fascinerande (eller möjligen märkliga) saker sas dock. Hunt påstod att om ett objekt har fält som man oftast använder tillsammans (exempelvis String firstName och String lastName) ska fältdefinitionerna ska ligga tillsammans i källkodsfilen för att förbättra möjligheten att fälten hamnar tillsammans i processorns cacheminne (eller som min gamla KTH-professor i datorarkitektur alltid kallade det: fickminnet). Flera personer i publiken uttryckte förvåning över detta, något jag inte annat än kan hålla med om. Visserligen kan väl inte kompilatorn veta vilka fält som kommer användas ungefär samtidigt i ett objekt, men de flesta javaklasser jag sett i mitt liv har kanske tio stycken fält i genomsnitt. Kan inte bara kompilatorn placera dessa efter varandra? Verkar märkligt.

Oops, I did it again

Förutom de långa utläggningarna om hur fantastisk T1/T2-arkitekturen är, gav Hunt tips på ett antal förändringar man kan göra med switchar till jvm:en. En av de främsta är att om man kör en 64-bitars jvm, kan man använda 32-bitars pekare. Detta kallas "Compressed Pointers" eller "Compressed Oops" (Ordinary Object Pointer) och kan användas från och med jdk 6u12. 64-bitars heap, men med 32-bitars prestanda (som ju är bättre eftersom pekarna inte är så stora och därmed mindre jobb att hantera). Flagga för att göra detta är -XX:+UseCompressedOops.

Suns mätningar visar att en 64-bitars jvm med 32-bitars pekare är 5% snabbare än 8 stycken 32-bitars jvm:er. Troligen kommer "Compressed Oops" bli påslaget default i 64-bitars maskiner inom en snar framtid.

Källkoden är dokumentationen

Charlie Hunt avslutade med att påpeka att det finns en flagga som samlar alla prestandainställningarna, speciellt för flerkärnorsystem, -XX:+AggressiveOpts. Mycket underhållande när någon frågade om vilka inställningar denna flaggor innebär och Hunt svarade utan att skoja att det bara är att titta i källkoden till java.

Betyg: 4

Sammantaget en mycket intressant eftermiddag, men jag hade velat höra något om G1-skräpinsamlaren. Otroligt nyttigt att få upprepa exakt hur skräpinsamlingen går till. Förutom detta är Suns T1/T2 jättebra. Köp den. Gör det. Köp många. Larry behöver ett nytt jetplan.

Inga kommentarer:

Skicka en kommentar

Om Omegapoint

Omegapoint AB är ledande rådgivare och experter inom Systemarkitektur, Säkerhet och IT-ledning.

Twitter uppdateringar

Omegapoints kvitterström:

    Andra Omegapointbloggar