Portál AbcLinuxu, 21. května 2024 00:38

Co možná (ne)víte o Javě

3. 10. 2003 | Daniel Michalik
Články - Co možná (ne)víte o Javě  

Java dlouhou dobu polarizovala linuxovou komunitu. Možná slibovala víc, než byla schopna splnit. Vytýkalo se jí, že je pomalá, že se nehodí pro vývoj desktopových aplikací nebo zabírá spoustu paměti. Jak je na tom Java dnes?

Pokud Javu zatím nepoužíváte, přinese vám článek náměty k zamyšlení. Srovnáme výkon Javy s C++ (zaručuji překvapení), popíšeme optimalizující HotSpot kompilátory v Sun J2SDK 1.4.2 a řekneme si o správě paměti a grafickém uživatelském rozhraní.

Budeme popisovat knihovny a nástroje, které se osvědčily ve vývoji aplikací provozovaných v režimu 24x7, tedy 24 hodin denně a 7 dnů v týdnu, u kterých je restart velmi nežádoucí a pád naprosto nepřijatelný (stejně jako u našeho oblíbeného operačního systému :-). Proto pominu open source projekty, které zatím s distribucí Sunu nesnesou srovnání a nabízí obvykle pouze podmnožinu celé platformy.

Výkon

Níže uvádím výsledky numerického benchmarku SciMark 2.0, který simuluje typicky výpočetně náročnou aplikaci. Pro účely spravedlivého srovnání jsem upravil zdrojový kód třídy Random.java, která se používá v testu Monte Carlo, tak, aby stejně jako verze C neobsahavala žádné synchronizované metody. Test byl prováděn na systému Red Hat Linux 8.0 s procesory Intel Xeon 2.66GHz. Zdrojové kódy SciMarku jsou k dispozici na http://math.nist.gov/scimark. Pro kompilaci C verze byl použit originální makefile, který vyvolával C kompilátor s přepínačem -O6.

GNU C++ 3.2 HotSpot Server VM 1.4.2 HotSpot Client VM 1.4.2
Composite Score 456.67 510.56 207.24
FFT (Mflops) 283.16 300.52 98.17
SOR (Mflops) 391.66 663.55 356.32
Monte Carlo (Mflops) 144.32 179.26 54.16
Sparse matmult (Mflops) 697.19 440.43 139.56
LU (Mflops) 767.04 969.01 387.99

HotSpot Server ve všech testech kromě Sparse matmult dost výrazně překonává C verzi. Výsledek je o to zajímavější, že Java narozdíl od C provádí za běhu kontrolu indexu pole a všechny metody v Javě jsou virtuální!

Technologie HotSpot VM

V dalším výkladu pod pojmem kompilátor budeme rozumět modul virtuálního stroje Javy, který překládá Java bytekód do kódu procesoru, případně provádí optimalizace (nezaměňovat s kompilátorem zdrojového kódu - tím se zabývat nebudeme).

První generace virtuálních strojů Javy v dobách verze 1.0 používala pouze interpretaci bez žádných optimalizací.

Druhá generace strojů použitých pro řadu 1.1 a 1.2 používala tzv. JIT (just in time) kompilátory, které překládaly bytekód třídy při načtení do kódu procesoru počítače. Překládaly se všechny metody třídy a při startu aplikace nebyl čas na provádění kvalitnějších optimalizací. Jednou přeložený kód byl ponechán po celou dobu běhu programu.

Třetí generace strojů (počínaje verzí 1.3) obsahuje plně adaptivní kompilátory, které sledují běh programu, hledají jeho slabá místa ("hot spots") a ty pak silně optimalizují. Vycházejí z aplikace zásady, že 80 % času program stráví ve 20 % kódu. Zbylých 80 % programu může být dokonce zbytečné převádět do kódu procesoru a postačí interpretace. Jedná se o absolutní high-end technologie dynamických interpretů, jak uvidíme dále.

Standardní distribuce Sunu obsahuje dvě verze těchto strojů. Tzv. "HotSpot Client" a "HotSpot Server", které se liší tím, kolik času si mohou dovolit na provádění optimalizací.

