Přechod z Javy 8 na Javu 11

Neexistuje žádné řešení pro přechod kódu z Javy 8 na Javu 11. Pro ne triviální aplikaci může být přesun z Javy 8 na Javu 11 značné množství práce. Mezi potenciální problémy patří odebrání rozhraní API, zastaralé balíčky, použití interního rozhraní API, změny zavaděčů tříd a změny uvolňování paměti.

Obecně platí, že se přístupy snaží spustit na Javě 11 bez rekompilace nebo zkompilovat pomocí sady JDK 11. Pokud je cílem co nejrychleji zprovoznění aplikace, je často nejlepším přístupem jen pokus o spuštění v Javě 11. Cílem knihovny bude publikovat artefakt, který je zkompilován a testován pomocí sady JDK 11.

Přechod na Javu 11 stojí za to. Od Javy 8 byly přidány nové funkce a vylepšení. Tyto funkce a vylepšení zlepšují spouštění, výkon, využití paměti a poskytují lepší integraci s kontejnery. A existují doplňky a úpravy rozhraní API, které zlepšují produktivitu vývojářů.

Tento dokument se týká nástrojů pro kontrolu kódu. Řeší také problémy, na které můžete narazit a doporučení k jejich řešení. Měli byste se také podívat na další příručky, jako je průvodce migrací Oracle JDK. Postup, jak vytvořit stávající modulární kód, se zde nevztahuje.

Panel nástrojů

Java 11 má dva nástroje, jdeprscan a jdeps, které jsou užitečné pro vychytávání potenciálních problémů. Tyto nástroje lze spouštět u existujících souborů třídy nebo souborů JAR. Můžete posoudit úsilí o přechod bez nutnosti rekompilovat.

jdeprscan hledá použití zastaralého nebo odebraného rozhraní API. Použití zastaralého rozhraní API není blokující problém, ale je to něco, co se má podívat. Existuje aktualizovaný soubor JAR? Potřebujete protokolovat problém, který řeší použití zastaralého rozhraní API? Použití odebraného rozhraní API je blokující problém, který je potřeba vyřešit před pokusem o spuštění v Javě 11.

jdeps, což je analyzátor závislostí třídy Java. Při použití s --jdk-internals možností vám jdeps řekne, která třída závisí na tom, na kterém interním rozhraní API. Interní rozhraní API můžete dál používat v Javě 11, ale nahrazení využití by mělo být prioritou. Nástroj pro analýzu závislostí java na wikiwebu OpenJDK doporučil nahrazení některých běžně používaných interních rozhraní API sady JDK.

Pro Gradle i Maven existují moduly plug-in jdeps a jdeprscan . Tyto nástroje doporučujeme přidat do skriptů sestavení.

Samotný kompilátor Java, javac, je dalším nástrojem na panelu nástrojů. Z kompilátoru se zobrazí upozornění a chyby, které se zobrazí z souboru jdeprscan a jdeps . Výhodou použití souboru jdeprscan a jdeps je, že tyto nástroje můžete spouštět přes existující soubory jar a soubory tříd, včetně knihoven třetích stran.

Co jdeprscan a jdeps nemůže dělat, je varovat o použití reflexe pro přístup k zapouzdřeného rozhraní API. Reflexní přístup se kontroluje za běhu. Nakonec musíte kód spustit na Javě 11, abyste věděli s jistotou.

Použití metody jdeprscan

Nejjednodušší způsob, jak použít soubor JAR , je dát mu soubor JAR z existujícího sestavení. Můžete mu také dát adresář, například výstupní adresář kompilátoru nebo název jednotlivé třídy. --release 11 Pomocí této možnosti získáte kompletní seznam zastaralého rozhraní API. Pokud chcete určit prioritu, které zastaralé rozhraní API se má pocházet, vytočte nastavení zpět na --release 8. Rozhraní API, které bylo v Javě 8 zastaralé, se pravděpodobně odebere dříve než rozhraní API, které bylo v poslední době zastaralé.

jdeprscan --release 11 my-application.jar

