Maszyna wirtualna HotSpot

Kompilator HotSpot posługuje się metodą tzw. optymistycznej kompilacji: nie wykonuje tej operacji dla części kodu, o których wiadomo że pojawiają się rzadko (obsługa sytuacji wyjątkowych, część kodu wykonywana tylko raz przy nie spełnionym warunku do wyjścia z pętli, inicjalizacja zmiennych i obiektów, in.).

Kandydatem do kompilacji jest pętla obliczeniowa wykonywana tysiące razy, ale pętlę wykonywaną raz należy interpretować. Jest to możliwe tylko dzięki ścisłej współpracy dynamicznego kompilatora z interpreterem, natomiast brak tej możliwości w typowych kompilatorach JIT, dokonujących operacji kompilacji statycznie tylko raz, na początku pracy programu.

Kod inline

Wstawianie kodu maszynowego w kod języka wysokiego poziomu (inlining) praktykował już ponad 10 lat temu Borland w swoich kompilatorach. Podobną metodę zastosowano w maszynie HotSpot.

Wywołanie każdej metody (procedury, według terminologii proceduralnych języków programowania) wymaga wielu instrukcji maszynowych. Jeśli jest ona używana często i jest krótka (aby czas wywołania stanowił znaczący udział czasu wykonania), warto wstawić jej kod bezpośrednio w program wykonywany.

Inlining zdecydowanie poprawia wydajność, ale zwiększa znacznie rozmiar programu (nawet dziesięciokrotnie). Nie można więc dokonywać tej operacji w całym programie.

HotSpot wykonuje ją tylko w ważnych miejscach programu, wpływających w istotny sposób na jego wydajność. Prawo Pareto (zwane również regułą 80/20) mówi, że "80% czasu program spędza w 20% kodu". Dokonując więc operacji wstawiania metod inline w te części kodu, można uzyskać zdumiewający zysk na wydajności.

Skuteczność HotSpot

Sun twierdzi, że wyniki badania wydajności działania programów uruchamianych za pomocą maszyny HotSpot wskazują, iż da się uzyskać wydajność zbliżoną lub lepszą niż wydajność porównywalnych obiektowych programów C++. Natomiast jeśli za pomocą C (lub C++) napisze się nieobiektowy program o takich samych właściwościach, to będzie on na pewno szybszy od programu w C++ i Javie.

Początek drogi

Prawdopodobnie pojawienie się na rynku maszyny wirtualnej HotSpot zachęci programistów i przedsiębiorstwa do większego zainteresowania się językiem i środowiskiem Java. Wyeliminuje bowiem największy problem obecnych programów Java - małą wydajność.

Sun przewiduje, że pełne wdrożenie technologii HotSpot nastąpi w ciągu najbliższych kilku lat i wymagać będzie jeszcze wiele pracy.

Natomiast wątpliwości budzi pomysł Suna, aby maszynę wirtualną Java sprzedawać, zamiast dołączać bezpłatnie np. do przeglądarek Web, jak dzieje się to obecnie.

Interpretery, kompilatory, maszyna wirtualna

U podstaw każdego programu komputerowego leży ograniczony zestaw instrukcji, gdyż tylko one - w formie tzw. kodu maszynowego - są wykonywane przez procesor.

Asembler. Początkowo programiści opracowali program pozwalający im powiązać każdą z tych instrukcji ze skrótem mnemonicznym. Przekształceniem każdego takiego skrótu na jedną instrukcję kodu maszynowego zajmuje się program o nazwie asembler.

Kompilator. Pojawienie się języków wysokiego poziomu, takich jak C i Pascal, pozwoliło na przekształcenie jednej instrukcji tego języka na wiele instrukcji maszynowych. Proces kompilacji to właśnie przekształcenie instrukcji języka wysokiego poziomu na kod maszynowy. Obejmuje on kilka etapów, w tym tzw. konsolidowanie, umożliwiające dołączenie standardowych bibliotek, oszczędzających wysiłek programisty, który nie musi osobiście opracowywać elementarnych składników programu.

Kompilator optymalizujący rozpoznaje pewne zależności w programie i wykorzystuje je do uzyskania szybszego kodu maszynowego. Na przykład jeśli jakieś dane będą używane w kolejnej instrukcji, nie ma sensu zapisywać ich do pamięci, a warto je zachować w rejestrze procesora. Nowoczesne kompilatory optymalizujące mogą zmienić kolejność instrukcji kodu źródłowego, usunąć pewne zmienne lub zamienić je na stałe i generalnie przeorganizować strukturę programu w celu jego optymalizacji (pod względem rozmiaru kodu lub szybkości wykonania).

Interpreter. Proces uruchamiania programu polegający na wielokrotnym wykonywaniu pętli - pisanie kodu, kompilowanie, próbne uruchamianie, znajdowanie i poprawianie błędów - jest na tyle czasochłonny, że dla prostych programów korzystniejsze jest używanie interpretera. Pobiera on kolejne instrukcje języka wysokiego poziomu (na ogół Basicu) i wykonuje je na bieżąco. Wymaga to dużego nakładu czasu maszynowego i dlatego programy interpretowane działają na ogół setki razy wolniej niż programy kompilowane.

Interpreter p-kodu to rozwiązanie, które pojawiło się ok. 20 lat temu w UCSD Pascalu (opracowanym na University of California, San Diego - stąd skrót). Polega ono na wstępnym przekształceniu kodu języka wysokiego poziomu na kod pośredni (p-code), który jest wykonywany przez prostszy, a zatem szybszy interpreter języka. Jest to pierwowzór maszyny wirtualnej Java.

Maszyna wirtualna Java to interpreter kodu bajtowego Java, uzyskanego w wyniku wstępnej kompilacji kodu źródłowego Java. Kod bajtowy Java można uważać za instrukcje nie istniejącego procesora Java (choć ostatnio pojawiły się procesory Sun PicoJava, wykonujące bezpośrednio kod bajtowy Java).

Kompilator na bieżąco (Just-in-Time - JIT) to program zamieniający kod bajtowy Java na kod maszynowy, gdy po raz pierwszy jest on wykonywany. Ponieważ JIT ma dostęp do ciągu instrukcji kodu bajtowego, może on dokonywać pewnej optymalizacji, bazując na ich zestawach, co jest istotne zwłaszcza w przypadku krótkich pętli obliczeniowych, wykonywanych wielokrotnie. JIT nie potrafi natomiast optymalizować części kodu intensywnie wykorzystujących wejście/wyjście lub dokonujących eliminacji z pamięci obiektów już nie używanych (tzw. garbage collection).

JIT przekształca do kodu maszynowego tylko te części kodu bajtowego, które są wykonywane, ale dokonuje tej operacji niezależnie od tego, ile razy dana część kodu jest używana w programie - raz czy tysiące. Aby dokonać lepszej optymalizacji, musiałby mieć informacje o wykonywanym kodzie, dostępne tylko podczas jego wykonania. Optymalizacja ani kompilacja JIT nie są potrzebne w przypadku części kodu inicjalizujących zmienne w programie, ponieważ są one wykonywane tylko raz (ale jest wiele takich miejsc w kodzie).

Operacje te przyspieszają wykonanie kodu bajtowego Java, ale największe oszczędności daje kompilacja dynamiczna.


TOP 200