POLITECHNIKA ŚLĄSKA WYDZIAŁ INŻYNIERII MATERIAŁOWEJ I METALURGII. Edukacja Techniczno Informatyczna Studia dzienne. Praca dyplomowa inżynierska



Podobne dokumenty
Java jako język programowania

Programowanie obiektowe

Język JAVA podstawy. wykład 1, część 2. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

WPROWADZENIE DO JĘZYKA JAVA

Czym jest Java? Rozumiana jako środowisko do uruchamiania programów Platforma software owa

Programowanie Strukturalne i Obiektowe Słownik podstawowych pojęć 1 z 5 Opracował Jan T. Biernat

Ćwiczenie 1. Przygotowanie środowiska JAVA

Translacja wprowadzenie

Programowanie obiektowe. Literatura: Autor: dr inŝ. Zofia Kruczkiewicz

Język JAVA podstawy programowania

Programowanie w Javie cz. 1 Wstęp. Łódź, 24 luty 2014 r.

Metody Kompilacji Wykład 1 Wstęp

Programowanie obiektowe zastosowanie języka Java SE

Początki Javy. dr Anna Łazińska, WMiI UŁ Podstawy języka Java 1 / 8

Java EE produkcja oprogramowania

Laboratorium 1 Temat: Przygotowanie środowiska programistycznego. Poznanie edytora. Kompilacja i uruchomienie prostych programów przykładowych.

Wykład 1: Wprowadzenie do technologii Java

Podstawy i języki programowania

Tworzenie oprogramowania

WPROWADZENIE DO JĘZYKA JAVA

Język JAVA podstawy. wykład 2, część 1. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Programowanie komputerów

JAVA?? to proste!! Autor: wojtekb111111

Programowanie obiektowe. Wprowadzenie

Wstęp do programowania w języku Java

Wykład V. Rzut okiem na języki programowania. Studia Podyplomowe INFORMATYKA Podstawy Informatyki

PLAN WYNIKOWY PROGRAMOWANIE APLIKACJI INTERNETOWYCH. KL IV TI 6 godziny tygodniowo (6x15 tygodni =90 godzin ),

Java w 21 dni / Rogers Cadenhead. Gliwice, cop Spis treści. O autorze 11. Wprowadzenie 13 TYDZIEŃ I JĘZYK JAVA

INFORMATYKA, TECHNOLOGIA INFORMACYJNA ORAZ INFORMATYKA W LOGISTYCE

Podstawy programowania

Programowanie w języku Python. Grażyna Koba

1 Podstawy c++ w pigułce.

JAVA. Platforma JSE: Środowiska programistyczne dla języka Java. Wstęp do programowania w języku obiektowym. Opracował: Andrzej Nowak

KONSTRUKCJA KOMPILATORÓW

Podstawy języka Java. przygotował:

Język ludzki kod maszynowy

Krótka Historia. Co to jest NetBeans? Historia. NetBeans Platform NetBeans IDE NetBeans Mobility Pack Zintegrowane moduły. Paczki do NetBeans.

Java Podstawy. Michał Bereta

Spis treści. 1 Java T M

SYSTEMY OPERACYJNE: STRUKTURY I FUNKCJE (opracowano na podstawie skryptu PP: Królikowski Z., Sajkowski M. 1992: Użytkowanie systemu operacyjnego UNIX)

1.Wstęp. 2.Generowanie systemu w EDK

Architektury Usług Internetowych. Laboratorium 2. Usługi sieciowe

Java Podstawy JUST JAVA Michał Bereta Intytu Modelowania Komputerowego Wydział Fizyki, Matematyki i Informatyki Stosowanej

Narzędzia i aplikacje Java EE. Usługi sieciowe Paweł Czarnul pczarnul@eti.pg.gda.pl

Tworzenie aplikacji w języku Java

Algorytm. a programowanie -

JAVA. Java jest wszechstronnym językiem programowania, zorientowanym. apletów oraz samodzielnych aplikacji.

Wstęp do programowania w języku Java

SYSTEMY OPERACYJNE I SIECI KOMPUTEROWE

Systemy operacyjne. System operacyjny Linux - wstęp. Anna Wojak

Programowanie Urządzeń Mobilnych. Laboratorium nr 7, 8

Ćwiczenie Nr 6 Przegląd pozostałych najważniejszych mechanizmów systemu operacyjnego Windows

JAVA. Tomasz Grzywacz.

Wstęp do programowania w języku Java

Narzędzie pomocnicze do przedmiotu Podstawy Informatyki - złożoność obliczeniowa

Programowanie w C. dr inż. Stanisław Wszelak

Java. język programowania obiektowego. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

Programowanie dla początkujących w 24 godziny / Greg Perry, Dean Miller. Gliwice, cop Spis treści

Programowanie w Javie

Informatyka I. dr inż. Andrzej Czerepicki.

JDK można pobrać ze strony

Zasady programowania Dokumentacja

Javadoc. Piotr Dąbrowiecki Sławomir Pawlewicz Alan Pilawa Joanna Sobczyk Alina Strachocka

Programowanie i techniki algorytmiczne

Multimedia JAVA. Historia

Języki programowania zasady ich tworzenia

Laboratorium Informatyka (I) AiR Ćwiczenia z debugowania

Sposoby tworzenia projektu zawierającego aplet w środowisku NetBeans. Metody zabezpieczenia komputera użytkownika przed działaniem apletu.

