Czy tworzenie plików klas Javy jest deterministyczne?

Przy użyciu tego samego JDK (tzn. tego samego javac wykonywalnego), czy wygenerowane pliki klas są zawsze identyczne? Czy może być różnica w zależności od systemu operacyjnego lub sprzętu? Poza wersją JDK, czy mogą być jakieś inne czynniki powodujące różnice? Czy są jakieś opcje kompilatora, aby uniknąć różnic? Jest różnicą tylko teoretycznie, czy javac Oracle rzeczywiście produkuje różne pliki klas dla tego samego wejścia i kompilatora opcje?

Update 1 interesuje mnie generacja , czyli wyjście kompilatora, a nie to, czy plik klasy może być uruchamiany na różnych platformach.

Update 2 przez 'Same JDK' mam też na myśli ten sam javac wykonywalny.

Update 3 rozróżnienie pomiędzy różnicą teoretyczną i praktyczną w kompilatorach Oracle.

[edytuj, dodając parafrazowane pytanie]
"Jakie są okoliczności, w których ten sam javac wykonywalny, gdy uruchomiony na innej platformie, będzie produkować inny bajt kod?"

Author: mstrap, 2013-02-20

11 answers

Ujmijmy to tak:

Mogę łatwo stworzyć całkowicie zgodny kompilator Javy, który nigdy nie produkuje tego samego pliku .class dwa razy, biorąc pod uwagę ten sam plik .java.

Mógłbym to zrobić, poprawiając wszystkie rodzaje konstrukcji kodu bajtowego lub po prostu dodając zbędne atrybuty do mojej metody (co jest dozwolone).

Biorąc pod uwagę, że Specyfikacja Nie wymaga kompilator do tworzenia bajtów dla bajtów identycznych plików klas, unikałbym zależności takiego wyniku.

jednak, kilka razy, które sprawdziłem, kompilowanie tego samego pliku źródłowego z tym samym kompilatorem z tymi samymi przełącznikami (i tymi samymi bibliotekami!) did wynik w tych samych plikach .class.

Update: ostatnio natknąłem się na ten ciekawy wpis na blogu o implementacji switch on String w Javie 7. W tym wpisie na blogu są pewne istotne części, które zacytuję tutaj (podkreślenie moje):

Aby dane wyjściowe kompilatora były przewidywalne i powtarzalne, Mapy i zestawy używane w tych strukturach danych to LinkedHashMap s i LinkedHashSet s, a nie tylko HashMaps i HashSets. pod względem poprawności funkcjonalnej kodu wygenerowanego podczas danej kompilacji, użycie HashMap i HashSet byłoby w porządku ; kolejność iteracji nie ma znaczenia. Jednak uważamy, że korzystne jest, aby wyjście javac nie różniło się w zależności od szczegółów implementacji systemu klasy .

To dość wyraźnie ilustruje problem: kompilator nie jest wymagany do działania w sposób deterministyczny, o ile pasuje do specyfikacji. Programiści kompilatora zdają sobie jednak sprawę, że jest to ogólnie dobry pomysł, aby spróbować (pod warunkiem, że nie jest to zbyt drogie, prawdopodobnie).

 63
Author: Joachim Sauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-16 14:22:59

Kompilatory nie mają obowiązku tworzenia tego samego kodu bajtowego na każdej platformie. Aby uzyskać konkretną odpowiedź, należy skonsultować się z narzędziem różnych dostawców.


Pokażę praktyczny przykład z porządkowaniem plików.

Powiedzmy, że mamy 2 pliki jar: my1.jar i My2.jar. Umieszczane są w katalogu lib obok siebie. Kompilator odczytuje je w porządku alfabetycznym (ponieważ jest to lib), ale kolejność jest my1.jar, My2.jar gdy system plików jest niewrażliwe na wielkość liter oraz My2.jar, my1.jar Jeśli rozróżnia się wielkość liter.

my1.jar ma klasę A.class z metodą