Nástroj jdeprscan vygeneruje chybovou zprávu, pokud má potíže s řešením závislé třídy. Například, error: cannot find class org/apache/logging/log4j/Logger. Přidání závislých tříd do --class-path cesty třídy nebo použití aplikace se doporučuje, ale nástroj bude pokračovat ve kontrole bez ní. Argument je --class-path. Nebudou fungovat žádné jiné varianty argumentu cesty třídy.

jdeprscan --release 11 --class-path log4j-api-2.13.0.jar my-application.jar
error: cannot find class sun/misc/BASE64Encoder
class com/company/Util uses deprecated method java/lang/Double::<init>(D)V

Tento výstup nám říká, že com.company.Util třída volá zastaralý konstruktor java.lang.Double třídy. Javadoc doporučí rozhraní API, aby se používalo místo zastaralého rozhraní API. Žádné množství práce se nevyřeší, protože se jedná o error: cannot find class sun/misc/BASE64Encoder rozhraní API, které bylo odebráno. Vzhledem k tomu, že java 8 by java.util.Base64 měla být použita.

Spusťte jdeprscan --release 11 --list , abyste získali představu o tom, co rozhraní API bylo od Javy 8 zastaralé. Seznam odebraných rozhraní API získáte spuštěním jdeprscan --release 11 --list --for-removalpříkazu .

Použití sady jdeps

K vyhledání závislostí na interním rozhraní API sady JDK použijte sadu jdeps--jdk-internals. V tomto příkladu je potřeba možnost --multi-release 11 příkazového řádku, protože log4j-core-2.13.0.jar je soubor jar s více verzemi. Bez této možnosti si jdeps stěžuje, pokud najde soubor jar s více verzemi. Možnost určuje, která verze souborů tříd se má zkontrolovat.

jdeps --jdk-internals --multi-release 11 --class-path log4j-core-2.13.0.jar my-application.jar
Util.class -> JDK removed internal API
Util.class -> jdk.base
Util.class -> jdk.unsupported
   com.company.Util        -> sun.misc.BASE64Encoder        JDK internal API (JDK removed internal API)
   com.company.Util        -> sun.misc.Unsafe               JDK internal API (jdk.unsupported)
   com.company.Util        -> sun.nio.ch.Util               JDK internal API (java.base)

Warning: JDK internal APIs are unsupported and private to JDK implementation that are
subject to be removed or changed incompatibly and could break your application.
Please modify your code to eliminate dependence on any JDK internal APIs.
For the most recent update on JDK internal API replacements, please check:
https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool

JDK Internal API                         Suggested Replacement
----------------                         ---------------------
sun.misc.BASE64Encoder                   Use java.util.Base64 @since 1.8
sun.misc.Unsafe                          See http://openjdk.java.net/jeps/260   

Výstup poskytuje dobré rady k odstranění použití interního rozhraní API sady JDK! Pokud je to možné, navrhuje se náhradní rozhraní API. Název modulu, ve kterém je balíček zapouzdřen, je uveden v závorkách. Název modulu lze použít s --add-exports nebo --add-opens pokud je nutné explicitně přerušit zapouzdření.

Použití sun.misc.BASE64Encoder nebo sun.misc.BASE64Decoder způsobí java.lang.NoClassDefFoundError v Javě 11. Kód, který používá tato rozhraní API, musí být upraven tak, aby používal java.util.Base64.

Pokuste se eliminovat použití libovolného rozhraní API pocházejícího z modulu jdk.unsupported. Rozhraní API z tohoto modulu bude odkazovat na návrh vylepšení sady JDK (JEP) 260 jako navrhované nahrazení. V maticovém přehledu JEP 260 říká, že použití interního rozhraní API bude podporováno, dokud nebude k dispozici náhradní rozhraní API. I když váš kód může používat interní rozhraní API sady JDK, bude dál běžet aspoň chvíli. Podívejte se na JEP 260, protože odkazuje na nahrazení některých interních rozhraní API. Popisovače proměnných se dají použít místo některého rozhraní SUN.misc.Unsafe API, například.

jdeps může provádět více než jen vyhledávání použití interních souborů JDK. Je to užitečný nástroj pro analýzu závislostí a generování souborů s informacemi o modulu. Další informace najdete v dokumentaci .