Programowanie w Internecie. Java

Analiza leksykalna 1. Teoria kompilacji. Dr inż. Janusz Majewski Katedra Informatyki

Wprowadzenie. Organizacja pracy i środowisko programistyczne. Mirosław Ochodek

Języki i metodyka programowania

Programowanie niskopoziomowe. dr inż. Paweł Pełczyński

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Pierwsze kroki. Algorytmy, niektóre zasady programowania, kompilacja, pierwszy program i jego struktura

SYSTEMY OPERACYJNE I SIECI KOMPUTEROWE

Języki i paradygmaty programowania doc. dr inż. Tadeusz Jeleniewski

Wprowadzenie do środowiska Qt Creator

Programowanie I. O czym będziemy mówili. Plan wykładu nieco dokładniej. Plan wykładu z lotu ptaka. Podstawy programowania w językach. Uwaga!

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

Język JAVA podstawy. wykład 1, część 3. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

QEMU działa na procesorach procesorach: emuluje procesory: dostępne dla s.o. hosta:

Tworzenie aplikacji Web Alicja Zwiewka. Page 1

Po uruchomieniu programu nasza litera zostanie wyświetlona na ekranie

Widoczność zmiennych Czy wartości każdej zmiennej można zmieniać w dowolnym miejscu kodu? Czy można zadeklarować dwie zmienne o takich samych nazwach?

16MB - 2GB 2MB - 128MB

Przykładowe sprawozdanie. Jan Pustelnik

Jeśli chcesz łatwo i szybko opanować podstawy C++, sięgnij po tę książkę.

Wprowadzenie do języka Java

Jak napisać program obliczający pola powierzchni różnych figur płaskich?

Laboratorium 03: Podstawowe konstrukcje w języku Java [2h]

Zapisywanie algorytmów w języku programowania

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Programowanie w języku C++ Grażyna Koba

Struktury systemów operacyjnych

Informatyka- wykład. Podstawy programowania w Pythonie. dr Marcin Ziółkowski

Bezpieczeństwo systemów komputerowych. Java i JavaScript. Java i JavaScript. Java - historia

Wprowadzenie do programowania w języku Visual Basic. Podstawowe instrukcje języka

Transkrypt:

POLITECHNIKA ŚLĄSKA WYDZIAŁ INŻYNIERII MATERIAŁOWEJ I METALURGII Edukacja Techniczno Informatyczna Studia dzienne Praca dyplomowa inżynierska Tomasz Babiarz PORÓWNANIE KOMPILATORÓW DLA ŚRODOWISKA JAVA COMPARISON OF JAVA COMPILERS Kierujący pracą: dr inż. Marcin Blachnik Katowice, marzec 2010r. 1

Spis treści 1. Wstęp... 3 1.1 Cel pracy... 4 1.2 Wersje Javy... 5 1.3 Tworzenie programu w Javie... 6 2. Wirtualna maszyny Javy... 7 3. Kompilatory... 9 3.1 Kompilacja w Javie... 14 3.2 Javac... 19 3.3 Jikes... 22 3.4 GCJ... 22 4. Część praktyczna... 24 4.1 Wyniki testów... 31 4.2 Wykresy... 32 4.3 Wnioski... 35 5. Podsumowanie... 36 6. Literatura... 37 7. Spis rysunków... 38 8. Spis tabel... 39 9. Spis wykresów... 40 2

1. Wstęp Początki JAVY sięgają 1991, kiedy to James Gosling wraz z grupką inżynierów rozpoczął prace nad niewielkim językiem komputerowym, który znalazłby zastosowanie w niewielkich urządzeniach elektronicznych. Z powodu, iż urządzenia te nie posiadały za wiele pamięci, język musiał być mały a przy tym generować prosty kod maszynowy. Ponieważ producenci sprzętu elektronicznego montowali w swoich produktach rożne procesory, ważne było aby język nie był zależny od żadnej konkretnej architektury, a tym samym był przenośny. Projekt ten początkowo został nazwany Green [1]. Projektanci oparli swój język na C++ czerpiąc z niego to, co najlepsze wprowadzając jednocześnie dużo prostszą składnie. Gotowy język ochrzczono nazwą Oak (ang. dąb). Ta nazwa nie zakorzeniła się na długo gdyż, jak się okazało była już zajęta przez inny język programowania, więc została zmieniona na Java. Swój debiut na scenie internetowej Java miała pod koniec 1995 roku od razu podbijając serca użytkowników. Za zadanie postawiono jej aby stała się uniwersalnym łącznikiem miedzy użytkownikami a informacjami, bez zależności, czy informacja pochodzi z serwerów stron internetowych, baz danych czy innego miejsca. Jak się okazało Java doskonale spełnia pokładane w niej nadzieje, co sprawiło że uzyskała akceptacje wszystkich największych firm oprócz Microsoftu [2]. Java posiada wbudowany system ochrony i bezpieczeństwa, który zapewnia komfort użytkownikom i programistom, jak i również wbudowane biblioteki uproszczające łączność między bazami danych, programowanie sieciowe. Jedną z najważniejszy zmian jaka została wprowadzona w porównaniu do poprzedników, jest to, że kod źródłowy programu jest kompilowany do kodu pośredniego. Sprawia to, że skompilowany kod nie jest programem, który można od razu uruchomić lecz powstaje tzw. Beta-kod interpretowany przez zainstalowaną na dowolnej stacji roboczej maszynie wirtualnej. Takie rozwiązanie sprawia, że powstały kod jest niezależny od konfiguracji sprzętowych i systemowych [2]. 3

