Home Innovatie & Strategie Permgen geheugen verlagen

Permgen geheugen verlagen

78

Gedeelde bibliotheken
Niet zo lang geleden werd mij gevraagd om een advies om het geheugen gebruik van een Java webapplicatie op een JBoss container te verminderen. Het project bestond uit meerdere webapplicaties die allemaal op dezelfde container werden deployed. Een van mijn adviezen was om te overwegen bibliotheken te delen. Per webapplicatie worden bibliotheken in het “Permgen” geheugen geladen. Als dezelfde bibliotheek in iedere webapplicatie wordt meegeleverd, dan wordt er meer “Permgen” geheugen gebruikt. Dit komt doordat iedere webapplicatie opnieuw dezelfde bibliotheek zal laden.

Maven en gedeelde bibliotheken
De meeste containers bieden de mogelijkheid om bibliotheken te delen tussen verschillende webapplicaties. Als een project met Maven wordt gebouwd dan kun je deze gedeelde bibliotheken in een common project plaatsen1. Dit common project kan dan in de gedeelde folder van de webcontainer worden geplaatst om te voorkomen dat iedere webapplicatie opnieuw dezelfde bibliotheek gaat inladen2.

In een multi-module maven project met meerdere webapplicaties, geeft het volgende bash commando een overzicht van de gedupliceerde compile-time bibliotheken in de verschillende modules (gebruik profielen om alleen de modules te selecteren die .war bestanden opleveren):

mvn dependency:tree | grep “compile” | sed ‘s/\[INFO\]//g;s/+-//g;s/\\-//g;s/|//g;s/^[ \t]*//;s/[ \t]*$//’ | sort | uniq -c | awk ‘$1+0 >= 2’ | sort –r

Dit commando zou bijvoorbeeld het volgende resultaat kunnen geven:

6 org.slf4j:slf4j-log4j12:jar:1.6.2:compile

6 org.slf4j:slf4j-api:jar:1.6.2:compile

6 log4j:log4j:jar:1.2.16:compile

5 oro:oro:jar:2.0.8:compile

De eerste kolom laat zien hoe vaak Maven de compile time dependecy in de tree is tegengekomen. Daarna volgt de naam en het versienummer. Deze bibliotheken zijn kandidaten om naar een common project te verplaatsen. Het projectteam zal moeten overwegen of het verstandig is om bibliotheken te delen. Als de bibliotheken worden gedeeld in een common module, dan moeten ze in de shared lib folder van de container worden geplaatst.

Binaries en gedeelde bibliotheken
Op het moment dat ik werd ingeschakeld om het geheugenverbruik te analyseren was de broncode niet beschikbaar. Ik had wel toegang tot de binaries (.war bestanden) in de deployment directory van de webcontainer. Het volgende commando werd uitgevoerd om uit te zoeken welke bibliotheken werden gedeeld tussen twee binaries:

comm -12 <(unzip -l webapp1.war | grep “lib” | uniq | sort) <(unzip -l webapp2.war | grep “lib” | uniq | sort)

Dit commando zou bijvoorbeeld het volgende resultaat kunnen geven:

1352924  03-03-2012 17:23   WEB-INF/lib/opensaml-2.5.1-1.jar

1501575  03-03-2012 17:16   WEB-INF/lib/guava-10.0.1.jar

Als de lijst van gedeelde bibliotheken erg lang is, kan het volgende commando worden gebruikt om het aantal gedeelde bibliotheken weer te geven:

comm -12 <(unzip -l webapp1.war | grep “lib” | uniq | sort) <(unzip -l webapp2.war | grep “lib” | uniq | sort) | wc –l

Ook kan het handig zijn om uit te rekenen hoe groot het aantal gedeelde bibliotheken op disk is. Dit is niet 1 op 1 aan het permgen geheugen te relateren, maar het geeft wel een indicatie hoeveel kleiner de binaries op disk zouden kunnen worden. Met het volgende commando kan dit worden geprint:

comm -12 <(unzip -l webapp1.war | grep “lib” | uniq | sort) <(unzip -l webapp2.war | grep “lib” | uniq | sort) | awk ‘{ sum+=$1} END {print sum}’

Permgen besparing
Om de geheugenbesparing te illustreren ga ik uit van de Tomcat archetype3. Door het volgende commando uit te voeren wordt een voorbeeld web project aangemaakt:

mvn archetype:generate -DarchetypeGroupId=org.apache.tomcat.maven

-DarchetypeArtifactId=tomcat-maven-archetype -DarchetypeVersion=2.0-beta-1

Voor het voorbeeld wordt de basic-webapp 5 keer (onder een verschillende context) deployed op een Tomcat 7 container. De eerste keer wordt de basic-webapp met alle compile dependencies opgeleverd (exact zoals de archetype). De tweede keer worden alle bibliotheken (behalve die beginnen met basic) op provided gezet en in de lib map van de container geplaatst. De webapp maakt onder andere gebruik van Spring, slf4j, log4j, wsdl4j en jaxb. Het permgen geheugen na deployment ziet er als volgt uit:

43Mb Permgen in gebruik (compile dependencies)

Permgen1-300x85

23Mb Permgen in gebruik (provided dependencies)

Permgen2-300x80

Het voorbeeld laat zien dat het gebruikte permgen geheugen bijna verdubbelt als de applicatie 5 keer wordt geladen en de bibliotheken niet in de shared lib folder worden geplaatst. Soms is het mogelijk om het permgen geheugen nog verder te reduceren door verschillende modules van dezelfde logging/collections/io bibliotheek gebruik te laten maken. Als module A bijvoorbeeld Log4j gebruikt en module B gebruikt Logback, dan worden twee bibliotheken van hetzelfde type geladen. Het kost in dat geval minder geheugen als beide modules er bijvoorbeeld voor kiezen om gebruik te maken van de Logback bibliotheek.

  1. http://www.sonatype.com/books/mvnex-book/reference/optimizing-sect-dependencies.html
  2. https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
  3. https://tomcat.apache.org/maven-plugin-2.0-beta-1/archetype.html

LAAT EEN REACTIE ACHTER

Vul alstublieft uw commentaar in!
Vul hier uw naam in