Použití javac

Kompilace pomocí sady JDK 11 bude vyžadovat aktualizace skriptů, nástrojů, testovacích architektur a zahrnutých knihoven. -Xlint:unchecked Pomocí možnosti javac získejte podrobnosti o použití interního rozhraní API sady JDK a dalších upozornění. Může být také nutné použít --add-opens nebo --add-reads vystavit zapouzdřené balíčky kompilátoru (viz JEP 261).

Knihovny můžou zvážit balení jako soubor jar s více verzemi. Soubory jar s více verzemi umožňují podporovat moduly runtime Java 8 i Java 11 ze stejného souboru JAR. Přidají do sestavení složitost. Postup sestavení více verzí jar je nad rámec tohoto dokumentu.

Spuštěno v Javě 11

Většina aplikací by měla běžet v Javě 11 bez úprav. První věcí, kterou se pokusíte, je spustit v Javě 11 bez opětovného dokončování kódu. Právě spuštěným bodem je zjistit, jaká upozornění a chyby pocházejí z provádění. Tento přístup získá
aplikace pro rychlejší spuštění v Javě 11 tím, že se zaměří na minimum, které je potřeba provést.

Většinu problémů, se kterými se můžete setkat, můžete vyřešit, aniž byste museli znovu komkompilovat kód. Pokud se problém musí opravit v kódu, proveďte opravu, ale pokračujte v kompilaci pomocí sady JDK 8. Pokud je to možné, před kompilací pomocí sady JDK 11 pracujte na tom, aby aplikace běžela s java verzí 11.

Kontrola možností příkazového řádku

Před spuštěním na Javě 11 proveďte rychlou kontrolu možností příkazového řádku. Možnosti, které byly odebrány, způsobí ukončení virtuálního počítače Java (JVM). Tato kontrola je obzvláště důležitá, pokud používáte možnosti protokolování GC, protože se výrazně změnily z Javy 8. Nástroj JaCoLine je dobrý nástroj pro detekci problémů s možnostmi příkazového řádku.

Kontrola knihoven třetích stran

Potenciálním zdrojem potíží jsou knihovny třetích stran, které neřídíte. Knihovny třetích stran můžete aktivně aktualizovat na novější verze. Nebo můžete vidět, co se nespouštět aplikace, a aktualizovat pouze ty knihovny, které jsou nezbytné. Problém s aktualizací všech knihoven na poslední verzi je, že je obtížnější najít původní příčinu, pokud v aplikaci dojde k nějaké chybě. Došlo k chybě kvůli nějaké aktualizované knihovně? Nebo došlo k chybě způsobené nějakou změnou modulu runtime? Problém s aktualizací pouze toho, co je potřeba, je, že může trvat několik iterací k vyřešení.

Toto doporučení je provést co nejvíce změn a aktualizovat knihovny třetích stran jako samostatné úsilí. Pokud aktualizujete knihovnu třetích stran, častěji, než ne, budete chtít nejnovější a největší verzi, která je kompatibilní s Javou 11. V závislosti na tom, jak daleko je vaše aktuální verze, možná budete chtít postupovat opatrněji a upgradovat na první verzi kompatibilní s Javou 9 nebo novější.

Kromě toho, že se podíváte na poznámky k verzi, můžete soubor JAR vyhodnotit pomocí souboru JAR. Skupina pro zvýšení kvality OpenJDK také udržuje stránku wikiwebu Quality Outreach , která uvádí stav testování mnoha projektů FREE Open Source Software (FOSS) ve verzích OpenJDK.

Explicitně nastavit uvolňování paměti

Paralelní uvolňování paměti (Parallel GC) je výchozí GC v Javě 8. Pokud aplikace používá výchozí nastavení, měl by být GC explicitně nastaven s možností -XX:+UseParallelGCpříkazového řádku . Výchozí změna v Javě 9 na uvolňování paměti První uvolňování paměti (G1GC). Aby bylo možné provést spravedlivé porovnání aplikace spuštěné v Javě 8 a Java 11, musí být nastavení GC stejné. Experimentování s nastavením GC by mělo být odloženo, dokud se aplikace neověří v Javě 11.