Rysunek 1. Wieloplatformowość Javy 1.1 Cel pracy Nie od dziś wiadomo, że Java jest jednym z najpopularniejszych języków programowania. Popularność Javy sprawiła, że jest dla niej dostępny obok oficjalnego kompilatora producenta Javy, firmy Sun, szereg innych rozwiązań. Spośród dostępnych kompilatorów dla języka Java, dwa z nich produkowane są przez jedne z największych firm udostępniających oprogramowanie: Sun Microsystems i IBM. Są to Javac i Jikes, które konkurują z tworzonym przez projekt GNU kompilatorem GCJ. Wszystkie wyżej wymienione programy są do pobrania bezpłatnie ze stron ich producentów. Celem pracy będzie przedstawienie i porównanie tych kompilatorów na podstawie pomiaru czasu wykonywania się 4

kodu maszynowego powstałego podczas kompilacji. Kod uruchamiany będzie na najpopularniejszej obecnie wśród maszyn wirtualnych, wirtualnej maszynie Javy. Dla porównania wyników, testy zostaną przeprowadzone pod dwoma systemami operacyjnymi zainstalowanymi na programie wirtualizującym stacje roboczą VirtualBox firmy Sun Microsystems. Wyżej wymienionymi systemami będą Microsoft Windows XP z Service Pack iem 2 oraz Ubuntu 10.4. Programy, którymi zostaną przetestowane kompilatory, będą korzystać z najpopularniejszych funkcji wykorzystywanych przez programistów. W celu wyeliminowania ewentualnych błędów pomiarowych całość testów zostanie powtórzona dziesięciokrotnie, a wszystkie wyniki uśrednione. 1.2 Wersje Javy Do tej pory firma Sun Microsystems wydała już kilka wersji Javy, którymi są: 1.0x pierwsza uboga i niedopracowana wersja Javy 1.1x wprowadzenie: nowego modelu zdarzeń, Swing, JavaBeans 1.2x zmiana nazwy interfejsu programistycznego z JDK (Java Development Kit) na Java 2 SDK (Software Development Kit), integracja obecnych rozwiązań platforma Java 2 posiadająca trzy niezależne edycje: standardowa J2SE (Java 2 Standard Edition) biznesowa J2EE (Java 2 Enterprise Edition) mikro J2ME (Java 2 Micro Edition) Java 6 obecna wersja platformy Javy dostępna w trzech wersjach: Obecnie dostępne wersje to: JSE 6.0 (Java Standard Edition) - środowisko podstawowe do standardowych zastosowań komputerów osobistych i serverów 5

JEE 6.0 (Java 2 Enterprise Edition) środowisko do tworzenia zaawansowanych i rozbudowanych aplikacji biznesowych, stworzone na bazie JSE JME 3.0 (Java 2 Mobility Edition) środowisko do tworzenie aplikacji do urządzeń przenośnych o ograniczonych zasobach np. telefony komórkowe, palmtopy[3][4] 1.3 Tworzenie programu w Javie Do stworzenia najprostszego programu w Javie nie jest potrzebny żaden specjalnie dedykowany program. Wystarczy dowolny edytor tekstu np. notatnik. Dla wszystkich programów postępujemy według schematu: Stworzenie pliku źródłowego zawierającego klasę o takiej samej nazwie jak nazwa pliku Zapisanie kodu w pliku z rozszerzeniu *.java Kompilacja pliku z kodem źródłowym o rozszerzeniu *.java do pliku o rozszerzeniu *.class Uruchomienie w środowisku interpretatora Beta-kodu Pisanie programów w notatniku nie jest jednak komfortowe, gdyż istnieje duże prawdopodobieństwo popełnienia błędu, co uniemożliwi kompilacje i późniejsze uruchomienie programu. Dlatego najlepiej korzystać z jakiegoś środowiska programistycznego, które sprawdzi czy nie popełniliśmy blędów np. literówek, oraz w znaczący sposób ułatwi programowanie. Najpopularniejszymi środowiskami wśród programistów są Eclipse i NetBeans. Oba programy są darmowe i można je ściągnąć ze stron ich producentów: http://www.eclipse.org/ http://netbeans.org/ 6

2. Wirtualna maszyny Javy Wirtualna maszyna jest to system uruchomieniowy dla programów, którego charakterystyczną cechą jest niezależność od platformy, na której jest zainstalowany. Obecnie najpopularniejszą maszyną wirtualna jest JVM firmy Sun Microsystems. Istnieje także kilka innych wirtualnych maszyn, produkowanych przez tak znane i uznane firmy jak IBM czy Oracle. Maszyny wirtualne wyżej wymienionych firm nie znalazły jednak zbyt dużej popularności wśród zwykłych użytkowników. Firma SUN Microsystems dostarcza bezpłatnie oprogramowanie Java w dwóch wersjach Java SE Development Kit 6u18 oraz Java SE Runtime Environment 6u18. Środowisko RE zawierający podstawowy pakiet programów pozwalających na ich uruchomienie. Wirtualna maszyna Javy posiada wsparcie dla wszystkich wersji Windows a oraz dla różnych dystrybucji Linuxa i Solarisa. Java Software Development Kit jest zestawem narzędzi przeznaczonym dla programistów w jego skład wchodzą: narzędzia do budowania, kompilacji i uruchamiania programu, narzędzia do dokumentowania i archiwizacji programów, pakiety klas standardowych przykładowe aplety i aplikacje. Zainstalowany pakiet SDK zawiera min. katalogi: bin zawierający programy usługowe: javac kompilator, java - interpreter, appletviewer przeglądarka apletów, javadoc - generator dokumentacji, jdb debuger, jar narzędzie do tworzenia archiwów, 7

