Egy kritikus Java 8-as alkalmazást lépcsőzetesen frissítsen: leltározzon fel minden függőséget, először lépjen Java 11-re, majd sorban minden későbbi hosszú távú támogatású (LTS) verzióra, és a tesztek és terheléses tesztek döntsék el, mikor biztonságos egy-egy lépés. A munka nagy része a könyvtárakban, a build-eszközökben és a JDK belső részeihez nyúló kódban van, nem az üzleti logikában.
Ha a Java 8-on marad, évről évre szűkülnek a lehetőségei
Egy Java 8-as alkalmazás évekig futhat tovább, és éppen ez a csapda. A biztonsági javítások egyre inkább fizetős támogatási szerződéstől vagy olyan disztribúciótól függenek, amely még visszaportolja őket, a nagy könyvtárak pedig továbbléptek. A Spring Boot 3 például legalább Java 17-et igényel, így a Java 8-on maradás a keretrendszer-verziókat is befagyasztja.
Az újabb JVM-ek ugyanazt a kódot jobban is futtatják. A G1 a Java 9 óta az alapértelmezett szemétgyűjtő, az alacsony szünetidejű gyűjtők, például a ZGC, éles használatra készek, a tömörített karakterláncok (compact strings) pedig csökkentik a memóriahasználatot a szövegigényes terheléseknél. A fejlesztők a modern nyelvi funkciókat is elvárják, például a recordokat, a szövegblokkokat és a switch-kifejezéseket, ezért egy Java 8-as kódbázishoz nehezebb embert találni.
Kezdje az alkalmazás összes függőségének leltárával
Maga a JDK-frissítés ritkán a nehéz rész. A nehéz rész a Java 8-ra írt és soha nem frissített könyvtárak, pluginek és ágensek hosszú sora. Mielőtt bármit módosítana, készítsen teljes listát, és jegyezze fel minden elem verzióját.
- A JDK szállítója és verziója minden környezetben, a fejlesztői laptopoktól az éles környezetig.
- Közvetlen és tranzitív függőségek, a Maven függőségfájából vagy a Gradle függőségi riportjából exportálva.
- Build-pluginek, kódgenerátorok és annotációfeldolgozók, például a Lombok.
- Az alkalmazásszerver vagy servletkonténer, ha az alkalmazás ilyenben fut.
- Monitorozásra, profilozásra vagy biztonságra szolgáló Java-ágensek, amelyek mélyen beépülnek a JVM-be.
- A belső JDK API-kat használó kód, amelyet a jdeps eszközzel és annak jdk-internals opciójával talál meg.
- JVM-kapcsolók, a szemétgyűjtő beállításai és minden szkript, amely a java -version kimenetét dolgozza fel.
Haladjon végig a hosszú távú támogatású verziókon, ne ugorjon
Lépjen Java 8-ról 11-re, majd 17-re, aztán 21-re vagy 25-re. Minden lépésnek megvannak a saját eltávolításai és viselkedésbeli változásai, egyetlen ugrás pedig mindezeket egyetlen, diagnosztizálhatatlan hibába keveri. Ha rétegenként javít, minden lépés elég kicsi marad ahhoz, hogy át lehessen nézni és vissza lehessen vonni.
Ahol lehet, a könyvtárakat még a Java 8-on frissítse, mert sok újabb verziójuk a régi és az új JDK-kat is támogatja. Ezután a meglévő buildet futtassa az új JVM-en, mielőtt a fordítási célt módosítaná. Így elválnak a futásidejű problémák a fordítási problémáktól, a release fordítókapcsoló pedig explicit módon rögzíti a bájtkód célverzióját.
Minden lépés kapuja a tesztkészlet legyen
A tesztek az egyetlen objektív jelzés arra, hogy a frissített alkalmazás ugyanúgy viselkedik. Ha a kritikus útvonalak lefedettsége alacsony, először írjon karakterizációs teszteket: olyan teszteket, amelyek rögzítik, mit csinál ma a rendszer, a furcsa határeseteivel együtt, anélkül hogy megítélnék, helyes-e ez a viselkedés.
Adjon hozzá valódi adatbázissal és valódi üzenetközvetítőkkel (message broker) futó integrációs teszteket, mert sok frissítési hiba csak ezeken a határokon jelentkezik. Számításigényes rendszereknél futtassa át ugyanazokat a bemeneteket a régi és az új verzión, és mezőről mezőre vesse össze a kimeneteket. Figyeljen a dátumokra, a számokra és a szövegekre: a Java 9 alapértelmezés szerint CLDR területi adatokra váltott, a Java 18 pedig az UTF-8-at tette alapértelmezett karakterkészletté, és mindkettő csendben megváltoztathatja a formázást vagy az értelmezést.
Ismerje a buktatókat, amelyek elrontják a Java 8-as kódot
A legtöbb hiba néhány ismert kategóriába esik. A frissítés előtt keressen rá mindegyikre, ahelyett hogy élesben fedezné fel őket.
- Eltávolított Java EE- és CORBA-modulok: a Java 11 kivette a JDK-ból a JAXB-t, a JAX-WS-t, a JavaBeans Activationt és a közös annotációkat (common annotations), ezért ezeket explicit függőségként kell visszaadni.
- A JDK belső részeinek erős egységbezárása: a tiltott reflektív hozzáférés a Java 9-től figyelmeztetést adott, a Java 16 óta alapértelmezetten tiltott, és a Java 17 óta globálisan nem kapcsolható vissza. A megoldás a könyvtár frissítése; az add-opens kapcsolót csak dokumentált, ideiglenes kivételként használja.
- Elavult bájtkód-eszközök: a Lombok, a Mockito, a Byte Buddy, az ASM és a build-pluginek régebbi verziói hibára futnak az újabb class-fájlverziókon.
- Eltávolított funkciók: a Nashorn JavaScript-motort a Java 15 eltávolította, a Security Managert a Java 17 elavultnak nyilvánította, a Java 24 pedig véglegesen letiltotta.
- Eltávolított JVM-kapcsolók: a CMS szemétgyűjtőt a Java 14 eltávolította, és egy eltávolított opciókkal indított JVM meg is tagadhatja az indulást.
A teljesítményt valósághű terhelés alatt bizonyítsa, ne laptopon
Egy új JDK megváltoztatja a szemétgyűjtőt, a JIT-fordítót és a memóriaelrendezést, így a teljesítmény bármelyik irányba elmozdulhat. Minden lépés előtt és után futtasson az éles forgalmi profilt reprodukáló terheléses tesztet, ugyanazon a hardveren vagy példánytípuson. Vesse össze a késleltetési percentiliseket, az áteresztőképességet, a memóriát és a szemétgyűjtési szüneteket, és mérés előtt hagyja bemelegedni a JIT-et.
Mérnökeink a Sopra Steria révén vezették egy kritikus, földgáz napon belüli kereskedésére (day trading) szolgáló alkalmazás Java 8-ról 16-ra történő migrációját egy nemzeti gázkereskedési részleg számára. Abban a rendszerben a kapacitás- és energiaáramlás-számításoknak nagyfrekvenciás terhelés alatt is helyesnek és gyorsnak kellett maradniuk, ezért a frissítés ezeknek a számításoknak az optimalizálásával járt együtt, és terhelés alatt validálták, nem pedig feltételezték.
Fokozatosan vezesse be, és tartson meg egy visszautat
A frissített futtatókörnyezetet először egy példányra vagy a forgalom kis részére telepítse, és a Java 8-as alapvonalhoz képest figyelje a hibaarányt, a késleltetést és a szemétgyűjtés viselkedését. Az előző buildet tartsa telepíthető állapotban, amíg az új verzió végig nem futott a szokásos üzleti ciklusokon, a hónapzárást vagy a csúcsidőszakokat is beleértve.
Csak ezután távolítsa el a Java 8-as buildtermékeket, és kezdje használni az új nyelvi funkciókat. Ha segítséget szeretne egy ilyen frissítés megtervezéséhez vagy végrehajtásához, az SDK Enterprises saját mérnökökkel és ellenőrzött szakemberekkel, egyetlen felelős szerződés keretében biztosítja a csapatot.
A legfontosabbak
- Egy Java 8-as frissítés munkája főként a függőségekben, a build-eszközökben és a JDK belső részeinek használatában van, nem az üzleti logikában.
- Egyenként haladjon végig a hosszú távú támogatású verziókon, hogy minden hibának egyetlen, megtalálható oka legyen.
- Minden lépés kapuját a karakterizációs és integrációs tesztek jelentik, különösen a dátumok, a számok és a szövegkódolás körül.
- A teljesítményt élesszerű terhelés alatt validálja, mert a szemétgyűjtés és a JIT változásai bármelyik irányba elmozdíthatják az eredményeket.
- Először a forgalom kis részére vezesse be, és tartsa telepíthető állapotban a Java 8-as buildet, amíg az új futtatókörnyezet nem bizonyított.
GYIK
Frissíthetek közvetlenül Java 8-ról Java 21-re?
Megteheti, de így a Java 9 és 21 közötti összes változást egyszerre örökli, ami megnehezíti a hibák visszakövetését. A 11-es és a 17-es verzión való áthaladás egy kicsivel több build-időbe kerül, de egy kritikus rendszernél sok hibakeresést takarít meg.
Mennyi ideig tart egy Java 8-as frissítés?
Ez a függőségek számától, attól, hogy közülük hány használja a JDK belső részeit, a tesztlefedettségtől és attól függ, mennyi terheléses tesztelést igényel a rendszer. Egy leltár és egy próbafuttatás az új JVM-en korán reális becslést ad, mielőtt egy ütemterv mellett elköteleződne.
Újra kell írnom a kódot, hogy használhassam az új Java-funkciókat?
Nem. A Java erős visszafelé kompatibilitást tart fenn azoknál a kódoknál, amelyek szabványos, nem elavult API-kat használnak. A recordokat, a szövegblokkokat és a többi funkciót fokozatosan vezesse be, miután a frissített futtatókörnyezet stabilan fut élesben.