Explicitně nastavit výchozí možnosti

Pokud běží na virtuálním počítači HotSpot, nastavení možnosti -XX:+PrintCommandLineFlags příkazového řádku vypíše hodnoty možností nastavených virtuálním počítačem, zejména výchozí hodnoty nastavené GC. Spusťte tento příznak v Javě 8 a při spuštění na Javě 11 použijte vytištěné možnosti. Ve většině případů jsou výchozí hodnoty stejné od 8 do 11. Použití nastavení z 8 ale zajišťuje paritu.

Doporučujeme nastavit možnost --illegal-access=warn příkazového řádku. V Javě 11 bude použití reflexe pro přístup k internímu rozhraní API JDK výsledkem neplatného reflexního upozornění přístupu. Ve výchozím nastavení je upozornění vydáno pouze pro první neplatný přístup. Nastavení --illegal-access=warn způsobí upozornění na každý neplatný reflexní přístup. Další případ najdete v případě, že je neplatný přístup s možností nastavenou na upozornění. Ale dostanete také spoustu nadbytečných upozornění.
Jakmile aplikace běží na Javě 11, nastavte --illegal-access=deny napodobování budoucího chování modulu runtime Java. Počínaje Javou 16 bude --illegal-access=denyvýchozí .

Upozornění třídy ClassLoader

V Javě 8 můžete přetypovat zavaděč tříd systému na URLClassLoader. Obvykle to provádí aplikace a knihovny, které chtějí vkládat třídy do cesty třídy za běhu. Hierarchie zavaděče tříd se změnila v Javě 11. Zavaděč tříd systému (označovaný také jako zavaděč tříd aplikace) je nyní interní třídou. Přetypování do objektu URLClassLoaderClassCastException vyvolá za běhu. Java 11 nemá rozhraní API k dynamickému rozšíření cesty třídy za běhu, ale dá se provést reflexí, přičemž zřejmé upozornění týkající se použití interního rozhraní API.

V Javě 11 zavaděč spouštěcí třídy načte pouze základní moduly. Pokud vytvoříte zavaděč tříd s nadřazenou hodnotou null, nemusí najít všechny třídy platformy. V Javě 11 musíte v takových případech předat ClassLoader.getPlatformClassLoader() místo null jako zavaděč nadřazené třídy.

Změny dat národního prostředí

Výchozí zdroj pro data národního prostředí v Javě 11 byl změněn pomocí JEP 252 na společné úložiště dat národního prostředí unicode. To může mít vliv na lokalizované formátování. V případě potřeby nastavte vlastnost systému java.locale.providers=COMPAT,SPI tak, aby se vrátila k chování národního prostředí Java 8.

Potenciální problémy

Tady jsou některé běžné problémy, se kterým se můžete setkat. Další podrobnosti o těchto problémech najdete na odkazech.

Nerozpoznané možnosti

Pokud byla odebrána možnost příkazového řádku, aplikace se vytiskne Unrecognized option: nebo Unrecognized VM option následuje název možnosti off-endu. Nerozpoznaná možnost způsobí ukončení virtuálního počítače. Možnosti, které byly zastaralé, ale neodebrané, vytvoří upozornění virtuálního počítače.

Obecně platí, že možnosti, které byly odebrány, nemají žádnou náhradu a jediným řešením je odebrat možnost z příkazového řádku. Výjimkou jsou možnosti protokolování uvolňování paměti. Protokolování GC bylo znovu implementováno v Javě 9, aby bylo možné použít sjednocenou architekturu protokolování JVM. V části Povolit protokolování pomocí architektury jednotného protokolování JVM v javě SE 11 nástrojů najdete v části Mapování starších příznaků protokolování uvolňování paměti na konfiguraci Xlogu.

Upozornění virtuálních počítačů