jre zawierający środowisko uruchomieniowe, maszynę wirtualną - Javy, bibliotekę klas, lib zawierający dodatkowe biblioteki klas, demo zawierający aplety i aplikacje demonstracyjne, src.zip kody źródłowe klas standardowych, docs zawierający znormalizowana dokumentacja pakietu w postaci stron HTML. [2] 8

3. Kompilatory Kompilator najprościej możemy opisać jako program, który czyta kod napisany w jednym języku (języku źródłowym) i tłumaczy go na program równoważny (język wynikowy). Bardzo ważnym elementem procesu translacji jest zgłaszanie i wyświetlanie użytkownikowi komunikatów o ewentualnych błędach w programie źródłowym. Rysunek 2. Ogólny schemat kompilatora Analizując ilość dostępnych kompilatorów początkowo możemy zostać przytłoczeni. Obecnie istnieją tysiące języków źródłowych, licząc od tradycyjnych języków programowania Java, Pascal, rodzina języków C, aż po wyspecjalizowane języki, stosowane do bardzo różnych zastosowań komputera. W takim samym stopniu zróżnicowane są języki wynikowe, którymi mogą być: inny język programowania albo kod maszynowy dowolnego urządzenia elektronicznego (od procesor mikrofalówki po procesor komputera). Zazwyczaj kompilatory są sklasyfikowane w oparciu o cel ich przeznaczenia jak i w zależności od ich konstrukcji. Kompilatory dzielą się na: jednoprzebiegowe wieloprzebiegowe uruchomieniowe (ang. debugging) typu załaduj i uruchom (ang. load-and-go) 9

Pomimo zróżnicowanych zadań kompilatorów, podstawowe ich funkcje pozostają takie same. Kompilatory działają w fazach, które następując kolejno po sobie przekształcają program z jednej postaci na inną. Poniżej znajduje się schemat pozwalający zobrazować to zagadnienie Rysunek 3. Schemat blokowy kompilatora 10

Pierwsze trzy części składają się na część analizującą kompilatora. W jej skład wchodzi : analizator leksykalny analizator składnikowy analizator semantyczny Pozostałe części kompilatora to: generator kodu pośredniego optymalizator kodu generator kodu Zarządzanie tablicą symboli i obłoga błędów współdziałają ze wszystkimi elementami i zazwyczaj również nazywane fazami. W celu ułatwienia rozrumienia zasady działania kompilatorów Javy, trzeba przynajmniej znać sposób działania niektórych z wyżej wymienionych elementów składowych uogólnionego kompilatora. Jak już wiemy praca kompilatora przebiega w fazach, a sam kompilator zbudowany jest z kilku części. Pierwsze fazy kompilatora wchodzą w skład analizy źródłowej programu. W wcześniej przedstawionym modelu kompilatora program źródłowy w pierwszej kolejności trafia do analizatora leksykalnego. Jego głównym zadaniem jest czytanie znaków z wejścia oraz produkcja symboli leksykalnych do analizy składniowej. 11

Rysunek 4. Wymiana danych miedzy analizatorem leksykalnym i składniowym Powyższy schemat obrazuje oddziaływanie między analizatorem leksykalnym a analizatorem składniowym. Współpraca między nimi polega na tym, że po otrzymaniu polecenia daj następny symbol od analizatora składniowego, analizator leksykalny czyta wszystkie znaki z wejścia, aż zidentyfikuje kolejny symbol leksykalny. Analizator leksykalny będąc częścią kompilatora czytającą tekst źródłowy również może wykonywać pewne zadania związane z interfejsem użytkownika. Jednym z nich może być omijanie z pliku wejściowego komentarzy i białych znaków czyli spacji, tabulacji i nowych wierszy. Kolejnym zadaniem jest dopasowywanie wyświetlanych komunikatów do określonego miejsca w kodzie źródłowym programu. Polega to na śledzeniu przez analizator leksykalny wczytanych wierszy z wejścia aby kompilator podczas wystąpienia ewentualnego błędu mógł razem z komunikatem o danym błędzie wyświetlić numer wiersza, w którym on występuje. Zdarza się, że analizatory leksykalne podzielone są na dwie fazy, pierwszą z nich jest skanowanie, a drugą analiza leksykalna. Skaner odpowiedzialny jest za wykonywanie prostych zadań, a analizator leksykalny zajmuje się tymi bardziej skompilowanymi. Kolejną istotnym elementem kompilatora, jest analizator składniowy, który otrzymuje ciąg znaków leksykalnych od analizatora leksykalnego, a następnie 12