HotSpot Client je předvolený virtuální stroj, pokud spouštíte Java aplikaci. Explicitně se tyto stroje rozlišují pomocí přepínačů při spouštění příkazu:

java ...
java -client ...
java -server ...

HotSpot Server VM

Tento virtuální stroj je doporučeno použít pro dlouho běžící aplikace, u kterých nevadí, že startují déle nebo zabírají více paměti. Právě to umožňuje provádět podstatně agresívnější optimalizaci. Charakteristika HotSpot Server VM:

HotSpot Client

Tento virtuální stroj provádí minimální množství optimalizací za účelem co nejrychlejšího startu aplikace a poskytutí co nejrychlejší odezvy pro uživatele.

Nikde ovšem není řečeno, že pro klientské aplikace nelze použít HotSpot Server. Prostě se musíte smířit s tím, že aplikace bude nabíhat déle a bude mít zpočátku pomalejší odezvy. Ale s tím, jak jsou procesory stále rychlejší, je tento faktor stále více zanedbatelný.

Správa paměti

Jeden z největších přínosů Javy pro programátory je v tom, že je to první široce dostupný programovací jazyk, který poskytuje automatickou správu paměti (garbage collection). V tradičních jazycích se dynamická alokace a vrácení paměti provádí pomocí explicitního volání příslušných funkcí (malloc/free). V praxi to bývá zdrojem úbytků paměti, chyb a pádů programů, problémů s výkonem (!) a potíž při psaní opakovatelně využitelného kódu (různé moduly musí respektovat společná pravidla).

Garbage collector Javy automaticky provádí uvolňování nepotřebné paměti v pozadí a to tak, že vrátí paměť objektu, když může "dokázat", že objekt již není déle pro běžící program dosažitelný. Takový důkaz není v praxi vůbec triviální a není zde prostor pro naivní algoritmy.

Například microsoftí technologie COM nebo Python jsou založeny na tzv. reference counting neboli počítání odkazů na objekty. Když se na objekt nikdo neodkazuje, čítač má hodnotu 0 a objekt se skartuje. V praxi se ovšem používají složité struktury, u kterých vznikají cyklické odkazy. Lze si např. představit, že objekt A se odkazuje na B a B se odkazuje zpětně na A. Čítače odkazů budou u obou objektů rovny 1 a tudíž nedojde k jejich uvolnění. Pokud navíc tyto objekty drží nějaký systémový zdroj (např. neuzavřené databázové připojení nebo soubor), máme vážný problém.

Dalším závažným problémem, který má dopad na výkon systému, je fragmentace paměti, která se projeví obzvlášť u dlouho běžících programů (serverové procesy). Fragmentace vzniká postupně tím, jak program alokuje novou paměť a vrací tu, kterou nepotřebuje. Taková paměť pak připomíná silně děravé síto, kvůli němuž musí operační systém přidělit programu více paměťových stránek, než odpovídá jeho "teoretické" spotřebě. Systém pak více "swapuje". V praxi se to stane velice snadno, když nastane špička provozu nějakého serverového procesu (např. mail nebo web server). Jak a zda vůbec se s tím tvůrci serverů vůbec vypořádavají mi není známo. V každém případě při použití jazyků, které používají ukazatele (C, C++, .NET!), se to jeví jako velmi problematické.

Tradičně byly garbage collectory považovány za pomalé a vzhledem k výše popsaným problémům je to pochopitelné. Navíc musí občas pozastavit provádění vlastní aplikace (ta pak v extrémním případě "zamrzne" i na několik vteřin) a provést úklid paměti. Velmi dobrá zpráva je, že výzkum v této oblasti pokročil natolik, že výkon systémů s moderními garbage collectory je lepší než u modelu s explicitním uvolňováním paměti.

HotSpot Garbage Collector

Java HotSpot VM obsahuje prokročilý systém správy paměti, který uvolňuje všechny nedostupné objekty a odstraňuje problém fragmentace paměti a občasného zamrzání aplikace. Stává se tak výbornou platformou pro provoz trvale běžících aplikací s požadovanou vysokou propustností, rychlou odezvou a takových, ve kterých jsou úbytky paměti nebo nedostatek paměti v důsledku fragmentace velmi nežádoucí.