Použití zastaralých možností způsobí upozornění. Možnost je zastaralá, když byla nahrazena nebo už není užitečná. Stejně jako u odebraných možností by se tyto možnosti měly odebrat z příkazového řádku. Upozornění VM Warning: Option <option> was deprecated znamená, že tato možnost je stále podporovaná, ale tato podpora se může v budoucnu odebrat. Možnost, která již není podporována a vygeneruje upozornění VM Warning: Ignoring option. Možnosti, které už nejsou podporované, nemají vliv na modul runtime.

Průzkumník možností webové stránky poskytuje úplný seznam možností, které byly přidány nebo odebrány z Javy od verze JDK 7.

Chyba: Nepodařilo se vytvořit virtuální počítač Java

Tato chybová zpráva se vytiskne, když JVM narazí na nerozpoznanou možnost.

UPOZORNĚNÍ: Došlo k neplatné reflexní operaci přístupu

Pokud kód Java používá reflexi pro přístup k internímu rozhraní API JDK, modul runtime vydá neplatné reflexní upozornění přístupu.

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by my.sample.Main (file:/C:/sample/) to method sun.nio.ch.Util.getTemporaryDirectBuffer(int)
WARNING: Please consider reporting this to the maintainers of com.company.Main
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

To znamená, že modul neexportoval balíček, ke kterému se přistupuje prostřednictvím reflexe. Balíček je zapouzdřen v modulu a v podstatě je interní rozhraní API. Upozornění je možné ignorovat jako první úsilí o zahájení a spuštění v Javě 11. Modul runtime Java 11 umožňuje reflexní přístup, aby starší kód mohl i nadále fungovat.

Pokud chcete toto upozornění vyřešit, vyhledejte aktualizovaný kód, který nepoužívá interní rozhraní API. Pokud problém nejde vyřešit aktualizovaným kódem, --add-exports můžete použít možnost --add-opens příkazového řádku k otevření přístupu k balíčku. Tyto možnosti umožňují přístup k nevyexportovaným typům jednoho modulu z jiného modulu.

Tato --add-exports možnost umožňuje cílovému modulu přístup k veřejným typům pojmenovaného balíčku zdrojového modulu. Někdy se kód použije setAccessible(true) pro přístup k neveřejovým členům a rozhraní API. To se označuje jako hluboké odrazy. V tomto případě použijte --add-opens k tomu, abyste svému kódu poskytli přístup k neveřejým členům balíčku. Pokud si nejste jistí, jestli se má použít --add-export nebo --add-opens, začněte příkazem --add-export.

Tyto --add-exports možnosti --add-opens by se měly považovat za pracovní, nikoli dlouhodobé řešení. Pomocí těchto možností se zapouzdří zapouzdření systému modulů, což je určené k tomu, aby se interní rozhraní API sady JDK přestalo používat. Pokud se interní rozhraní API odebere nebo změní, aplikace selže. Reflexní přístup bude odepřen v Javě 16, s výjimkou případů, kdy je přístup povolený možnostmi příkazového řádku, například --add-opens. Pokud chcete napodobovat budoucí chování, nastavte --illegal-access=deny na příkazovém řádku.

Upozornění ve výše uvedeném příkladu je vydáno, protože sun.nio.ch balíček není exportován modulem java.base . Jinými slovy, v module-info.java souboru modulu java.basenení žádné exports sun.nio.ch; . To lze vyřešit pomocí --add-exports=java.base/sun.nio.ch=ALL-UNNAMED. Třídy, které nejsou definovány v modulu implicitně patří do nenázvového modulu, doslova pojmenované ALL-UNNAMED.

java.lang.reflect.InaccessibleObjectException

Tato výjimka označuje, že se pokoušíte volat setAccessible(true) pole nebo metodu zapouzdřené třídy. Můžete také získat upozornění na nelegální reflexní přístup. --add-opens Použijte možnost udělit kódu přístup k neveřejým členům balíčku. Zpráva o výjimce vám oznámí, že modul "neotevře" balíček do modulu, který se pokouší volat set Nepřístupný. Pokud je modul "bez názvu modulu", použijte UNNAMED-MODULE ho jako cílový modul v možnosti --add-opens .

java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.util.ArrayList jdk.internal.loader.URLClassPath.loaders accessible: 
module java.base does not "opens jdk.internal.loader" to unnamed module @6442b0a6