sprawdza czy dany ciąg może zostać wygenerowany przez gramatykę dla języka źródłowego. Analizator składniowy ma również jeszcze jedno bardzo istotne zadanie, mianowicie ma zgłaszać występowanie błędów składniowych. Jest to bezpośrednio związane z tym, że nie realne jest aby kompilator przetwarzał tylko poprawne programy, gdyż programiści to też ludzie i zdarzają się im błędy. Gdyby jednak dało się ich całkowicie unikać, wtedy projekt i stworzenie kompilatora bardzo by się uprościły. Niestety tak nie jest i często zdarza się, że programiści piszą niepoprawne programy, więc dobry kompilator powinien pomóc programiście w zlokalizowaniu błędu i jego usunięciu. To spowodowało, że obsługa błędów w analizatorze składniowym ma postawione proste cele: zgłaszać obecność błędu, w jasny sposób w razie wykrycia błędu, szybo wracać do analizy składniowej w celu wykrycia kolejnych błędów nie spowalniać w sposób znaczący przetwarzania poprawnych programów. Jedną z ważniejszych funkcji kompilatora jest zapamiętywanie identyfikatorów, które są używane w programie źródłowym oraz zbieranie informacji o ich różnych atrybutach. Atrybuty mogą dostarczać informacji na temat ilości zajętej pamięci przez identyfikator, o jego typie, zasięgu (jego dostępność i widoczność w programie). Także w przypadku nazw atrybuty podają liczbę i typy argumentów, metody przekazywania każdego argumentu oraz typ wyniku o ile ta metoda zwraca wynik. Tablica symboli jest zbudowana ze zbioru rekordów z atrybutami dla wszystkich identyfikatorów. Struktura to za zadanie ma umożliwiać szybkie znalezienie rekordu dla każdego identyfikatora oraz szybkie zapisywanie i odczytywanie danych z rekordu. Identyfikator napotkany przez analizę leksykalną dodawany jest do tablicy symboli. Reszta faz wstawia do tablicy informacje o identyfikatorach, aby w kolejnych etapach kompilacji wykorzystać je do różnych celów takich jak; sprawdzenie podczas analizy semantycznej i generacji kodu czy identyfikatory używane są poprawnie oraz wygenerowania poprawnych operacji na nich działających. Dokładne informacje na temat przydzielonej pamięci identyfikatorom są niezbędne do działania generatora kodu [5]. 13

Ostatnim etapem pracy kompilatora Javy jest generowanie kodu pośredniego (inne kompilatory np. języka C posiadają jeszcze dodatkowo generatory kodu wynikowego). Efektem jego pracy jest stworzenie tzw. Beta kodu, które zostanie opisane w dalszej części pracy 3.1 Kompilacja w Javie Znając ogólne zasady działania kompilatorów możemy przyjrzeć się jak wygląda kompilacja programu napisanego w konkretnym języku, którym w naszym przypadku jest Java. Jak wiemy proces kompilacji jest niezbędny do uruchomienia programu napisanego w Javie. Polega on na zamianie kodu źródłowego programu na kod bajtowy, a następnie bezpośrednio przed jego wykonaniem na kod maszynowy, co dobrze obrazuje poniższy schemat Rysunek 5. Ogólny schemat kompilacji programu napisanego w Javie 14

Podczas kompilacji plików z kodem Javy (pliki *.java) tworzone są instrukcje kodu bajtowego (pliki *.class) które zostają wykonane przez interpreter kodu bajtowego. Jednak dobrze by było przyjrzeć się bliżej temu procesowi gdyż, co prawda cała kompilacja od strony praktycznej sprowadza się do wpisania polecenia w linii komend i jest przeprowadzana automatycznie przez kompilator, jednak obserwacja efektów jego działania należy już do użytkownika. Programista tworzący programy w Javie powinien wiedzieć jaka metodą powstaje kod maszynowy dla danej platformy, czyli innymi słowy jaka jest droga pomiędzy kodem źródłowym a kodem wykonywalnym. Musimy sobie jednak uświadomić, że użytkownik nie zawsze ma dostęp do kodu wykonywalnego na daną platformę, ponieważ taki kod może być generowany dopiero w trakcie uruchamiania programu. Aby wyjaśnić cały mechanizm generowania kodu maszynowego programu stworzonego w Javie dla danej platformy trzeba przedstawić kilka podstawowych zagadnień z nim związanych. Kod maszynowy jest ciągiem liczb interpretowanych przez komputer, a konkretnie jego procesor w celu wywołania pożądanego efektu. Używanie serii liczb w celu wykonania określonego zadania nie jest jednak zbyt efektywne, a dla samego użytkownika niekomfortowe. Dlatego też stworzony został prosty język, który posiada proste instrukcje wywoływane z odpowiednimi parametrami lub wartościami. Tak przygotowany kod tłumaczony jest przez komputer na kod maszynowy. Opisywany wyżej język nosi nazwę assembler, a w przypadku Javy kod źródłowy kompilowany jest do pseudo-assamblera nazwanego b-kodem. Instrukcje napisane w B-kodzie, tłumaczone są na odpowiadające im ciągi liczb (kod maszynowy) w trakcie uruchomienia programu (klasy). W praktyce oznacza to, że jedna linia kodu napisanego w assamblerze zwykle generuje jedną linie kodu maszynowego. Dla porównania assemblera i pseudo-assamblera, poniżej przedstawiony jest fragment kodu w assemblerze, który wygląda następująco: add eax, edx mov ecx, eax 15

