690 likes | 999 Views
Java Virtual Machine. Things you w anted to know but were afraid to ask about 16 .02.2005 Konstantin Tretjakov Webmedia AS. Mis on “Java”?. The Java Language Java API ( + r untime environment , rt.jar ) The Java Virtual Machine Java Tools ( java, javac, jdb, javap , … ). Kava. JVM
E N D
Java Virtual Machine Things you wanted to know but were afraid to ask about 16.02.2005 Konstantin Tretjakov Webmedia AS
Mis on “Java”? • The Java Language • Java API (+runtime environment, rt.jar) • The Java Virtual Machine • Java Tools (java, javac, jdb, javap, …)
Kava • JVM • Classloading • Security • Memory, Garbage Collection
JVM • Abstraktne arvutusmasin • Tagab • Sõltumatust platvormist • Turvalisust • Network-mobility (Applets, RMI, Jini) • Pole ainus VM, peaaegu iga keele jaoks on tehtud oma virtuaalmasin (P-machine, MaMa, Lisp-machine, ...) • JVM oli algusest peale loodud praktiliseks kasutamiseks ja on teistest paremini läbi mõeldud paljudes aspektides (mäluhaldus, lõimed, turvalisus, ...).
JVM kui interpretaator • Lihtsustades, võib öelda et JVM on in-terpretaator, mis täidab vastava bait-koodi: while (still alive) { fetch next instruction; execute instruction;} • Täitmine võib käia mitmes lõimes • Tüüpiline JVM implementatsioon ei ole “puhtalt” interpretaator. (JIT-compilation)
Näide 1: Sun Java HotSpot VM • Koodi üldiselt interpreteeritakse, kuid nendes kohtades, kus tehakse palju tööd (hot spots) koodi kompileeritakse. Kompilaator oskab kasutada run-time statistikat selleks et valida häid optimisatsioone. • On olemas kaks HotSpot VM variatsiooni: • “Client” (java –client), vaikimisi kasutatav • “Server” (java –server) • Soovi korral võib paluda koodi ainult interpreteerida võtmega –Xint.
Client Kiirem startup Kiirem kompileerimine Väiksem footprint Soovitatud GUI rakendustele Server Aeglasem startup Rohkem kompileerimist Suurem footprint Maksimaalne kiirus suurem Soovitatud serverrakendustele HotSpot “Client” vs “Server” Java “Client” ja Java “Server” on sisuliselt kaks erinevat implementatsiooni, mis kasutavad erinevaid JIT kompilaatoreid, erinevaid mäluhaldamis-strateegiaid ning optimeerivad erinevaid asju.
Näide 2: BEA JRockit • Veel üks JVM implementatsioon • Nagu Sun’i JVM, kompileerib ainult vajalikke kohtiadaptiivselt tagaplaanil. • Huvitav garbage-collectorite valik • Optimeeritud Intel x86 jaoks • Minu kogemuse järgi jooksutab asju kuni 2 korda kiiremini kui Sun-i JVM, kuid mõnikord teeb kummalisi core-dump-e. • Tasuta
JVM arhitektuur Heap Memory (objects) Method area (class data) Java stacks PC registers Security subsystem Classloader subsystem Execution engine Native interface
Stacks & PC registers • Iga lõime jaoks on oma PC-register (program counter), mis viitab selles lõimes järgmisena täitmiseks kuuluva instruktsioonile (v.a. juhul kui lõim täidab native-code-i). • Igal lõimel on oma stack, mis koosneb freimidest. Iga meetodi väljakutse loob uue freimi, meetodist väljumine eemaldab viimase freimi stack-ist. • Threadi stacki suurust saab määrata optsiooniga –Xss. Sun JVM default on vist 128k. (-Xss128k)* • Java ei toeta tail-recursion-it, seega stack on alati “täielik”
Stacks & PC registers Thread 1 Thread 2 PC = 0x2322 PC = 0x4221 Stack Stack Stack Frame Stack Frame Stack Frame Stack Frame Stack Frame Stack Frame
JVM arhitektuur Heap Memory (objects) Method area (class data) Java stacks PC registers Security subsystem Classloader subsystem Execution engine Native interface
Kuidas JVM laadib klasse? • Klasside laadimine toimub ClassLoader-ite abil. • Classloader-ile antakse ette klassi täisnimi, tema ülesandeks on leida vastava klassi implementatsiooni ning laadida seda mällu • JVM kasutab vaikimisi “system class loader”-it, kuid vajaduse korral võib luua ka oma implementatsiooni.
Class Loaders • Järgmised lõigud on (peaaegu) samaväärsed: somepackage.SomeClass obj = new somepackage.SomeClass(); somepackage.SomeClass obj = (somepackage.SomeClass)Main.class.getClassLoader() .loadClass(“somepackage.SomeClass”).newInstance();
ClassLoaders • Kohe pärast käivitamist kasutab JVM enda sisseehitatud “bootstrap class loader”-i selleks et laadida süsteemseid klasse (rt.jar). • Pärast luuakse “system class loader”, mis oskab kasutada CLASSPATH-is antud infot klasside otsimiseks. Ta laadib parameetrina antud peaklassi kõvakettalt ning käivitab teda. • Iga klass on seotud teda laadinud class loaderiga. Klassi sees kasutatakse seda teiste klasside laadimiseks.
Bootstrap class loader • Bootstrap class loaderit saab (Sun JVM) puhul konfigureerida parameetritega • –Xbootclasspath:<nimekiri> • -Xbootclasspath/a:<nimekiri> • -Xbootclasspath/p:<nimekiri> • Seda oleks vaja ainult siis kui soovite ümberdefineerida java.lang.Long vms standardse klassi.
Custom Class Loader • Custom class loaderit võib vaja minna: • Kui vaikimisi kasutatav kettalt klasside laadimine ei sobi (võrgust või mujalt klasside laadimine, klasside genereerimine on-the-fly, krüptitud klasside laadimine, jne) • Nimekonfliktide lahendamiseks (pluginid, mitu rakendust deploy-tud ühes serveris, jne) • Turvalisuse mõttes
Custom Class Loader Class CustomClassLoader extends ClassLoader { public Class findClass(String name) { byte[] b = {leia ja laadi klassi andmed} return defineClass(name, b, 0, b.length); } } ... ClassLoader loader = new CustomClassLoader(); Object o = loader.loadClass(“MyClass”).newInstance(); ...
Class Namespace • Iga klassiga on seotud tema class loader. • SomeClass.class.getClassLoader() • Iga klass on üheselt määratud tema täisnime ja tema class loaderi poolt. • Ühes JVM-is saab kasutada mitu erinevat sama nimega klassi juhul kui nad on laaditud erinevate class loader-ite abil. • Class loader teab kust klassi laaditi ja kuidas, seda kasutatakse turvalisuse tagamiseks.
Class Loader Hierarchy • Kui class loader ei oska leida klassi, mida palutakse, küsib ta teda laadinud class loaderi poolt. Niimoodi tekib class loader-ite hierarhia, kus ülemine on system class loader. • “Vaikimisi” realiseeritud class loaderi klassi laadimise algoritm (meetod loadClass) on tegelikult selline: 1. kui klass oli juba laaditud, tagasta seda 2. küsi kas parent class loader oskab seda laadida 3. proovi laadida klassi ise • Selline järjekord ei anna “juhuslikult” üledefineerida süsteemseid klasse, ning soodustab “ühiste klasside” kasutamist, samas tal on teatud miinused.
Class Loader Hierarchy Bootstrap Class Loader JVM System Class Loader Custom Class Loader Rakendus Vaikimisi realisatsioon lubab klassidele “ära joosta” custom class loaderist, see võib olla ebamugav.
Näide: Plugins System Class Loader Application Class Loader Plugin1 Class Loader Plugin2 Class Loader Erinevad pluginid võivad sõltumatult kasutada sama nimega klasse
Näide: Firewall Class Loader Firewall class loader ei tegele klasside laadimisega vaid päringute filtreerimisega: ta laseb läbi päringud ainult teatud klasside jaoks (näiteks ainult java.*). See isoleerib rakendust soovimatutest klassidest mis olid CLASSPATH-is määratud. System Class Loader Firewall Class Loader Application Class Loader
Näide: J2EE rakendusserver System Class Loader Server Class Loader Application1 Class Loader Application2 Class Loader Module1 Class Loader Module2 Class Loader Ühe rakenduse erinevatel moodulitel võivad olla erinevad class loaderid. See lubab moduleid redeploy-da ühe kaupa “on-line”, kuid teeb moodulite vahelist suhtlust keerulisemaks.
Näide: Weblogic App (default) Application Class Loader (laadib kõik EJB moodulid) WebApp1 Class Loader WebApp2 Class Loader • Selline konfiguratsioon on mugav sest: • Enamasti veebirakendused peavad saama ligi EJB-dele kuid mitte vastupidi • Veebis on hot-redeploy olulisem (JSP-d). • Soovi korral saab konfigureerida class loaderid ka teistmoodi. (weblogic-application.xml)
JVM arhitektuur Heap Memory (objects) Method area (class data) Java stacks PC registers Security subsystem Classloader subsystem Execution engine Native interface
Security Subsystem • Java oli algsest peale planeeritud mobiilsete rakenduste loomiseks (näiteks appletid), ning tal on olemas spetsiaalsed vahendid koodi privileegide piiramiseks. • Kas koodil on lubatud midagi teha või mitte otsustab klass SecurityManager. • Enne potentsiaalselt ohtliku operatsiooni sooritamist (näiteks faili lugemist) kutsub JVM välja SecurityManageri meetodit checkPermissionsobiva parameetriga. Kui tegevus pole lubatud, viskab see meetod SecurityException.
Security Subsystem • Alguses on JVM Security Manager määramata, rakendus saab teha midaiganes. Security manageri saab määrata kutsudes välja. System.setSecurityManager(myManager) • Muidugi võib realiseerida oma security manager, kuid Javaga kaasa tuleb juba päris mõistlik realisatsioon. Seda saab JVM käivitamisel sisse lülitada järgmiselt: java –Djava.security.manager
Java Security Manager • Java Security Manager loeb policy faili määratud java.security.policy property abil, s.t. java –Djava.security.manager –Djava.security.policy=my.policy • Policy failis on kirjeldatud mis kood saab teha mida.
Policy fail grant signedBy “trustedparty" { permission java.io.FilePermission “/conf/files/*", "read"; }; grant codeBase “file:/my/files/”{ permission java.security.AllPermissions; };
Stack Inspection Stack Selleks et kontrollida kas antud turvakriitiline operatsioon on lubatud, Java Security Manager uurib kõiki stack-is olevaid frame-sid, ning veendub et igale stackis oleva meetodile on lubatud seda operatsiooni sooritama. (seda kontrolli sooritab tegelikult klass nimega AccessController) Kuna stackis on olemas EvilClass, kellele faili lugemine pole lubatud, viskab AccessController exception-it
Privileged Execution • Oletame et Applet tahab luua tekstiakna, selleks kasutab ta JTextBox klassi, mis oskab ekraanile teksti joonistada. Teksti joonistamiseks on vaja fonte, mis asuvad kuskil failis. • Kuna JTextBox on käivitatud võõra Appleti poolt, pole tal nagu õigust faile lugeda. • Selles situatsioonis saab JTextBox öelda et ta vajab oma privileege sõltumata sellest kes teda välja kutsus: AccessController.doPrivileged(new PrivilegedAction() { public Object run() { // Tee mida vaja on kasutades enda privileege } } • SecurityManager ei uuri stacki “doPrivileged” väljakutsest ülevale • Idee poolest sarnaneb see lähenemine “suid bitiga” Unixis.
JVM arhitektuur Heap Memory (objects) Method area (class data) Java stacks PC registers Security subsystem Classloader subsystem Execution engine Native interface
Mälu (Heap) • Mälus hoitakse: • Loodud objekte • Vajalikke abistruktuure (klasside metaandmed, jms) • Konstante (constant pool) • Mälu suurust saab määrata optsioonidega • -Xms näitab esialgselt reserveeritud mälu suurust • -Xmx näitab mälu ülempiiri. • Defaults (Intel, umbes): -Xms3m –Xmx64m • Serverrakendustele on soovitatud panna ms = mx
Constant Pool • Final-muutujad, String literaalid, klasside ja meetodite nimed (Stringidena) hoitakse nn. constant pool-is. See teatud mõttes sarnaneb sümbolite tabeliga teistes keeltes. • Selle pärast • Parem on kasutada Boolean.valueOf(...) kuid mitte new Boolean(..) • Paljukasutatavad Stringid on parem defineerida literaalidena (nad lähevad kohe pool-i) või kasutada String.intern(). • Intern-itud stringid saab võrrelda ==abil. • “A” + “b” on kiirem kui new StringBuffer(“A”).append(“b”).
Automatic Memory Management Javas ei pea mõtlema sellest kes ja millal objekte vabastab: C++ Obj* o = new Obj(); doWork(o); delete o; Java doWork(new Obj()); Mõned arvavad et automaatne mäluhaldus on peamine faktor miks Java (VB/C#/PHP/Perl/Haskell/Javascript/...) on mugavam keel milles arendus läheb kiiremini.
Automatic Memory Management • On erinevaid viise kuidas korraldada automaatset mäluhaldust: • Reference counting • Garbage collection • Infinite memory • JVM ei spetsifitseeri kuidas konkreetselt peab mäluhaldus realiseeritud olema, kuid enamasti kasutatakse prügikoristamis tehnikat.
Garbage Collection • Objekte luuakse mälus ning ei vabastata kuni mälust puudu pole. Kui mälu “täis saab” teostatakse “prügikoristamist”: läbitakse terve mälu ning vabastatakse need objektid, millele keegi enam ei viida. • Lokaalsed muutujad ning kõik objektid, millele saab pääseda ligi nende kaudu on “elusad”, kõik ülejäänud on “surnud”.
Näide: Mark & Sweep GC Stack Heap lokaalsed muutujad freimides
Näide: Mark & Sweep GC Stack Heap lokaalsed muutujad freimides
Näide: Mark & Sweep GC Stack Heap lokaalsed muutujad freimides
Generational GC • Terve mälu koristamine võtab palju aega (proportsionaalselt koristatavate objektide arvuga) • Saab tähele panna et enamus objekte “sureb noortena”. • Sun JVM jagab mälu kaheks osaks: osa noorte objektide jaoks (young generation) ja osa vanade jaoks (tenured generation) • Enamus prügikoristusi toimub young generation-is, need on “väiksed”. Väikest koristust üleelanud objekt liigub “vanade” objektide juurde. • Kui vanade seas mälu otsas, tehakse “täis” koristus
Sun Java 1.4.2 GC http://java.sun.com/docs/hotspot/gc1.4.2/
Sun JVM GC Configuration • Verbose output: -verbose:gc [GC 325407K->83000K(776768K), 0.2300771 secs] [GC 325816K->83372K(776768K), 0.2454258 secs] [Full GC 267628K->83769K(776768K), 1.8479984 secs] • Olulisem parameeter on young generation-i suurus: • -XX:NewRatio=3tähendab et young:tenured generationite suuruste vahekord on 1:3 • Client JVM: NewRatio=8 • Server JVM: NewRatio=2
Different Garbage Collectors • Throughput collector (-XX:+UseParallelGC) • Concurrent collector (-XX:+UseConcMarkSweepGC) • Incremental collector (-Xincgc)
Reference Objects SoftReference WeakReference PhantomReference http://java.sun.com/developer/technicalArticles/ALT/RefObj/
JVM arhitektuur Heap Memory (objects) Method area (class data) Java stacks PC registers Security subsystem Classloader subsystem Execution engine Native interface
JVM arhitektuur Heap Memory (objects) Method area (class data) Java stacks PC registers Security subsystem Classloader subsystem Execution engine Native interface
Bonus Some random stuff
Bonus Kava • Lõimed, sünkroniseerimine • Huvitavaid Java konstruktsioone • Strings, encodings