Det är viktigt att få ut användbar information från loggfilerna för att din Java-applikation ska bli framgångsrik. Loggdata ger dig värdefull information om din applikations prestanda, stabilitet och användbarhet.
Analysering av loggdata kan tyckas tråkigt, men det behöver inte vara det. Det finns en mängd olika verktyg för att läsa, analysera och konsolidera loggdata. Grundläggande kommandoradsverktyg som grep, uniq och sort kan kombinera och extrahera användbar information från loggfiler. Mer avancerade loggparsare som Logstash eller Fluentd kan extrahera nyckeldata från dina loggar till lätt sökbara tokens. Molnbaserade loggtjänster som SolarWinds® Loggly® lagrar dina loggdata åt dig och erbjuder sofistikerade analysfunktioner, vilket gör att du inte behöver underhålla loggar själv.
Det här avsnittet utforskar några metoder och verktyg för att analysera loggar i syfte att förbättra dina applikationer.
- Finnande av de vanligaste undantagen
- Finnande av undantag efter typ med hjälp av grep
- Finnande av undantag efter klass med hjälp av grep
- Användning av en logghanteringslösning
- Buggning av produktionsproblem
- Samla in information om problemet
- Identifiera orsaken till problemet
- Lös problemet och förhindra att det upprepas
- Mer verktyg för felsökning i produktionen
- jdb
- OverOps
- BTrace
- Chronon
- jhsdb
- Spårning av transaktioner
- Spårning av unika ID
- Spårning av metodanrop
- Spårningsmetoder i Log4j
- Spårningsmetoder i SLF4J
- Hantering av minnesanvändning
- Garbage Collection
- Minnesläckor
- Allmänna resurser
- Se det. Analysera den. Inspektera den. Lös det
Finnande av de vanligaste undantagen
Finnande av de vanligaste undantagen kan hjälpa dig att lokalisera områden med dålig prestanda i din Java-applikation. De flesta loggningsramverk registrerar undantagstypen, undantagsmeddelandet och den metod där undantaget inträffade. Om du använder Log4j kommer en logg över undantag att se ut som en av följande.
09:54:44.565 ERROR Log4jTest.MathClass - java.lang.ArithmeticException: / by zero10:00:10.157 ERROR Log4jTest.Log4jTest - java.io.FileNotFoundException: myFile (No such file or directory)10:00:10.157 ERROR Log4jTest.Log4jTest - java.io.FileNotFoundException: newFile (No such file or directory)
Om din utdata innehåller stacktraces, se avsnittet Parsing Multiline Stack Traces i den här guiden.
Låt oss börja med ett enkelt exempel där vi använder det populära GNU-verktyget grep. Sedan visar vi hur ett logghanteringsverktyg kan göra det ännu enklare.
Finnande av undantag efter typ med hjälp av grep
Med följande Unix-kommando hittar du undantag, extraherar undantagstypen och räknar antalet förekomster. grep är ett populärt kommandoradsverktyg som utför mönstermatchning, medan uniq och sortera grupperar respektive sorterar resultaten:
$ grep -o "\w*Exception" myLog.log | sort -r | uniq -c
I det här exemplet matchar det reguljära uttrycket \w*Exception
alla ord som slutar på ”Exception”. Flaggan -o
talar om för grep att endast skriva ut de delar av utmatningen som matchar söksträngen. sort -r
sorterar resultatet i omvänd ordning, medan uniq -c
grupperar resultaten och räknar antalet förekomster. Som ett resultat får vi ett antal undantag per typ.
2 FileNotFoundException1 ArithmeticException
Vi kan också använda grep för att söka i loggen efter varje specifik förekomst av dessa undantag.
$ grep ArithmeticException myLog.log09:54:44.565 ERROR Log4jTest.Log4jTest - java.lang.ArithmeticException: / by zero
Detta returnerar varje rad som innehåller en instans av söksträngen ArithmeticException
, med själva söksträngen markerad i utmatningen.
Finnande av undantag efter klass med hjälp av grep
Du kan också söka efter undantag efter klassen i vilken de inträffade. För enradiga logghändelser grupperar följande kommando antalet undantag efter klass och typ. grep extraherar klassnamnet genom att söka efter ett ord följt av ett visst tecken (i det här exemplet används ett bindestreck, men vilket tecken som helst kan användas så länge det är unikt för posten). Även om det här tecknet inte är nödvändigt hjälper det oss att hitta klassnamnet i logghändelsen. Med OR-operatorn (betecknad med |) kommer grep också att extrahera namnet på undantaget genom att söka efter ett ord som innehåller strängen ”Exception.”
sed är ett annat kommandoradsverktyg som kan användas för att formatera utdata från grep. I det här exemplet tar sed bort specialtecknet från vårt utdata samt alla tecken för nya rader. Utmatningen leds sedan till kommandona uniq och sort för att gruppera respektive sortera resultaten.
$ grep -o "w* -|\w*Exception" myLog.log | sed 'N; s/ -n/ /' | sort -r | uniq -c
Resultatet för oss närmare de viktigaste problemområdena:
2 Log4jTest FileNotFoundException1 MathClass ArithmeticException
Med dessa resultat kan vi använda grep för att hitta mer detaljer om problem som uppstår i specifika klasser. Följande kommando hämtar till exempel undantag som inträffade i klassen MathClass
.
$ grep -e "MathClass.*Exception" myLog.log09:54:44.565 ERROR Log4jtest.MathClass - java.lang.ArithmeticException: / by zero
Användning av en logghanteringslösning
De flesta logghanteringslösningar erbjuder sätt att gruppera och söka loggposter baserat på loggtyp, meddelandeinnehåll, klass, metod och tråd. Om dina loggar redan analyseras och lagras kan många lösningar grafera och sortera undantag efter antalet förekomster. Detta är en punkt och klick-operation i Loggly så du behöver inte memorera komplicerade grep-kommandon. Loggly indexerar också loggposterna, vilket gör sökningar och räkningar mycket snabbare än grep. Vi kan till exempel använda fältutforskaren för att se en lista över undantag och deras frekvens och sedan klicka på ett undantag för att visa alla relevanta loggar.
Använda Logglys fältutforskare för att snabbt hitta loggar efter undantagstyp.
Loggly erbjuder också visualiseringsverktyg som kan vara mer informativa än det textbaserade resultatet från grep. Grafer som dessa kan hjälpa dig att prioritera resurser för utvecklingssprintar eller för patchar efter en ny version.
Kartläggning av undantag efter frekvens i Loggly.
Buggning av produktionsproblem
När det gäller produktionsproblem är tiden viktig. Ett kritiskt fel i din applikation gör inte bara dina användare missnöjda, utan det sänker också försäljningen och minskar förtroendet för din applikation eller tjänst.
I de flesta fall kan lösningen av ett problem delas in i tre viktiga steg:
- Samla in information om problemet
- Identifiera orsaken till problemet
- Hitta en lösning och förhindra att problemet upprepas
Samla in information om problemet
Det första steget är att samla in information om problemet. Samla in så mycket information som möjligt – skärmdumpar, kraschrapporter, loggar, länkar (för webbtjänster) etc. – för att begränsa potentiella orsaker. Du vill att användaren som upplevde problemet ska ge detaljerad information om händelsen, inklusive: när och var i programmet problemet inträffade, hans eller hennes handlingar som ledde fram till problemet, driftsmiljön och eventuella konstiga eller ovanliga beteenden från programmet före och efter att problemet inträffade.
Därifrån kan du börja samla in logginformation. Om ditt program är en värdtjänst börjar du hämta loggar från webb- och applikationsservrarna. Om din applikation är ett distribuerat programvarupaket, be användaren att inkludera loggdata i sin felrapport. Alternativt, om din applikation skickar logghändelser till en centraliserad loggserver, kommer dina loggar att vara omedelbart tillgängliga.
När du har en rimlig mängd data om problemet kan du börja spåra det i koden.
Identifiera orsaken till problemet
När du har samlat in information om problemet är nästa steg att identifiera orsaken till det. Att reproducera ett fel i en utvecklingsmiljö är ett av de enklaste sätten att bekräfta dess existens, men det kan vara tidskrävande och fungerar kanske inte i alla fall. Om du har en grundlig uppsättning loggar kommer du direkt till problemets källa, vilket sparar tid och frustration.
En felrapport ger dig en allmän uppfattning om vad problemet är och var det inträffade. Med hjälp av ditt valfria logghanteringsverktyg kan du begränsa din sökning till ett mindre antal loggposter genom att söka efter en unik datatoken, t.ex. ett användarnamn, ett sessions-ID eller en meddelandetekst.
Låt oss gå igenom ett exempelscenario för att visa hur man felsöker ett system. Tänk dig att vi har ett webbaserat gränssnitt för fjärrinloggning till en webbplats. Webbsidan har en inloggningsskärm som utför grundläggande autentisering med användarnamn och lösenord. Användarna har rapporterat att de inte kan logga in på webbplatsen. Webbsidan accepterar deras inmatning men misslyckas sedan med ett generiskt fel.
Ett exempel på en webbplats som rapporterar ett fel efter ett inloggningsförsök.
Detta meddelande ger oss inte mycket information annat än en generisk indikation på loggens svårighetsgrad. Att söka i en logghanterare efter poster med ”Severe” i nivån eller meddelandet kan resultera i hundratals resultat utan någon garanti för att något av dem är relaterat till det aktuella problemet. Lyckligtvis registrerade vår Logger
också användarnamnet på den användare som upplevde felet, så vi kan filtrera på användarnamnet ”admin”.
Visa ett Java-undantag i Loggly.
Om vi borrar oss ner ser vi att orsaken till felet är en saknad eller ogiltig tabell. Detta är ett allvarligt problem eftersom det kan tyda på en oavsiktlig radering eller databaskorruption.
ERROR: Exception for user admincom.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'test_schema.users' doesn't exist...com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1651) at TestApplication.Test.doPost(Test.java:33) at javax.servlet.http.HttpServlet.service(HttpServlet.java:646) at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) at...
Om vi tittar på stapelspårningen ser vi att tjänsten misslyckades på rad 33 i Test.java. Denna rad består av ett SQL-meddelande som hämtar data om en användare från tabellen test_schema.users.
rs = stmt.executeQuery("SELECT * FROM test_schema.users WHERE username = " + username + " AND Password = " + password);
När vi försöker köra den här frågan i en SQL-frontend, upptäcker vi att tabellen ”test_schema.users” inte finns. Databasen har dock en tabell ”test_schema.user”. Vid något tillfälle kan en utvecklare ha misstagit sig på att skriva in fel tabellnamn och tryckt det reviderade formuläret till produktionen. Nu vet vi vad problemet är och var det dyker upp i vår kod.
Med SolarWinds Loggly kan du felsöka och hitta grundorsaken till problem genom att spåra fel genom applikationsstacken, genom flera logghändelser och till och med peka ut den relevanta kodraden i GitHub.
Lös problemet och förhindra att det upprepas
När vi nu har identifierat problemet och orsaken till det är det sista steget att lösa det. Vårt inloggningsexempel var ett överdrivet fall med en relativt enkel lösning, men du kan stöta på mer komplicerade fel som har sina rötter i olika delar av ditt program. Innan du hoppar på den snabba och smutsiga lösningen bör du noga överväga hur din ändring kommer att påverka programmet. Är detta verkligen den bästa lösningen på problemet? Är det möjligt att din ändring kommer att störa en annan komponent? Kommer den här lösningen att göra det svårt att införa nya lösningar eller funktioner längre fram? Kommer den här lösningen också att förhindra att liknande problem dyker upp senare?
Till exempel, vad händer om två olika användare lyckas skapa två separata konton med samma användarnamn och lösenord? Du skulle kunna tvinga fram ett unikt användarnamn för alla användare, men hur skulle det påverka din databasstruktur, din autentiseringskod och din befintliga användarbas? Du kan lägga till ett nytt unikt fält, t.ex. en e-postadress, och göra det obligatoriskt, men vad händer om en del av dina nuvarande användare inte har angett någon e-postadress? Vad händer om vissa e-postadresser delas mellan flera konton? Hur kommer dessa nya regler att påverka dina registrerings-, sök- och administrationssidor?
Du bör också ta hänsyn till fördröjningen mellan att skapa en lösning och distribuera den. I en företagsmiljö måste din kod granskas av andra utvecklare, integreras i kodbasen, byggas, testas av QA, staged och kanske gå igenom ytterligare flera steg innan den når produktionen. Ditt företag kan ha särskilda protokoll för kritiska eller tidskänsliga fel som du måste följa. När du har hittat en lösning ska du inkludera lösningen i din dokumentation, liksom alla rimliga lösningar på problemet. På så sätt kan dina kunder fortfarande använda din produkt och ditt supportteam kan minska antalet dubbla felrapporter medan lösningen tar sig till produktionen.
När du har distribuerat lösningen fortsätter du att övervaka din produktionsapplikation för att verifiera att problemet har lösts. Efter att ha distribuerat fixen för databasproblemet i exemplet ovan bör vi till exempel inte längre se händelser med ”Table ’test_schema.users’ doesn’t exist”. Om felen fortsätter eller om vi börjar se nya fel vet vi att lösningen inte fungerade. I idealfallet ser vi ett mönster som det som visas i följande skärmdump, där antalet fel sjunker till noll omedelbart efter att vi har distribuerat patchen.
Kartläggning av antalet loggar som innehåller ett fel i Loggly.
Mer verktyg för felsökning i produktionen
Men även om loggning är den beprövade metoden för felsökning och felsökning av program kan de här verktygen hjälpa dig att få mer insikt i hur programmet fungerar.
jdb
jdb, Java Debugger, är ett verktyg för felsökning av Java-klasser via kommandoraden. jdb är lätt att använda och levereras tillsammans med Java Development Kit. Genom att använda jdb skapas en ny Java Virtual Machine (JVM), vilket gör att du kan felsöka en klass utan att påverka några pågående program. Du kan också använda jdb för att felsöka pågående program genom att lägga till följande parametrar i ditt java-kommando:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n
När JVM:n startar tilldelas den ett portnummer för inkommande jdb-anslutningar. Du kan sedan ansluta till den pågående JVM-instansen med hjälp av jdb -attach:
$ jdb -attach
Detta kan du till exempel använda för att ansluta din felsökare till en pågående produktionsinstans. Var försiktig med att använda brytpunkter i det här scenariot, eftersom det kan pausa en aktiv tråd. Detta kan få konsekvenser om till exempel en kund använder programmet medan du felsöker det. Mer information finns i Java-dokumentationen om jdb.
OverOps
OverOps är en uppsättning verktyg för att övervaka program, analysera kod och upptäcka problem. Till skillnad från loggningsmjukvara, som förlitar sig på utdata som genereras av ett kört program, ansluter OverOps direkt till Java Virtual Machine för att kartlägga programmets kodbas, läsa variabler och registrera fel. Detta gör att den kan fånga upp fler fel och registrera fler data än till och med programmets loggningsramverk. OverOps använder en SaaS-modell (Software as a Service), där mätvärden samlas in och lagras på OverOps molnservrar. Du kan dock också distribuera den på plats. I båda fallen kan du visa dina data med hjälp av ett webbaserat gränssnitt.
BTrace
BTrace är ett spårningsverktyg som låter dig övervaka alla aspekter av din applikation, från klassnamn till fel. BTrace använder en aspektorienterad programmeringsmetod som innebär användning av annotationer, som anger var och hur BTrace övervakar din applikation. Följande BTrace-skript övervakar och loggar till exempel varje anrop till javax.swing
-paketet.
import com.sun.btrace.annotations.*;import static com.sun.btrace.BTraceUtils.*; @BTrace public class AllMethods { @OnMethod( clazz="/javax\.swing\..*/", method="/.*/" ) public static void m(@ProbeClassName String probeClass, @ProbeMethodName String probeMethod) { print(Strings.strcat("entered ", probeClass)); println(Strings.strcat(".", probeMethod)); }}
För mer information om BTrace, se BTrace GitHub-repository och Wiki.
Chronon
Chronon gör det möjligt att spola tillbaka och spela upp hela exekveringsflödet för ett program. Den registrerar varje enskild ändring som görs under en applikations livstid, så att du kan återskapa applikationens tillstånd vid varje given tidpunkt. Inspelningar sparas i en fil, vilket gör det enkelt att överföra exekveringshistoriken från en produktionsmaskin till en utvecklingsmaskin för testning.
Chronon består av Chronon Recording Server, som låter dig spela in Java-applikationer på distans, Embedded Chronon, som bäddar in inspelaren i en applikation, och Time Travelling Debugger, som låter dig spela upp inspelningar igen.
jhsdb
jhsdb (Java HotSpot Debugger) är en uppsättning verktyg för felsökning, analys och profilering av standard-JVM:en som tillhandahålls med både OpenJDK och Oracle JDK. Med jhsdb kan du ansluta dig till pågående Java-processer, ta ögonblicksbilder av stapelspår och till och med analysera kraschade JVM:er. Du kan använda den för att få tillgång till heap, kodcache, statistik om sophämtning med mera. Mer information finns på dokumentationssidan för jhsdb.
Spårning av transaktioner
När ett problem uppstår är det viktigt att veta var problemet startade och hur det påverkade resten av programmet. Detta är svårt nog i en monolitisk applikation, men blir ännu svårare i en distribuerad tjänsteorienterad arkitektur, där en enda förfrågan kan drabba dussintals tjänster. Det är inte alltid uppenbart vilken tjänst som innehåller grundorsaken till felet eller hur det påverkade andra tjänster. Spårning ger de data som behövs för att följa exekveringsvägen för din applikation och borra ner till den exakta orsaken till ett problem.
I avsnittet Debugging Production Problems i guiden gick vi igenom ett exempel där en användare hade svårt att logga in i en webbapplikation på grund av en ogiltig databastabell. I det här avsnittet visar vi hur spårning av transaktioner spelade en central roll för att lösa problemet.
Spårning av unika ID
För att kunna spåra en sekvens av logghändelser behöver du något sätt att unikt identifiera relaterade loggar. En miljö med flera användare kan generera hundratals identiska loggar, vilket gör det svårt att söka utifrån tidsstämpel eller Logger
. En enklare lösning är att inkludera en unik identifierare med relaterade loggposter. Denna identifierare kan vara ett användarnamn, ett sessions-ID, en API-nyckel eller en universellt unik identifierare (UUID). Lyckligtvis är ThreadContext perfekt lämpad för detta jobb.
I exemplet Debugging Production Problems hade vi ett webbaserat användargränssnitt som låg i en Tomcat-servlet som var ansluten till en MySQL-databas. Användarna skrev in sina inloggningsuppgifter på webbsidan, och efter att ha tryckt på submit körde servleten en fråga som jämförde deras inloggningsuppgifter med dem som fanns lagrade i databasen. Om användaren hade autentiserats framgångsrikt omdirigerades han eller hon till huvudsidan. Om ett fel inträffade loggades detaljer om felet och användarna fick ett generiskt meddelande.
Vi kunde felsöka detta problem genom att inkludera användarnas användarnamn i loggmeddelandet. Detta gjorde det möjligt för oss att snabbt söka efter logghändelser relaterade till adminanvändaren. Genom att bygga vidare på samma exempel kan vi använda ThreadContext.put()
för att mappa ett användarnamn till ett Logger
. När användarna skickar in sina inloggningsuppgifter går servleten in i doPost()
-metoden, som lägger till användarnas användarnamn i ThreadContext:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {ThreadContext.put("username", request.getParameter("username"));logger.info("Entering doPost().");...}
Användning av Log4j med mönstret %p : %m%n
resulterar i följande post:
INFO : Entering doPost().
Vi kan lägga till liknande händelser för att få tillgång till MySQL-databasen, lämna doPost-metoden och utföra andra åtgärder. På så sätt vet vi exakt vad användaren gjorde när undantaget inträffade om användaren utlöser ett undantag.
Spårning av metodanrop
Många loggningsramverk tillhandahåller inhemska metoder för att spåra en applikations exekveringsväg. Dessa metoder varierar något mellan ramverken, men följer samma allmänna format.
-
traceEntry()
markerar början av en metod. -
traceExit()
markerar slutet av en metod. För metoder som returnerar ett objekt kan du samtidigt returnera objektet och logga händelsen medreturn logger.exit(object)
. -
throwing()
markerar ett undantag som sannolikt inte kommer att hanteras, till exempel enRuntimeException
. -
catching()
markerar ett undantag som inte kommer att kastas om.
Du kan hitta ramspecifik information om dessa metoder i loggardokumentationen för Log4j, Logback och java.util.logging
.
Spårningsmetoder i Log4j
Som exempel på användning av Log4j ersätter vi metoderna Logger.info() i vår servlet med spårningsmetoder.
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { logger.entry(); ... logger.exit();}
Vi ändrar också Appender's PatternLayout
för att visa klassnamn, metod och radnummer (konverteringsmönstren %class
, %M
och %line
).
<PatternLayout pattern="%p %class %M %line: %m%n" />
Spårningsmetoder loggar händelser på TRACE
-nivån, vilket innebär att vi måste ändra Logger's
-nivån från DEBUG
till TRACE
. Annars kommer loggmeddelandena att undertryckas.
<Loggers> <Root level="trace"> <AppenderRef ref="Console"/> </Root></Loggers> TRACE DatabaseApplication.Login doPost 26: entry...TRACE DatabaseApplication.Login doPost 59: exit
Spårningsmetoder tillhandahåller också sina egna Markers
. Till exempel visar metoderna Logger.entry()
och Logger.exit()
ENTER
respektive EXIT
.
ENTER 14:47:41.074 TRACE DatabaseApplication.Login - EnterEXIT 14:47:41.251 TRACE DatabaseApplication.Login - Exit
Spårningsmetoder i SLF4J
SLF4J-användare kan dra nytta av MDC för att spåra logghändelser. I likhet med Log4j kan MDC-värden användas med en Appender med hjälp av konverteringsmönstret %X.
SLF4J tillhandahåller också entry()
, exit()
, throwing()
och catching()
metoder genom klassen XLogger
(Extended Logger). Du kan skapa en XLogger
-instans med hjälp av XLoggerFactory.getXLogger()
.
package DatabaseApplication;import org.slf4j.Logger;import org.slf4j.LoggerFactory; import org.slf4j.ext.XLogger;import org.slf4j.ext.XLoggerFactory; public class Login extends HTTPServlet { final static XLogger xlogger = XLoggerFactory.getXLogger(Login.class.getName()); final static Logger logger = LoggerFactory.getLogger(Login.class.getName()); @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { xlogger.entry(); .. xlogger.exit(); }}
Lägg till konverteringsmönstren %class och %line i logback.xml.
<configuration> <appender name="Console" class="ch.qos.Logback.core.ConsoleAppender"> <encoder> <pattern>%-5level %class{36} %M %L: %msg%xEx%n</pattern> </encoder> </appender> <root level="trace"> ... </root></configuration>
De resulterande loggposterna är identiska med dem som skapats av Log4j.
Hantering av minnesanvändning
Hantering av minne förbises ofta i språk på högre nivå, som Java. Även om den genomsnittliga mängden minne i moderna enheter ökar kan hög minnesanvändning ha stor inverkan på din applikations stabilitet och prestanda. När Java Virtual Machine inte längre kan allokera minne från operativsystemet kan ditt program avslutas och krascha. Om du vet hur du ska hantera minnet kan du förebygga problem när ditt program växer i storlek.
Föreställ dig till exempel att vi vill lagra en uppsättning siffror som börjar med ett. En användare anger en övre gräns och programmet lagrar varje heltal från ”1” till den gränsen. Vi använder en while-slinga med en räknare för att lägga till varje tal till en array.
import java.util.Scanner;...System.out.print("Please enter the maximum size of the array: ");Scanner scanner = new Scanner(System.in);int limit = scanner.nextInt(); ArrayList intArray = new ArrayList(); int count = 1;while (count <= limit) { intArray.add(count);}
Du kanske har märkt att count
inte ökar i slingan. Detta är ett stort problem; om användaren anger ett tal som är större än noll kommer arrayen att fortsätta att växa tills JVM använder allt tillgängligt minne och programmet kraschar.
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOf(Arrays.java:2245)at java.util.Arrays.copyOf(Arrays.java:2219)at java.util.ArrayList.grow(ArrayList.java:242)at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)at java.util.ArrayList.add(ArrayList.java:440)at MemoryTest.main(MemoryTest.java:10)
Garbage Collection
För att minska minnesanvändningen utför Java Virtual Machine en periodisk rensningsprocess som kallas garbage collection. Garbage collection söker upp minnesblock som inte längre används och gör dem tillgängliga för återanvändning. Följande resurser förklarar Javas skräpplockningsprocess mer i detalj.
- Java Garbage Collection Basics (Oracle)
- HotSpot Virtual Machine Garbage Collection Tuning Guide (Oracle)
- Understanding G1 GC Logs (Poonam Bajaj)
- Garbage Collection Tuning Guide (Atlassian)
Soprumkollektorn genererar diagnostisk information som kan vara användbar för att profilera prestandaprofilering av program. Du kan logga den här informationen genom att skicka -Xlog:gc till JVM när du startar programmet. Efter varje körning skriver skräpplockaren ut statistik om skräpplockningsprocessen i Unified JVM Logging-formatet. Här är ett exempel:
Using G1 Periodic GC disabled GC(0) Pause Young (Normal) (G1 Evacuation Pause) 7M->1M(64M) 8.450ms
Using G1
säger vilken skräpplockningsmetod som används. Garbage-First (G1)-samlaren är ofta aktiverad som standard på datorer med flera processorer och stora mängder RAM-minne. Periodic GC disabled
anger att sophämtningsprocessen inte kommer att upprepas. Den tredje raden talar om att detta är en evakueringspaus, där objekt kopieras mellan minnesregioner baserat på om de fortfarande används. Pause Young
talar om för oss att processen rensade den unga generationen, där nya objekt allokeras. Som ett resultat sjönk den totala minnesanvändningen av objekt i den unga generationen från 7M till 1M av 64M allokerade, och processen tog 8.450ms
.
Med garbage collector som skriver ut diagnostisk information till konsolen kan vi se hur mängden allokerat minne (heapstorleken) fortsätter att växa med tiden. Vi tar bort CPU-tiderna för enkelhetens skull.
$ java -Xlog:gc MemoryLeakDemo Using G1 Periodic GC disabled GC(0) Pause Young (Concurrent Start) (G1 Humongous Allocation) 25M->23M(64M) 4.372ms GC(1) Concurrent Cycle GC(1) Pause Remark 50M->34M(64M) 0.250ms GC(1) Pause Cleanup 94M->94M(124M) 0.057ms GC(1) Concurrent Cycle 169.695ms GC(2) Pause Young (Concurrent Start) (G1 Humongous Allocation) 94M->94M(124M) 3.738ms GC(3) Concurrent Cycle...
Diagram som visar en gradvis ökning av JVM-heapanvändningen över tiden.
Du kan lägga till tidsstämplar till varje post med -XX:+PrintGCDateStamps
och -XX:+PrintGCTimeStamps
. Du kan också logga garbage collectors utdata till en fil genom att skicka-Xlog:gc:file
: till JVM när du startar programmet.
Om du övervakar dina loggfiler med rsyslog kan du sedan vidarebefordra loggarna till ett centraliserat loggningssystem där de analyseras och är redo för analys i nära realtid. Så här ser ett tidslinje-diagram ut i Loggly. Den visar den totala heapstorleken samt heapstorleken före och efter att garbage collector körs.
Minnesläckor
En minnesläcka är en situation där ett program allokerar minne snabbare än det kan frigöra det. Det enklaste sättet att upptäcka en minnesläcka är när programmet inte längre svarar, blir instabilt eller orsakar OutOfMemoryErrors
. I Java hittar du fler exempel på Full GC
-samlingar när minnesanvändningen ökar.
Du kan lära dig mer om garbage collectors utdata och om att lokalisera minnesläckor i kapitlet Felsöka minnesläckor i Java-dokumentationen.
Allmänna resurser
Skärpa felsökningstiden utan att skriva en enda kodrad (Loggly)-Guide för att använda Takipi
The Ultimate Guide: 5 Methods for Debugging Production Servers at Scale (High Scalability)-Verktyg och tekniker för felsökning av produktionsproblem
Hantering av minne
Garbage Collection Tuning Guide (Oracle)-Guide för att förstå och finjustera Javas skräpkollektion
Understanding Java Garbage Collection (CUBRID Blog)-Guide för garb
Se det. Analysera den. Inspektera den. Lös det
Se vad som är viktigt.
STARTA GRATIS provperiod