Natomiast te same instrukcje, w pseudo-assamblerze wyglądają zupełnie inaczej [6]: 0 iload_1 1 iload_2 2 iadd 3 istore_3 W celu ułatwienia życia programistom stworzone zostały języki tzw. wysokiego rzędu jak np. C, C++, Pascal w których pojedyncza linia komend może być zamieniona na kilka linii kodu maszynowego dla danej platformy. Cały proces konwertowania kodu źródłowego języka wysokiego rzędu do kodu wykonywalnego nazwano kompilacją statyczną i w przypadku technologii Java sprowadza się do kompilacji kodu źródłowego do B-kodu. Proces kompilacji składa się z siedmiu podstawowych procesów wymienionych w poprzednim rozdziale. tłumaczenie kodu źródłowego generacja kodu maszynowego/b-kodu optymalizacja kodu maszynowego/b-kodu Pierwszym etapem jest tłumaczenie kodu źródłowego, które polega na wydobyciu z tekstu poszczególnych elementów języka np. if, ), class ; a w kolejnym etapie połączenie w wyrażenia języka. W momencie gdy zostaną napotkane elementy niezrozumiałe dla danego języka, które nie są zgodne z jego wzorcami, to kompilator przerywa pracę i zwraca użytkownikowi błąd, co zazwyczaj niesie za sobą konieczność skorygowania elementów powodujących konflikty. Jeżeli tłumaczenie całego kodu przebiegnie pomyślnie to następuje konwersja do B-kodu, który w kolejnym kroku zostaje zoptymalizowany. Celem procesu optymalizacji jest zmniejszenie rozmiarów kodu oraz poprawa szybkości jego działania. Wyrażenia języka są bardzo często kompilowane i tworzą biblioteki czyli gotowe zbiory kodów, które możemy wykorzystać podczas konstrukcji własnego programu. Podczas kompilacji programu korzystamy więc z gotowych wcześniej skompilowanych kodów. Zbiór bibliotek stanowi niezbędną część zasobów danego języka programowania, jednak mogą być też wytwarzane przez użytkowników środowiska tworzenia programów. Krótko podsumowując 16

kompilacja statyczna jest procesem konwertowania kodu źródłowego na kod pośredni. Kod pośredni (B-kod) podczas uruchomienia aplikacji jest konwertowany na kod maszynowy. Do tego celu służą różne techniki. Jedną z nich jest interpretowanie, które polega na cyklicznym pobieraniu instrukcji języka, tłumaczeniu ich na kod maszynowy, generacji i wykonywaniu. Przykładowymi interpreterami są powłoki (ang. shell) systemów operacyjnych przykładem, których są DOS, bash, csh. Największą wada interpretowanie jest jednak to, że nie można wykonać optymalizacji kodu maszynowego, gdyż nie jest on dostępny. Jeżeli chodzi o Javę interpretowanie kodu wygląda jednak trochę inaczej. Związane jest to z jedną z głównych cech, która była jednym z powodów jej stworzenia. Mowa oczywiście o przenośności kodu. Założenie, że kod może być uruchamiany na każdej platformie sprzętowej spowodowało konieczność stworzenia takich interpretatorów, które umożliwiałyby efektywną konwersję tego samego kodu źródłowego na kod wykonywujący się tak samo na rożnych platformach sprzętowych. Proces interpretacji zajmuje stosunkowo bardzo dużo czasu, dlatego konieczne były pewne modyfikacje przyśpieszające go, aby uruchamiany program był odpowiednio efektywny. Zabiegi te znacznie poprawiły wydajność programów napisanych w Javie. Proces ten przebiega dwuetapowo. Pierwszym etapem opisanym wyżej jest kompilacja kodu źródłowego na kod pośredniego, który nosi również nazwę kodu maszyny wirtualnej. Kod pośredni jest efektem translacji kodu źródłowego zgodnie z architekturą języka Java. W wyniku translacji powstaje zestaw bajtów, który przed uruchomieniem musi być przekonwertowany na kod maszynowy zgodny z architekturą platformy sprzętowej, na której jest uruchamiany. W języku używanym przez programistów zestaw bajtów jest również nazywany Beta-kodem lub B-kodem. Kod wynikowy konkretnej platformy powstaje w wyniku interpretowania Beta-kodu przez interpreter maszyny wirtualnej. Potrzeba jeszcze większego przyśpieszenia procesu kompilacji spowodowała, że zamiast interpretatora B-kodu opracowano różne kompilatory dynamiczne. Ich działanie oparte jest na kompilacji w locie B-kodu do kodu wykonywalnego dla danej maszyny. Otrzymany w ten sposób kod wykonywalny 17