public class A {
     public static void a(String s) {}
}

My2.jar ma ten sam A.class, ale z innym podpisem metody (akceptuje Object):

public class A {
     public static void a(Object o) {}
}

Jest jasne, że jeśli masz połączenie

String s = "x"; 
A.a(s); 

Skompiluje wywołanie metody z innym podpisem w różnych przypadkach. Tak więc, w zależności od tego, czy system plików jest wrażliwy na wielkość liter, w rezultacie otrzymasz inną klasę.

 37
Author: gaborsch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-20 18:01:53

Krótka odpowiedź - NIE


Długa Odpowiedź

One bytecode nie muszą być takie same dla różnych platform. To JRE (Java Runtime Environment) wie, jak dokładnie wykonać bajt.

Jeśli przejrzysz Java VM specification dowiesz się, że nie musi to być prawdą, że bajt jest taki sam dla różnych platform.

Przechodząc przez format pliku klasy , pokazuje strukturę pliku klasy as

ClassFile {
    u4 magic;
    u2 minor_version;
    u2 major_version;
    u2 constant_pool_count;
    cp_info constant_pool[constant_pool_count-1];
    u2 access_flags;
    u2 this_class;
    u2 super_class;
    u2 interfaces_count;
    u2 interfaces[interfaces_count];
    u2 fields_count;
    field_info fields[fields_count];
    u2 methods_count;
    method_info methods[methods_count];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

Sprawdzanie wersji minor I major

Minor_version, major_version

Wartości minor_version i elementy major_version są numerami wersji minor I major tego plik klasowy.Razem numer wersji major i minor określają wersja formatu pliku klasy. Jeśli plik klasy ma główną wersję numer M i numer wersji M, oznaczamy wersję jego format pliku klasy jako M. M. Tak więc plik klasy wersje formatowe mogą być uporządkowane leksykograficznie, na przykład 1.5

Czytaj więcej w przypisach

1 wirtualna maszyna Javy implementacja Sun ' s JDK release 1.0.2 obsługuje wersje plików w formacie klas 45.0 do 45.3 włącznie. Sun ' s JDK wyda 1.1.X może obsługiwać formaty plików klas wersji w zakres od 45.0 do 45.65535 włącznie. Implementacje wersji 1.2 platformy Java 2 może obsługiwać formaty plików klas wersji w zakres 45.0 do 46.0 włącznie.

Więc, zbadanie tego wszystkiego pokazuje, że pliki klas generowane na różnych platformach nie muszą być identyczne.

 6
Author: mtk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-20 17:05:35

Po pierwsze, nie ma absolutnie takiej gwarancji w spec. Zgodny kompilator mógłby stemplować czas kompilacji do wygenerowanego pliku klasy jako dodatkowy (Niestandardowy) atrybut, a plik klasy nadal byłby poprawny. To jednak produkowałoby inny plik na poziomie bajtów na każdej pojedynczej kompilacji, i trywialnie tak.

Po drugie, nawet bez takich paskudnych sztuczek, nie ma powodu, aby oczekiwać, że kompilator zrobi dokładnie to samo dwa razy z rzędu, chyba że oba jego konfiguracja i jej wejście są identyczne w obu przypadkach. Spec opisuje nazwę pliku źródłowego jako jeden ze standardowych atrybutów, a dodanie pustych linii do pliku źródłowego może zmienić tabelę numerów linii.

Po trzecie, nigdy nie spotkałem się z żadną różnicą w budowie ze względu na platformę hosta(inną niż ta, którą można przypisać różności w tym, co było na classpath). Kod, który różni się w zależności od platformy (np. natywnych bibliotek kodu) nie jest częścią plik klasy i rzeczywiste generowanie kodu natywnego z kodu bajtowego następuje po załadowaniu klasy.

Po czwarte (i najważniejsze) to cuchnie złym zapachem procesu (jak zapach kodu, ale za to, jak działasz na kod) chcieć to wiedzieć. Wersja źródła, jeśli to możliwe, a nie kompilacji, a jeśli trzeba wersję kompilacji, wersja na poziomie całego komponentu, a nie na poszczególnych plikach klas. Dla preferencji, Użyj serwera CI (takiego jak Jenkins) do zarządzania proces przekształcania źródła w kod nadający się do pracy.

 3
Author: Donal Fellows,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-20 22:51:57

Wierzę, że jeśli użyjesz tego samego JDK, wygenerowany kod bajtowy będzie zawsze taki sam, bez związku z używanym oprogramowaniem harware i systemem operacyjnym. Tworzenie kodu bajtowego jest wykonywane przez kompilator Javy, który używa deterministycznego algorytmu do "przekształcania" kodu źródłowego w kod bajtowy. Tak więc wyjście zawsze będzie takie samo. W tych warunkach tylko aktualizacja kodu źródłowego wpłynie na wynik.

 2
Author: viniciusjssouza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-20 16:45:02

Ogólnie rzecz biorąc, muszę powiedzieć, że nie ma gwarancji, że to samo źródło wytworzy ten sam bajt kodu, gdy skompiluje go ten sam kompilator, ale na innej platformie.

Przyjrzałbym się scenariuszom obejmującym różne języki (strony kodowe), na przykład Windows z obsługą języka japońskiego. Pomyśl o znakach wielobajtowych; chyba że kompilator zawsze zakłada, że musi obsługiwać wszystkie języki, może zoptymalizować dla 8-bitowego ASCII.

Jest sekcja o kompatybilności binarnej w Specyfikacja Języka Java .

W ramach kompatybilności binarnej Release-to-Release w SOM (Forman, Conner, Danforth, and Raper, Proceedings of OOPSLA '95), Java binaria języka programowania są kompatybilne binarnie pod wszystkimi istotnymi przekształceń, które autorzy identyfikują (z pewnymi zastrzeżeniami z w odniesieniu do dodawania zmiennych instancji). Korzystając z ich schematu, oto lista niektórych ważnych binarnych kompatybilnych zmian, które Java Obsługa języka programowania:

* Reimplementowanie istniejących metod, konstruktorów i inicjalizatorów do popraw wydajność.

* Zmiana metod lub konstruktorów na zwracające wartości na wejściach, dla których wcześniej albo rzucali wyjątki, które normalnie nie powinny wystąpić lub nie udało się wejść w nieskończoną pętlę lub spowodować impas.

* dodawanie nowych pól, metod lub konstruktorów do istniejącej klasy lub interfejs.

•usuwanie prywatnych pola, metody lub konstruktory klasy.

•gdy cały pakiet jest aktualizowany, usuwanie domyślne (tylko pakiet) dostęp do pól, metod lub konstruktorów klas i interfejsów w paczka.

* Zmiana kolejności pól, metod lub konstruktorów w istniejącym typie deklaracja.

* przeniesienie metody w górę hierarchii klas.

* Zmiana kolejności listy bezpośrednich superinterfejsów klasy lub interfejs.

•Wstawianie nowe typy klas lub interfejsów w hierarchii typów.

Ten rozdział określa minimalne standardy kompatybilności binarnej gwarantowane przez wszystkie wdrożenia. Język programowania Java gwarantuje kompatybilność, gdy binaria klas i interfejsów są mieszane, które nie są znane z kompatybilnych źródeł, ale których źródła zostały zmodyfikowane w sposób zgodny opisany tutaj. Uwaga że omawiamy kompatybilność pomiędzy wydaniami podanie. A omówienie zgodności pomiędzy wydaniami Javy Platforma SE wykracza poza zakres niniejszego rozdziału.

 1
Author: Kelly S. French,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-20 18:36:01

Java allows you write/compile code on one platform and run on different platform. AFAIK ; będzie to możliwe tylko wtedy, gdy plik klasy wygenerowany na innej platformie będzie taki sam lub technicznie taki sam tzn. identyczny.

Edit

Co mam na myśli przez technicznie Ten Sam komentarz jest taki. Nie muszą być dokładnie takie same, jeśli porównasz bajt po bajcie.

Więc zgodnie ze specyfikacją .plik klas klasy na różnych platformach nie musi pasować bajt po bajcie.

 1
Author: rai.skumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-27 06:45:25

Dla pytania:

" jakie są okoliczności,w których ten sam program wykonywalny javac, uruchamiany na innej platformie, wytworzy inny bajt?"

Przykład Cross-Compilation pokazuje jak możemy użyć opcji Javac: - target version

Ta flaga generuje pliki klas, które są zgodne z wersją Javy podaną podczas wywoływania tego polecenia. Stąd pliki klas będą się różnić w zależności od atrybutów, które dostarczamy podczas compaliation przy użyciu tej opcji.

 1
Author: PhilipJoseParampettu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-27 09:30:51

Najprawdopodobniej odpowiedź brzmi "tak", ale aby uzyskać dokładną odpowiedź, trzeba wyszukać kilka kluczy lub generowanie guid podczas kompilacji.

Nie pamiętam sytuacji, w której to miało miejsce. Na przykład, aby mieć ID do celów serializacji, jest on kodowany na twardo, tzn. generowany przez programistę lub IDE. P. S. również JNI może mieć znaczenie.

P. P. S. odkryłem, że {[0] } jest napisany w Javie. Oznacza to, że jest identyczny na różnych platformach. Stąd nie generowałaby inny kod bez powodu. Tak więc może to zrobić tylko z natywnymi połączeniami.

 0
Author: Suzan Cioc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-20 16:51:42

Są dwa pytania.

Can there be a difference depending on the operating system or hardware? 

Jest to pytanie teoretyczne, a odpowiedź jest oczywiście, tak, Może być. Jak powiedzieli inni, specyfikacja nie wymaga od kompilatora tworzenia bajtowych identycznych plików klas.

Nawet jeśli każdy kompilator aktualnie istniejący wyprodukował ten sam kod bajtowy we wszystkich okolicznościach (inny sprzęt itp.), odpowiedź jutro może być inna. Jeśli nigdy nie planujesz aktualizacji javac lub systemu operacyjnego, może przetestować zachowanie tej wersji w konkretnych okolicznościach, ale wyniki mogą być inne, jeśli na przykład Java 7 Update 11 do Java 7 Update 15.

What are the circumstances where the same javac executable, when run on a different platform, will produce different bytecode?
To nie do poznania.

Nie wiem, czy zarządzanie konfiguracją jest powodem do zadawania pytania, ale jest to zrozumiały powód do troski. Porównywanie kodów bajtowych jest legalną kontrolą IT, ale tylko w celu ustalenia, czy pliki klas uległy zmianie, a nie w celu ustalenia, czy pliki źródłowe się zmieniły.

 0
Author: Skip Addison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-27 13:33:40

Ujęłabym to inaczej.

Po pierwsze, myślę, że pytanie nie dotyczy determinizmu:

Oczywiście jest to deterministyczne: przypadkowość jest trudna do osiągnięcia w informatyce i nie ma powodu, dla którego kompilator by ją tutaj wprowadził.

Po drugie, jeśli przeformułujesz go przez "jak podobne są pliki bajtowe dla tego samego pliku kodu źródłowego ?", wtedy Nie., nie można polegać na tym, że będą podobne.

Dobry sposób na zrobienie pewny tego, jest opuszczając .klasa (lub .pyc w moim przypadku) na etapie git. Zdasz sobie sprawę, że wśród różnych komputerów w Twoim zespole, git zauważa zmiany pomiędzy .plików pyc, gdy do pliku .py (i .pyc rekompilowany tak czy siak).

Przynajmniej to zauważyłem. Więc wpisz *.pyc i *.klasa w Twoim .gitignore !

 0
Author: Augustin Riedinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-27 14:37:04