$ java --add-opens=java.base/jdk.internal.loader=UNNAMED-MODULE example.Main

java.lang.NoClassDefFoundError

NoClassDefFoundError je nejpravděpodobnější příčinou rozděleného balíčku nebo odkazováním na odebrané moduly.

NoClassDefFoundError způsobené rozdělenými balíčky

Rozdělený balíček je, když se balíček najde ve více knihovnách. Příznakem problému s rozděleným balíčkem je, že třída, o které víte, že je na cestě ke třídě, nebyla nalezena.

K tomuto problému dojde pouze při použití cesty modulu. Systém modulu Java optimalizuje vyhledávání tříd omezením balíčku na jeden pojmenovaný modul. Modul runtime dává přednost cestě modulu nad cestou třídy při vyhledávání třídy. Pokud je balíček rozdělený mezi modul a cestu ke třídě, použije se pouze modul k vyhledání třídy. To může vést k chybám NoClassDefFound .

Snadným způsobem, jak zkontrolovat rozdělení balíčku, je připojit cestu k modulu a cestu ke třídě do souboru jdeps a použít cestu k souborům tříd aplikace jako <cestu>. Pokud existuje rozdělený balíček, soubor jdeps vytiskne upozornění: Warning: split package: <package-name> <module-path> <split-path>.

Tento problém lze vyřešit přidáním --patch-module <module-name>=<path>[,<path>] rozděleného balíčku do pojmenovaného modulu.

NoClassDefFoundError způsobené použitím modulů Java EE nebo CORBA

Pokud aplikace běží na Javě 8, ale vyvolá java.lang.NoClassDefFoundError nebo a java.lang.ClassNotFoundException, je pravděpodobné, že aplikace používá balíček z modulů Java EE nebo CORBA. Tyto moduly byly v Javě 9 zastaralé a odebrány v Javě 11.

Pokud chcete tento problém vyřešit, přidejte do projektu závislost modulu runtime.

Odebraný modul Ovlivněný balíček Navrhovaná závislost
Java API pro webové služby XML (JAX-WS) java.xml.ws JAX WS RI Runtime
Architektura Java pro vazbu XML (JAXB) java.xml.bind JAXB Runtime
JavaBeans Activation Framework (JAV) java.activation Architektura aktivace JavaBeans (TM)
Běžné poznámky java.xml.ws.annotation Rozhraní API pro poznámky Javax
Common Object Request Broker Architecture (CORBA) java.corba GlassFish CORBA ORB
Rozhraní JAVA Transaction API (JTA) java.transaction Rozhraní API pro transakce v Javě

-Xbootclasspath/p už není podporovaná možnost

Byla odebrána podpora pro -Xbootclasspath/p . Místo toho použijte --patch-module. Možnost --patch-module je popsána v JEP 261. Vyhledejte část s popiskem "Obsah modulu opravy". --patch-module lze použít s javac a javou k přepsání nebo rozšíření tříd v modulu.

Co --patch-module v podstatě vloží modul patch do vyhledávání tříd systému modulu. Systém modulů nejprve vezme třídu z modulu patch. To je stejný účinek jako před vyřízením cesty bootclass v Javě 8.

Nepodporovaná chybaClassVersionError

Tato výjimka znamená, že se pokoušíte spustit kód, který byl zkompilován s novější verzí Javy ve starší verzi Javy. Například používáte Javu 11 s jarem, který byl zkompilován pomocí sady JDK 13.

Verze Javy Verze formátu souboru třídy
8 52
9 53
10 54
11 55
12 56
13 57

Další kroky

Po spuštění aplikace v Javě 11 zvažte přesunutí knihoven mimo cestu ke třídě a do cesty modulu. Vyhledejte aktualizované verze knihoven, na které vaše aplikace závisí. Pokud je k dispozici, zvolte modulární knihovny. Použijte co nejvíce cestu k modulu, i když neplánujete používat moduly ve vaší aplikaci. Použití cesty modulu má lepší výkon pro načítání tříd, než to dělá cesta třídy.