umieszczany jest w pamięci komputera, tym samym oznacza to, że nie jest zapisywany w postaci pliku na dysku. Z tego wynika, że kod po skończeniu działania programu jest po prostu niedostępny. [2] Ta metoda kompilacji została nazwana kompilacją JIT Just-In-Time (kompilacja w locie). Poprawa wydajności w stosunku do interpreterów kodu polega na buforowaniu bloków kodu, a nie interpretowaniu każdej linii po kolei za każdym razem gdy program jest otwierany. Kod źródłowy skompilowany do Beta-kodu nie jest przypisany do konkretnego komputera co sprawia, że może on być przenoszony miedzy rożnymi architekturami. Taki kod może być interpretowany lub uruchamiany na maszynie wirtualnej. Mimo, iż kompilacja JIT ma za zadanie przyśpieszenie wykonywania programów zwykle powoduje nieznacznie opóźnienie w początkowym etapie realizacji kodu. Wpływa na to czas potrzebny do załadowania i skompilowania Beta-kodu. Kod wykonuje się tym lepiej im więcej razy zostanie wykonany.[7] Receptą na potrzebę rozgrzewania się wyeliminowano przez stworzenie kompilatora AOT (ang. Ahead-of-time), który przed uruchomieniem kodu jeszcze raz go przekompilowuje wprowadzając przy tym zmiany, które poprawiają wydajność kodu. W Javie oprócz błędów występują również wyjątki. Wyjątki są to określone sytuacje konfliktowe spowodowane przez niewłaściwe funkcjonowanie klas i metod. Przykładem wyjątków mogą być: dzielenie przez zero, brak pliku o podanej ścieżce, brak hosta o podanym adresie, czy też brak klasy. Wystąpienie wyjątku nie powoduje zazwyczaj przerwania pracy programu. Każdy wyjątek związany jest bezpośrednio z określoną klasą i jej metodami. Przykładowe wyjątki i ich klasy to: w pakiecie java.lang.*: ClassNotFoundExeption brak klasy, ArrayIndexOutOFBoundsExeption przekroczenie zakresu tablicy, ArithmeticExeption wyjątek operacji arytmetycznych np. dzielenie przez zero, w pakiecie java.io.*; 18

EOFExeption koniec pliku, FileNotFoundExeption brak pliku, InterruptedIOExeption przerwanie operacji we/wy. Przykładowe błedy: OutOfMemoryError bład braku pamięci, NonSuchMethodError bład braku danej metody w klasie, NonSuchFieldError bład braku danego pola w klasie [2]. 3.2 Javac Javac jest podstawowym narzędziem wchodzącym w skład JVM (Java Virtual Machine) służącym do kompilacji programów. Jego najnowsza wersja ma oznaczenie 1.6.0_17. Pierwszym krokiem do rozpoczęcia kompilacji programów jest instalacja pakietu Java SE Development Kit 6u18 dostępny do pobrania z http://java.sun.com/javase/downloads/widget/jdk6.jsp. Aby rozpocząć prace z kompilatorem Javac należy rozpocząć od dodania zmiennej środowiskowej PATH do katalogu bin zawierającego kompilator Javac i interpreter Java. 19

Rysunek 6. Dodawanie zmiennej środowiskowej Po tej czynności można bez problemu korzystać z kompilatora. Jego uruchomienie odbywa się z linii komend. Wprowadzona zmienna środowiskowa pozwala nam na nie podawanie całej ścieżki dostępu do kompilatora. Wystarczające jest przejście do katalogu z plikiem programu i wprowadzenie jedynie polecenia javac nazwa_pliku.java. Jeżeli program został prawidłowo skompilowany konsola powinna nie wyświetlić żadnego błędu a jedynie nową linie komend. Gotowy program uruchamiamy poleceniem java nazwa_pliku. Ważne aby nie dodawać rozszeżenia class, ponieważ wtedy program nie uruchomi się. 20