Throughput Collector

Jedná se o typ collectoru maximalizujícího propustnost aplikace ve víceprocesorových systémech. Tento garbage collector pracuje paralelně na více procesorech, čímž se zkrátí čas pro uvolňování paměti a tím se zvýší průchodnost celého systému. Vhodný např. pro použití u aplikačních serverů, kdy požadujeme co nejkratší odezvy.

Throughput Collector se aktivuje pomocí přepínače -XX:+UseParallelGC.

Concurrent Low Pause Collector

Tento collector minimalizuje přestávky, na které musí zastavit aplikaci. Na druhou stranu tento garbage collector běží ve vlákně, které konkuruje aplikaci, a tak na jednoprocesorovém systému může dojít k nepatrnému snížení celkové průchodnosti. Výborné se hodí pro aplikace, které vyžadují plynulost (např. animace - viz Java2Demo). Mohu potvrdit, že narozdíl od starších virtuálních strojů Javy jsem skutečně žádné trhané animace už neregistroval (včetně aplikací v Java3D).

Concurrent Low Pause Collector se aktivuje pomocí přepínače -XX:+UseConcMarkSweepGC.

Desktopové aplikace

Java byla dlouho kritizována jako nevhodná platforma pro vývoj klientských grafických aplikací. I zde bohužel platí, že 100krát opakovaná lež se stává pravdou a tak zatímco jedni kritizovali, druzí pracovali a ukázalo se, že Java mezitím dozrála do stavu, kdy se hodí pro grafické uživatelské rozhraní víc než dost.

Swing nebo SWT?

Pro tvorbu profesionálního uživatelského rozraní máme dnes v zásadě 2 možnosti. Buď knihovnu Swing (známou též jako JFC neboli Java Foundation Classes), která je součástí každé distribuce Javy (od verze 1.2), a nebo knihovnu SWT (neboli Standard Widget Toolkit), jejíž vývoj sponzorovala IBM v rámci projektu vývojového prostředí Eclipse.

Pro úplnost ještě dodám, že existují projekty s Java bindings pro GNOME a KDE, nicméně jejich použití výrazně omezuje počet cílových platforem, na kterých lze vyvíjenou aplikaci provozovat.

Swing

Swing je napsán celý v Javě pomocí rozhraní Java2D, z čehož automaticky plynou tyto důsledky:

  1. O veškerou správu paměti se stará javovský garbage collector, což výrazně omezuje riziko vyčerpání systémových zdrojů.
  2. Uživatelské rozhraní je na všech platformách stejné (pochopitelně kromě fontů). Z hlediska programátora je to výhoda, protože aplikace se odladí jednou a obvykle bez problémů běží i na jiné platformě nezávisle na verzích různých knihoven operačního systému. Použije-li se navíc tzv. systémový look&feel, aplikace dost dobře emuluje vzhled i chování aktuálního uživatelského rozhraní. Java ve verzi 1.4.2 obsahuje poměrně použitelnou emulaci GTK, ve verzi 1.5 je slíbeno další zdokonalení.
  3. Prvky uživatelského rozhraní lze dále v Javě rozšiřovat, upravovat způsob kreslení, přidávat vlastní vykreslovací logiku. Snad proto se o Swingu říká, že je extrémně přizpůsobitelný.

Vzhled předvoleného look&feelu (Metal) je trnem v oku spoustě uživatelů. Toto už dnes není problém, protože existují atraktivní a profesionální look&feels, kterým se z hlediska grafického designu nedá nic vytknout. Pro příklad se podívejte na odkaz http://www.jgoodies.com, kde také najdete užitečné aplikace ke stažení zdarma. Ukázky spousty profesionálních aplikací ve Swingu najdete také přímo u Sunu na adrese http://java.sun.com/products/jfc/tsc.

