Java Classloader

Java Classloader je v informatice součást běhového prostředí Javy (JRE),[pozn. 1] které umožňuje dynamické načítání Java tříd do Java Virtual Machine (JVM). Obvykle se třídy načítají pouze při jejich potřebě, tj. za běhu programu (lazy initialization). Java nemusí znát přesně všechny soubory a souborové systémy, z důvodu použití právě classloaderu. Funkci classloaderu zajišťuje koncept delegace.

Softwarová knihovna je sbírka souvisejícího objektového kódu. V Javě, se knihovny typicky zabalují do JAR souborů. Knihovny mohou obsahovat objekty různých datových typů. Nejdůležitějším typem objektu nacházejícím se JAR souboru je Java třída. Třída v tomto případě je myšlena jako pojmenování jednotky kódu. Classloader zajišťuje vyhledání místa, kde se nacházejí knihovny, čtení jejich obsahu a tedy i načítání tříd z těchto knihoven. Načítání typicky probíhá za běhu programu „na požádání“, což znamená, že se třída načte až v případě, kdy bude programem používána. Každá třída může být načtena pouze jediným daným classloaderem.

Každá třída v Javě musí být načtena pomocí classloaderu.[1] Navíc programy v Javě mohou používat externí knihovny (to jsou knihovny napsané a poskytované někým jiným než je autor programu) nebo můžeme čerpat pouze části kódu z několika knihoven.

Když se JVM (Java virtual machine) spustí, jsou používány následující 3 knihovny:[2][3]

  1. Bootstrap class loader
  2. Extensions class loader
  3. System class loader

Bootstrap classloader načte třídy core Java knihoven (runtime třídy),[4] které se nacházejí ve složce <JAVA_HOME>/jre/lib.[pozn. 2] Tento classloader, jehož část obsahuje jádro JVM, je v nativním (strojovém) kódu.

Extensions classloader načte kód z adresářů, které obsluhují různá rozšíření (<JAVA_HOME>/jre/lib/ext[5][pozn. 2] nebo načítá i z jiných adresářů specifikovaných ve specifikaci systému java.ext.dirs). Tato definice je implementována ve třídě sun.misc.Launcher$ExtClassLoader.

System classloader načte kód, který nalezne v java.class.path třídy, které chceme načíst, nalezneme v proměnné CLASSPATH. Vše je implementováno ve třídě sun.misc.Launcher$AppClassLoader.

Uživatelsky definované class loadery

Java classloader je oproti jádru JVM, napsaný v Javě. Je tedy možné si v samotné Javě vytvořit svůj vlastní class loader bez nutnosti znát přesně detailní strukturu Java Virtual Machine. Každý Java classloader má nadřazený classloader, ten musí být definován ještě před vytvořením jeho instance nebo musí být nastaven v výchozím classloaderu Java Virtual Machine.

Díky tomu je možné například:

  • načítat nebo odebírat třídy za běhu programu (načítání dynamických tříd za běhu programu, například z HTTP zdrojů). Toto jsou podstatné vlastnosti pro:
    • implementaci skriptovacích jazyků,
    • použití bean builderů,
    • podporu uživatelsky definovatelné rozšiřitelnosti,
    • umožňuje používat více jmenných prostorů, s tímto se můžeme setkat při používání protokolů CORBA / RMI,
  • změna způsobu načítání bytekódu, využití nalezne v zabezpečení, kde lze využít šifrovaného bytekódu[6]).
  • změnu již načteného bytekódu, které nalezne využití například v Aspektově orientovaném programování, konkrétně v utilitě zajišťující podporu load-time weavingu.

Class Loadery v JEE

Java Platform, Enterprise Edition (JEE) aplikační servery většinou načítají třídy z WAR nebo EAR souborů, které jsou sestaveny do stromu classloaderů, tímto oddělují naši aplikaci od jiných aplikací, ale dokáží sdílet jednotlivé třídy, které aplikace obsahuje, mezi nasazené moduly v ostatních aplikacích. Takzvané „servlet kontejnery“ jsou typickou implementací použití vícenásobných class loaderů.[1][7]

JAR hell