Rysunek 7. Poprawnie skompilowany program Javac zawiera duża ilość opcji i ustawień kompilacji są to min.: -g generuje wszystkie informacje debugowania -g:none generuje informacje bez błędów -g:{lines,vars,source generuje tylko niektóre informacje -nowarn nie wyświetla ostrzeżeń -verbose wyświetla informacje na temat pracy kompilatora -classpath <path> określa ścieżkę dodatkowych klas użytkownika -cp <path> j.w. -sourcepath <path> lokalizacja źródła plików wejściowych -extdirs <dirs> zmienia lokalizację zainstalowanych rozszerzeń -d <directory> określ, gdzie umieścić wygenerowane pliki class -s <directory> określ, gdzie umieścić wygenerowane pliki źródłowe -target <release> generowanie plików class dla konkretnych wersji VM -version informacje o wersji -help wyświetla standardowe opcje -X wyświetla niestandardowe opcje [8] 21

3.3 Jikes Jikes jest produktem IBM a jednak projekt ten nie jest od kilku lat rozwijany. Jego ewaluacja stanęła na wersji 1.22. Jikes jest tylko kompilatorem, nie wchodzi w skład żadnej maszyny wirtualnej przez to jego rozmiary są niewielkie. Program można z łatwością pobrać z http://sourceforge.net/projects/jikes/files/ wybierając wersje odpowiadającą naszemu systemowi operacyjnemu. Kompilator jest dostępny w wersjach dla takich platform jak: MS Windows, platformy Linux owe oraz MAC OS. Aby rozpocząć prace z kompilatorem Jikes nie potrzebna jest nam jego instalacja, jednak kompilacja kodu źródłowego wymaga zainstalowanej JVM. Jest to konieczne aby wskazać kompilatorowi położenie podstawowych klas. Dlatego kompilator do uruchomienia procesu kompilacji potrzebuje wskazania lokalizacji pliku rt.jar znajdującego się katalogu lib pakietu JRE. Gotowe polecenie wpisane w konsole wygląda następująco: jikes bootclasspath sciazka_do_rt.jar plik_kompilowany.java. Podobnie jak w przypadku kompilatora Javac jeżeli plik zostal poprawnie skompilowany konsola nie wyświetla żadnych komunikatów i zapisuje plik z rozszerzeniem *.class, który bez problemu można uruchomić poleceniem java plik_kompilowany [9]. 3.4 GCJ Ostatni z przedstawionych kompilatorów jest najczęściej stosowany przez użytkowników platform UNIX owych. GCJ czyli kompilator projektu GNU dla języka Java jest (GNU Compiler for Java) jest częścią GCC (GNU Compiler Collection) w skład którego wchodzi kompilator jak, środowisko uruchomieniowe podstawowe biblioteki i narzędzia dla języka Java. Jedną z głównych cech tego kompilatora jest to, że ułatwia integrację z innymi językami obsługiwanymi przez pakiet GCC. 22

Instalacja GCJ polega na wpisaniu w konsolę systemową Ubuntu polecenia sudo apt-get install gcj. Gdy oprogramowanie zostanie pobrane i zainstalowane w naszym systemie, możemy rozpocząć kompilacje programów. Skompilowanie programu polega na przejściu do katalogu zawierającego przygotowany wcześniej program i wpisaniu polecenia gcj C nazwa_pliku.java. Podobnie jak w poprzednich przypadkach, jeżeli program został prawidłowo skompilowany to konsola nie wyświetla żadnych błędów. Trzeba jednak pamiętać że GCJ jest bardziej wrażliwy na błędy występujące w kodzie. Przykładem może być sytuacja w której wykonywane są pewne obliczenia ale nie jest zwracana żadna wartość. Wtedy co prawda kompilator skompiluje program jednak, wyświetlany błąd zasygnalizuje nam, o zaistniałym problemie [10]. 23

4. Część praktyczna Zgodnie z zdefiniowanym celem pracy, część praktyczna będzie polegać na porównaniu szybkości wykonywania skompilowanego wyżej opisanymi kompilatorami kodu. Proces ten zostanie zrealizowany przez trzy kompilatory: Javac, Jikes i GCJ. Wszystkie skompilowane pliki zostaną uruchomione na maszynie wirtualnej Javy (JVM). W skład zestawu testującego wchodzi laptop HP Pavilon dv5 o parametrach: Procesor: Intel Core 2 Duo P7350 2GHz Pamięć: 3GB Karta graficzna NVIDIA GeForce 9600M GT (512 MB) Dysk twardy: 300GB System operacyjny: Windows Vista Ultimate Wszystkie testy zostały przeprowadzone na wirtualnie zainstalowanych systemach, którymi były: Windows XP z SP@ Ubuntu 10.4 Do uruchomienia wirtualnych systemów został użyty darmowy program VirtualBox w wersji 3.1.2 [11]. Dla lepszego porównania wyników systemy zainstalowane są na maszynach wirtualnych o takich samych ustawieniach, które przedstawiają się następująco: Pamięć podstawowa: 1GB Partycja systemowa: 8GB Pamięć video: 16MB Do testów zostało użytych 8 programów. Programy w istocie są bardzo proste i sprawdzają wydajność podstawowych klas. Każdy z kodów jest zbudowany według tego samego schematu, czyli pętli wykonywanej określoną ilość razy oraz metody, która mierzy czas wykonywania programu. Każdy program 24

został uruchomiony 10 razy, a wszystkie wyniki uśrednione. Poszczególne programy mierzyły: Program 1 wydajność typowych operacji liczbowych takich jak dodawanie, odejmowanie, mnożenie i dzielenie, kod programu poniżej: [12][13] public static double RunTest(int Wielkoscpetli) { long x = 10; double y = 0; double z = 0; for (int i = 0; i < Wielkoscpetli; i++) { x = x + 3; y = (double) x / 7; z = y * 5; x -= 3; return z; Program 2 wydajność operacji na stringach używając metody do porównania ciągu znaków, kod programu poniżej: [12][13] public static boolean RunTest(int Wielkoscpetli) { String val0_2 = "hm"; String val1_2 = "hm"; String val2_2 = "Hm"; String val3_2 = "kg"; String val4_2 = "hm1"; String val0_4 = "Hara"; String val1_4 = "Hara"; String val2_4 = "HaRa"; String val3_4 = "xfdw"; String val4_4 = "Harai"; String val0_8 = "Haralamb"; String val1_8 = "Haralamb"; String val2_8 = "haralamb"; String val3_8 = "AmbzJ3pQ"; String val4_8 = "Haralambxq"; String val0_16 = "Haralambos Marma"; String val1_16 = "Haralambos Marma"; String val2_16 = "haralambos marma"; 25

String val3_16 = "gtswq*skdn-2iosc"; String val4_16 = "Haralambos Marmanis"; boolean test = false; for (int i = 0; i < Wielkoscpetli; i++) { if (val0_2.equals(val1_2) val0_2.equals(val2_2) val0_2.equals(val3_2) val0_2.equals(val4_2)) { test =!test; if (val0_4.equals(val1_4) val0_4.equals(val2_4) val0_4.equals(val3_4) val0_4.equals(val4_4)) { test =!test; if (val0_8.equals(val1_8) val0_8.equals(val2_8) val0_8.equals(val3_8) val0_8.equals(val4_8)) { test =!test; if (val0_16.equals(val1_16) val0_16.equals(val2_16) val0_16.equals(val3_16) val0_16.equals(val4_16)) { test =!test; return test; Program 3 - konfiguracja programu jest taka sama jak programu 2, ale program używa metody equalsignorecase, która przy porównaniu ignoruje wielkość znaków. Poniżej fragment kodu, którym rożni się od poprzedniego programu: [12][13] if (val0_2.equalsignorecase(val1_2) val0_2.equalsignorecase(val2_2) val0_2.equalsignorecase(val3_2) val0_2.equalsignorecase(val4_2)) 26