Rozhraní Java2D je velmi pokročilým rozhraním pro práci s grafikou. Poskytuje antialiasing, texturování, alpha blending, množinové geometrické operace, logické rastrové operace a spoustu jiných zajímavých věcí. Výkon linuxové implementace do verze 1.3 silně pokulhával, ve verzi 1.4.2 jej lze označit za dobrý a ve verzi 1.5 se můžeme těšit na akceleraci pomocí hardware podporujícího OpenGL (z diskuzních skupin, do kterých přispívají sunovští inženýři, se zdá, že lze očekávat špičkový výkon).

Ukázka možností Java2D:

java2d1

java2d1

java2d1

SWT

SWT je tím, čím mělo být AWT, tedy javovskou obálkou nad nativními prvky uživatelského rozhraní poskytovanou hostitelským operačním systémem. S jedním drobným rozdílem: SWT to dělá opravdu dobře.

Prvotní příčinou pro vznik SWT byl zřejmě kdysi neuspokojivý výkon Swingu, což už dnes neplatí.

Dalším argumentem pro vývoj SWT byla otázka, proč znova vymýšlet již vymyšlené a odladěné a dělat všechno úplně znova (přesně to dělá Swing). Místo toho je lepší použít kvalitní, odladěný kód knihoven uživatelských prvků, jako je např. GTK nebo Motif. Navíc se aplikace chová a vypadá tak, jak je uživatel ve svém systému zvyklý.

Jistým problémem je použití nativního kódu a nativních zdrojů operačního systému, což vyžaduje explicitní uvolňování objektů barva, štětec, font apod., čímž ztrácíme výhody, které nám poskytuje javovský garbage collector a vnášíme potenciální zdroj chyb. Pokud tedy potřebujete nejen použít hotové prvky, ale také kreslit, je zřejmě lepší volbou Swing a Java2D.

Pokud vyvíjíte aplikaci pro SWT, musíte počítat s laděním na více platformách a musíte se smířit s tím, že na každé platformě bude aplikace nejen jinak vypadat(look) ale bude se i jinak chovat(feel). Budete se muset zajímat o to, které verze těch či oněch závislých systémových knihoven uživatel používá, zkrátka nebude to bez problémů.

eclipse

Srovnání Swingu a SWT knihoven není vůbec snadné a je těžké argumenty nenaštvat zastánce té či oné knihovny. Situace silně připomíná konkurenci ve světě Smalltalku, kde se výrobci také rozdělili do dvou táborů: obálka nad nativním uživatelským rozhraním a emulované rozhraní vykreslované plně v režii Smalltalku. Jedno je jisté: oba toolkity tu zůstanou a je to tak dobře, protože jejich konkurence bude podporovat pokrok. Pokud dobře zvládáte jeden z nich, nemá smysl investovat do zvládnutí druhého. Spoustě vývojářů je bližší Swing, protože je v čisté Javě a je víc objektově orientován.

Instalace Javy v Linuxu

Pokud si chcete Javu vyzkoušet a nemáte ji nainstalovanou, proveďte níže popsané kroky.

Stáhněte si přímo z http://java.sun.com instalační soubor javy pro Linux (pro verzi 1.4.2 je to j2sdk-1_4_2-linux-i586.rpm.bin). Tento zkopírujeme do nějakého adresáře, kde jej z příkazového řádku spustíme. Objeví se licenční ujednání, které pomocí mezerníku odklikáme, a nakonec napíšeme yes. Tímto vznikne instalační soubor j2sdk-1_4_2-linux-i586.rpm, který pomocí příkazu rpm -ivh j2sdk-1_4_2-linux-i586.rpm nainstalujeme.

Poznámka: vyše popsané kroky platí pro RedHat Linux. Distribuce nepoužívající rpm mohou vyžadovat jiný postup.

Při instalaci vznikl adresář /usr/java/j2sdk1.4.2, v jehož podadresáři bin je řada nástrojů pro vývoj a spouštění Java aplikací. Tento adresář je vhodné přidat do cesty, například editací souboru /etc/profile:

JAVA_HOME=/usr/java/j2sdk1.4.2
PATH=$PATH:$JAVA_HOME/bin
...
export JAVA_HOME ...

Chcete-li si spustit demo ukázané na obrázku, proveďte tyto kroky:

cd /usr/java/j2sdk1.4.2/demo/jfc/Java2D
java -jar Java2Demo.jar

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.