JAR hell je výraz podobající se tzv. DLL hell. Používá se k popisu všech různých způsobů, který proces načítání tříd může jakýmkoliv způsobem narušit nebo i úplně ukončit.[8] Existují 3 možnosti, kdy JAR hell může nastat:

  • Vývojář nebo deployer Java aplikace vytvoří nechtěně dvě různé verze knihoven, které jsou dostupné v systému. V systému toto není považováno za chybu a obě verze přijme. V tomto případě se třídy mohou načítat z jedné nebo druhé knihovny, kde mezi jednotlivými verzemi se samozřejmě mohou vyskytovat rozdíly. Přidání nové knihovny do seznamu načítaných knihoven namísto jejího nahrazení způsobí načítání některých tříd ze staré neopravené knihovny. Systém se tedy bude chovat tak, jako kdyby používal verzi starou.
  • Dvě knihovny (nebo knihovna a aplikace) požadují různé verze stejné třetí knihovny. Pokud obě verze třetí knihovny obsahují stejné názvy tříd, neexistuje způsob jak načíst obě verze třetí knihovny s použitím jednoho a téhož classloaderu.
  • Mezi komplexní problémy JAR hell patří takové, které využívají plnou složitost systému načítání tříd. Každý java program nemusí používat pouze jeden „plochý“ classloader, ale místo toho může být složen z více různých (většinou z mnoha) provázaných, navzájem spolupracujících classloaderů. Třídy načítané různými classloadery mohou ale ve své vzájemné návaznosti vytvářet neočekávané komplexní problémy, které při nepochopení vývojářem aplikace, často způsobují neočekávané výstupy a chyby, které mohou způsobit i pád programu.[9]

Pro odstranění těchto problémů s JAR hell byla aliancí OSGi uvedena specifikace (pod původním názvem JSR 8 z roku 1998) modulárního frameworku. Toto řešení je v současnosti používáno ve virtuálních strojích v Java ME, SE a EE. Ve všech těchto uvedených verzích Javy je předpokládáno i použití specifikace ve verzích následujících. Hlavní myšlenkou této specifikace závisí na použití metadat v JAR manifestu, JAR soubory (neboli tzv. bundles) jsou sestaveny na základě jednotlivých balíčků (package). Bundles poté mohou jednotlivé balíčky exportovat, importovat, přidělovat jim přístupová práva, určovat strukturu pro další rozšířitelnost a také dokáží spravovat tzv. verzování.

K předcházení problémům s JAR knihovnami vznikla v roce 2005 pracovní skupina Java Community Process – JSR 277. Rozlišuje tzv. Java Module System, který má za cíl určit nový distribuční formát, schéma modulu podporujících verzování a společné úložiště tzv. repozitáře (s podobnými vlastnosti již funguje Microsoft .NET Global Assembly Cache). V prosinci Sun vyhlásil JSR 277 jako „dormant“, tedy je prohlášen za svou finální verzi.[10]

Poznámky

  1. označování JRE zaniká s verzí Java 11
  2. a b Cesty k souborům se mohou lišit podle verze (např. Java SE 11 a vyšší)

Reference

V tomto článku byl použit překlad textu z článku Java_Classloader na anglické Wikipedii.

  1. a b CHRISTUDAS, Binildas. Internals of Java Class Loading [online]. onjava.com, 2005-01-26 [cit. 2009-10-02]. Dostupné online. (anglicky) 
  2. Understanding Extension Class Loading [online]. java.sun.com, 2008-02-14 [cit. 2009-12-08]. Dostupné online. (anglicky) 
  3. SOSNOSKI, Dennis. Classes and class loading [online]. ibm.com, 2003-04-29 [cit. 2008-01-26]. Dostupné online. (anglicky) 
  4. Tyto knihovny jsou uloženy v Jar souborech, mezi které patří rt.jar, core.jar, server.jar, atd.
  5. Understanding Extension Class Loading [online]. Oracle [cit. 2020-02-12]. Dostupné online. (anglicky) 
  6. ROUBTSOV, Vladimir. Cracking Java byte-code encryption [online]. javaworld.com, 2003-05-09 [cit. 2008-01-26]. Dostupné v archivu pořízeném dne 2008-05-02. (anglicky) 
  7. DEBOER, Tim; KARASIUK, Gary. J2EE Class Loading Demystified [online]. ibm.com, 2002-08-21 [cit. 2008-01-26]. Dostupné online. (anglicky) 
  8. http://incubator.apache.org/depot/version/jar-hell.html
  9. GÜLCÜ, Ceki. Taxonomy of class loader problems with Jakarta Commons Logging [online]. QOS.ch, 2005-02-02, rev. 2010-04-09 [cit. 2020-02-12]. Dostupné online. (anglicky) 
  10. Archivovaná kopie. www.osgi.org [online]. [cit. 2014-05-16]. Dostupné v archivu pořízeném dne 2012-03-02. 

Související články

Externí odkazy