Java sınıfı dosyalarının oluşturulması belirleyici midir?


94

Kullanırken aynı JDK (yani aynı javacçalıştırılabilir), oluşturulan sınıf dosyaları her zaman aynıdır? İşletim sistemine veya donanıma bağlı olarak bir fark olabilir mi? JDK sürümü dışında, farklılıklara neden olan başka faktörler olabilir mi? Farklılıkları önlemek için herhangi bir derleyici seçeneği var mı? Muhtemelen teoride bir fark mı var yoksa Oracle'lar javacaynı girdi ve derleyici seçenekleri için gerçekten farklı sınıf dosyaları mı üretiyor?

Güncelleştirme 1 ilgilendiğim nesil , yani derleyici çıktı, bir sınıf dosyası edilebilir olup olmadığı çalıştırmak çeşitli platformlarda.

Güncelleme 2 'Aynı JDK' ile, aynı javacçalıştırılabilir dosyayı da kastediyorum .

Güncelleme 3 Oracle'ın derleyicilerindeki teorik fark ile pratik fark arasındaki ayrım.

[DÜZENLE, başka kelimelerle ifade edilmiş bir soru ekleyerek]
"Aynı javac yürütülebilir dosyasının farklı bir platformda çalıştırıldığında farklı bayt kodu üreteceği durumlar nelerdir?"


5
@Gamb CORA yok değil farklı platformlarda derlenmiş eğer bayt kodu tamamen aynı olacağı anlamına; bunun anlamı, üretilen bayt kodunun tam olarak aynı şeyi yapacağıdır.
Sergey Kalinichenko

11
Neden umurunda? Bu bir XY Problemi gibi kokuyor .
Joachim Sauer

4
@JoachimSauer İkili dosyalarınızın sürüm kontrolünü yapıp yapmadığınızı düşünün - değişiklikleri yalnızca kaynak kodu değişmişse tespit etmek isteyebilirsiniz, ancak JDK'nın çıktı ikili dosyalarını keyfi olarak değiştirebilmesi durumunda bunun mantıklı bir fikir olmadığını bilirsiniz.
RB.

7
@RB .: Derleyicinin, derlenen kodu temsil eden herhangi bir uygun bayt kodunu üretmesine izin verilir. Aslında, bazı derleyici güncellemeleri biraz farklı kod üreten hataları düzeltir (genellikle aynı çalışma zamanı davranışıyla). Başka bir deyişle: kaynak değişikliklerini tespit etmek istiyorsanız, kaynak değişikliklerini kontrol edin .
Joachim Sauer

3
@dasblinkenlight: Sahip olduklarını iddia ettikleri cevabın aslında doğru ve güncel olduğunu varsayıyorsunuz (sorunun 2003 yılına ait olduğu düşünüldüğünde şüpheli).
Joachim Sauer

Yanıtlar:


68

Bunu böyle koyalım:

Aynı .classdosya verildiğinde, aynı .javadosyayı asla iki kez üretmeyen, tamamen uyumlu bir Java derleyicisini kolayca üretebilirim .

Bunu, her tür bayt kodu yapısını değiştirerek veya yöntemime gereksiz nitelikler ekleyerek yapabilirim (buna izin verilir).

Göz önüne alındığında şartname gelmez gerektiren üretim bayt için bayt özdeş sınıf dosyalarına derleyici, ben ediyorum bağlı önlemek böyle bir sonuç.

Ancak , birkaç kez kontrol ettiğimde, aynı kaynak dosyasını aynı derleyiciyle aynı anahtarlarla (ve aynı kitaplıklarla!) Derlemek aynı .classdosyalarla sonuçlandı .

Güncelleme: Kısa bir süre önce Java 7'de on uygulamasının uygulanmasıyla ilgili bu ilginç blog gönderisineswitchString rastladım . Bu blog yazısında, burada alıntı yapacağım bazı ilgili kısımlar var (vurgu benimkini):

Derleyicinin çıktısını tahmin edilebilir ve tekrarlanabilir kılmak için, bu veri yapılarında kullanılan haritalar ve kümeler sadece ve yerine LinkedHashMaps ve LinkedHashSets'dir . Belirli bir derleme sırasında üretilen kodun işlevsel doğruluğu açısından , kullanmak ve iyi olur ; yineleme sırası önemli değil. Ancak, çıktısının sistem sınıflarının uygulama ayrıntılarına bağlı olarak değişmemesini yararlı buluyoruz .HashMapsHashSetsHashMapHashSetjavac

Bu, sorunu oldukça açık bir şekilde göstermektedir: Derleyicinin spesifikasyonla eşleştiği sürece deterministik bir şekilde hareket etmesi gerekmez . Ancak derleyici geliştiricileri, denemenin genellikle iyi bir fikir olduğunun farkındadır (muhtemelen çok pahalı değilse).


@GaborSch ne eksik? "Aynı javac yürütülebilir dosyasının farklı bir platformda çalıştırıldığında farklı bayt kodu üreteceği durumlar nelerdir?" temelde derleyiciyi üreten grubun kaprisine bağlı olarak
emory

3
Benim için bu, ona güvenmemek için yeterli bir sebep olurdu: güncellenmiş bir JDK, derleyicinin her zaman aynı kodu ürettiği gerçeğine güvenirsem, derleme / arşiv sistemimi bozabilir.
Joachim Sauer

3
@GaborSch: Böyle bir durumun mükemmel bir örneğine zaten sahipsiniz, bu yüzden sorunla ilgili bazı ek görüşler sıradaydı. Çalışmanızı kopyalamanın bir anlamı yok.
Joachim Sauer

1
@GaborSch Temel sorun, uygulamamızın, kullanıcıların web sitesinden yalnızca değiştirilmiş JAR'ları alabileceği verimli bir "çevrimiçi güncellemesini" uygulamak istememizdir. Giriş olarak aynı sınıf dosyalarına sahip özdeş JAR'lar oluşturabilirim. Ancak soru, sınıf dosyalarının aynı kaynak dosyalardan derlendiğinde her zaman aynı olup olmadığıdır. Tüm konseptimiz bu gerçekle ayakta durmakta ve başarısız olmaktadır.
mstrap

2
@mstrap: Sonuçta bu bir XY Problemi. Pekala, kavanozların farklı güncellemelerine bakabilirsiniz (böylece bir baytlık farklılıklar bile tüm kavanozun yeniden indirilmesine neden olmaz) ve yine de sürümlerinize açık sürüm numaraları sağlamalısınız, böylece bence tüm nokta tartışmalıdır. .
Joachim Sauer

39

Derleyicilerin her platformda aynı bayt kodunu üretme zorunluluğu yoktur. javacBelirli bir cevaba sahip olmak için farklı satıcıların yardımcı programına başvurmalısınız .


Bunun için dosya sıralamasıyla pratik bir örnek göstereceğim.

Diyelim ki 2 jar dosyamız var: my1.jarve My2.jar. Onlar koymak konum lib, dizindeki yan-yana. (Bu yana derleyici alfabetik sırayla okur lib), ancak sırasıdır my1.jar, My2.jardosya sistemi duyarsız olduğu zaman, ve My2.jar, my1.jaro harf duyarlı olup olmadığını.

my1.jarBir sınıfı vardır A.classbir yöntem ile

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

My2.jarAynı sahiptir A.class, ancak farklı bir yöntem imzayla (kabul Object):

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

Bir aramanız varsa

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

farklı durumlarda farklı imzalarla bir yöntem çağrısı derleyecektir. Böylece, dosya sistemi durumunuzun hassasiyetine bağlı olarak, sonuç olarak farklı bir sınıf elde edeceksiniz.


1
+1 Eclipse derleyicisi ile javac arasında sayısız fark vardır, örneğin sentetik oluşturucuların nasıl üretildiği .
Paul Bellora

2
@GaborSch Bayt kodunun aynı JDK için aynı, yani aynı javac için aynı olup olmadığı ile ilgileniyorum. Bunu daha açık hale getireceğim.
mstrap

2
@mstrap Sorunuzu anladım, ancak cevap hala aynı: satıcıya bağlı. javacEğer her platform (örneğin Win7, Linux, Solaris, Mac) farklı ikilileri çünkü, aynı değildir. Bir satıcı için farklı uygulamalara sahip olmak mantıklı değildir, ancak platforma özgü herhangi bir sorun sonucu etkileyebilir (örneğin, bir dizinde flie siparişi (dizininiz üzerinde düşünün lib), dayanıklılık, vb.).
gaborsch

1
Genellikle, çoğu javacJava'da uygulanır (ve javacyalnızca basit bir yerel başlatıcıdır), bu nedenle çoğu platform farklılığının hiçbir etkisi olmamalıdır.
Joachim Sauer

2
@mstrap - o yapıyor nokta hiçbir olmasıdır gereksinimi ortaya çıkan baytkodu aynı sonuçlar üretir Sadece bu da onların derleyici ürünler üretmekte platformlarında tam olarak aynı bayt kodu yapmak için herhangi bir satıcı için. Herhangi bir standart / şartname / gereklilik olmadığı için sorunuzun cevabı "Bu, belirli bir satıcıya, derleyiciye ve platforma bağlıdır" olacaktır.
Brian Roach

6

Kısa Cevap - HAYIR


Uzun cevap

Bunlar bytecodefarklı platform için aynı olması gerekmez. Bayt kodunun tam olarak nasıl çalıştırılacağını bilen JRE'dir (Java Runtime Environment).

Eğer geçmesi halinde Java VM spesifikasyonu bu baytkodu farklı platformlar için aynı olduğu doğru olmamaya ihtiyacı olduğunu bilmek geleceğim.

Sınıf dosyası biçiminden geçerek , bir sınıf dosyasının yapısını şu şekilde gösterir:

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];
}

Küçük ve büyük sürüm hakkında kontrol ediliyor

minor_version, major_version

Minor_version ve major_version öğelerinin değerleri, bu sınıf dosyasının küçük ve büyük sürüm numaralarıdır. Birlikte, bir büyük ve bir ikincil sürüm numarası, sınıf dosyası biçiminin sürümünü belirler. Bir sınıf dosyasının ana sürüm numarası M ve alt sürüm numarası m varsa, sınıf dosya biçiminin sürümünü Mm olarak belirtiriz. Bu nedenle, sınıf dosya biçimi sürümleri sözlükbilimsel olarak sıralanabilir, örneğin 1.5 <2.0 <2.1. Bir Java sanal makine uygulaması, ancak ve ancak v bazı bitişik Mi.0 v Mj.m aralığında yer alıyorsa, v sürümünün bir sınıf dosya biçimini destekleyebilir. Yalnızca Sun, Java platformunun belirli bir sürüm düzeyine uygun bir Java sanal makine uygulamasının hangi sürümleri destekleyebileceğini belirleyebilir.1

Dipnotlardan daha fazlasını okumak

1 Sun'ın JDK 1.0.2 sürümünün Java sanal makine uygulaması, 45.0 ile 45.3 dahil sınıf dosya formatı sürümlerini destekler. Sun'ın JDK sürümleri 1.1.X, 45.0 ila 45.65535 (dahil) aralığındaki sürümlerin sınıf dosya formatlarını destekleyebilir. Java 2 platformunun 1.2 sürümünün uygulamaları, 45.0 ila 46.0 (dahil) aralığındaki sürümlerin sınıf dosya biçimlerini destekleyebilir.

Dolayısıyla, tüm bunları araştırmak, farklı platformlarda oluşturulan sınıf dosyalarının aynı olması gerekmediğini gösterir.


Daha detaylı bir bağlantı verebilir misiniz lütfen?
mstrap

Sanırım 'platform' derken, işletim sistemini değil Java platformunu kastediyorlar. Tabii ki, javac 1.7'ye 1.6 uyumlu sınıf dosyaları oluşturma talimatı verirken, bir fark olacaktır.
mstrap

@mtk +1 derleme sırasında tek bir sınıf için kaç özelliğin üretildiğini gösterir.
gaborsch

3

İlk olarak, teknik özelliklerde kesinlikle böyle bir garanti yoktur. Uygun bir derleyici, derleme zamanını oluşturulan sınıf dosyasına ek (özel) bir öznitelik olarak damgalayabilir ve sınıf dosyası yine de doğru olacaktır. Bununla birlikte, her bir yapıda bayt düzeyinde farklı bir dosya üretirdi ve önemsiz bir şekilde.

İkincisi, bu kadar iğrenç hileler olmasa bile, bir derleyicinin aynı şeyi arka arkaya iki kez yapmasını beklemek için hiçbir neden yoktur, hem yapılandırması hem de girdisi iki durumda aynı değildir. Spec yapar standart özellikten biri olarak kaynak dosya adını tanımlamak ve kaynak dosyaya boş satırlar ekleyerek iyice satır numarası tablosunu değiştirebilir.

Üçüncüsü, ana bilgisayar platformu nedeniyle derlemede hiçbir zaman bir farkla karşılaşmadım (sınıf yolunda olan farklılıklara atfedilebilecek olanlar dışında). Platforma (yani yerel kod kitaplıkları) göre değişen kod, sınıf dosyasının bir parçası değildir ve bayt kodundan gerçek yerel kod üretimi, sınıf yüklendikten sonra gerçekleşir.

Dördüncüsü (ve en önemlisi) kötü bir süreç kokusu kokuyor (bir kod kokusu gibi, ama koda nasıl davrandığınız için) bunu bilmek istemek. Derlemeyi değil, mümkünse kaynağı sürümleyin ve derlemeyi sürümlendirmeniz gerekiyorsa, sürümü tek tek sınıf dosyalarında değil, tüm bileşen düzeyinde sürümleyin. Tercih olarak, kaynağı çalıştırılabilir koda dönüştürme sürecini yönetmek için bir CI sunucusu (Jenkins gibi) kullanın.


2

İnanıyorum ki, aynı JDK'yı kullanırsanız, üretilen bayt kodu, kullanılan harware ve işletim sistemi ile ilişkisi olmaksızın her zaman aynı olacaktır. Bayt kodu üretimi, kaynak kodunu bayt koduna "dönüştürmek" için deterministik bir algoritma kullanan java derleyicisi tarafından yapılır. Böylece çıktı her zaman aynı olacaktır. Bu koşullarda, yalnızca kaynak kodundaki bir güncelleme çıktıyı etkileyecektir.


3
Yine de bunun için bir referansınız var mı? Soru yorumlarında da söylediğim gibi, bu kesinlikle C # için geçerli değil , bu yüzden Java için geçerli olduğunu belirten bir referans görmek isterim . Özellikle çok evreli bir derleyicinin farklı çalıştırmalarda farklı tanımlayıcı adları atayabileceğini düşünüyorum.
RB.

1
Bu sorumun cevabı ve ben ne bekliyordum, ancak bunun için bir referansın önemli olacağı konusunda RB'ye katılıyorum.
mstrap

Ben de aynı inanıyorum. Kesin bir referans bulacağınızı sanmıyorum. Sizin için önemliyse, bir çalışma yapabilirsiniz. Önde gelenlerden birkaçını toplayın ve bazı açık kaynak kodlarını derleyerek farklı platformlarda deneyin. Bayt dosyalarını karşılaştırın. Sonucu yayınlayın. Buraya bir bağlantı koyduğunuzdan emin olun.
emory

1

Genel olarak, aynı kaynağın aynı derleyici tarafından ancak farklı bir platformda derlendiğinde aynı bayt kodunu üreteceğine dair bir garanti olmadığını söylemeliyim.

Farklı dilleri (kod sayfaları) içeren senaryolara bakardım, örneğin Japonca dil desteğine sahip Windows. Çok baytlı karakterleri düşünün; derleyici her zaman tüm dilleri desteklemesi gerektiğini varsaymadığı sürece, 8-bit ASCII için optimize edebilir.

Java Dil Spesifikasyonunda ikili uyumluluk ile ilgili bir bölüm vardır .

SOM'da (Forman, Conner, Danforth ve Raper, Proceedings of OOPSLA '95) Release-to-Release Binary Compatibility çerçevesinde, Java programlama dili ikili dosyaları, yazarların tanımladığı tüm ilgili dönüşümler altında ikili uyumludur (bazı uyarılarla birlikte örnek değişkenlerin eklenmesine saygı). Şemalarını kullanarak, Java programlama dilinin desteklediği bazı önemli ikili uyumlu değişikliklerin bir listesi:

• Performansı iyileştirmek için mevcut yöntemleri, kurucuları ve başlatıcıları yeniden uygulamak.

• Yöntemleri veya yapıcıları, daha önce normalde oluşmaması gereken veya sonsuz bir döngüye girerek veya bir kilitlenmeye neden olarak başarısız olan istisnaları attığı girdilere ilişkin değerler döndürmek için değiştirme.

• Mevcut bir sınıfa veya arabirime yeni alanlar, yöntemler veya yapıcılar ekleme.

• Bir sınıfın özel alanlarını, yöntemlerini veya yapıcılarını silme.

• Tüm paket güncellendiğinde, paketteki sınıfların ve arabirimlerin varsayılan (yalnızca paket) erişim alanlarını, yöntemlerini veya yapıcılarını silme.

• Mevcut bir tür bildirimindeki alanları, yöntemleri veya yapıcıları yeniden sıralama.

• Bir yöntemi sınıf hiyerarşisinde yukarı taşıma.

• Bir sınıfın veya arabirimin doğrudan üst arabirimlerinin listesini yeniden sıralama.

• Tür hiyerarşisine yeni sınıf veya arabirim türleri ekleme.

Bu bölüm, tüm uygulamalar tarafından garanti edilen ikili uyumluluk için minimum standartları belirtir. Java programlama dili, uyumlu kaynaklardan geldiği bilinmeyen, ancak kaynakları burada açıklanan uyumlu yollarla değiştirilen sınıfların ve arabirimlerin ikili dosyaları karıştırıldığında uyumluluğu garanti eder. Bir uygulamanın sürümleri arasındaki uyumluluğu tartıştığımızı unutmayın. Java SE platformunun sürümleri arasındaki uyumluluk tartışması bu bölümün kapsamı dışındadır.


Bu makale Java sürümünü değiştirdiğimizde neler olabileceğini tartışıyor. OP'nin sorusu, platformu aynı Java sürümü içinde değiştirirsek ne olabileceğiydi. Aksi takdirde iyi bir yakalama.
gaborsch

1
Bulabildiğim kadar yakın. Dilin spesifikasyonu ile JVM'nin spesifikasyonu arasında garip bir boşluk var. Şimdiye kadar, OP'ye 'aynı java derleyicisinin farklı bir platformda çalıştırıldığında aynı bayt kodunu üreteceğinin garantisi yok' şeklinde cevap vermem gerekiyor.
Kelly S. Fransız

1

Java allows you write/compile code on one platform and run on different platform. Bildiğim kadarıyla ; bu ancak farklı platformda oluşturulan sınıf dosyası aynı veya teknik olarak aynı, yani aynı olduğunda mümkün olacaktır.

Düzenle

Teknik olarak aynı demek istediğim yorumla şey bu. Bayt bayt ile karşılaştırırsanız tam olarak aynı olmaları gerekmez.

Bu nedenle, farklı platformlardaki bir sınıfın .class dosyasına göre bayt-bayt eşleşmesi gerekmez.


OP'nin sorusu , sınıf dosyalarının aynı mı yoksa "teknik olarak aynı" mı olduğuydu.
bdesham

Aynı olup olmadıklarıyla ilgileniyorum .
mstrap

ve cevap evet. Demek istediğim, bayt ile bayt karşılaştırırsanız aynı olmayabilirler, bu yüzden teknik olarak aynı kelimeyi kullandım.
rai.skumar

@bdesham aynı olup olmadıklarını bilmek istedi. "Teknik olarak aynı" derken neyi anladığınızdan emin değilim ... Olumsuz oy vermenin nedeni bu mu?
rai.skumar

@ rai.skumar Cevabınız temelde şöyle diyor: "İki derleyici her zaman aynı şekilde davranan çıktı üretir." Elbette bu doğrudur; Java platformunun tüm motivasyonu. OP, gönderilen kodun bayt özdeş bayt olup olmadığını öğrenmek istedi , cevabınızda belirtmediğiniz.
bdesham

1

Soru için:

"Aynı javac yürütülebilir dosyasının farklı bir platformda çalıştırıldığında farklı bayt kodu üreteceği durumlar nelerdir?"

Çapraz Derleme örneği biz Javac seçeneğini kullanabilirsiniz nasıl gösterir: -target versiyon

Bu bayrak, bu komutu çalıştırırken belirlediğimiz Java sürümüyle uyumlu sınıf dosyalarını oluşturur. Dolayısıyla sınıf dosyaları, bu seçeneği kullanarak derleme sırasında sağladığımız özniteliklere bağlı olarak farklılık gösterecektir.


0

Büyük olasılıkla, cevap "evet" dir, ancak kesin bir cevaba sahip olmak için, derleme sırasında bazı anahtarları veya kılavuz oluşturmayı aramak gerekir.

Bunun meydana geldiği durumu hatırlayamıyorum. Örneğin serileştirme amacıyla ID'ye sahip olmak için kodlanmış, yani programcı veya IDE tarafından üretilmiştir.

Not: JNI da önemli olabilir.

PPS buldum javackendisinin java ile yazılmış olduğunu . Bu, farklı platformlarda aynı olduğu anlamına gelir. Dolayısıyla bir sebep olmadan farklı kod üretmeyecektir. Yani, bunu yalnızca yerel aramalarla yapabilir.


Java'nın sizi tüm platform farklılıklarından korumadığını unutmayın . Dizin içeriği listelendiğinde döndürülen dosyaların sırası tanımlanmadığında ve bunun bir derleyici üzerinde bazı etkileri olabilir .
Joachim Sauer

0

İki soru var.

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

Bu teorik bir sorudur ve cevap açıkça, evet olabilir . Diğerlerinin de söylediği gibi, belirtim derleyicinin bayt için bayt özdeş sınıf dosyaları üretmesini gerektirmez.

Şu anda var olan her derleyici her koşulda (farklı donanım, vb.) Aynı bayt kodunu üretmiş olsa bile, yarın verilecek cevap farklı olabilir. Java 7 Güncelleme 11'den Java 7 Güncelleme 15'e giderseniz, javac'ı veya işletim sisteminizi hiçbir zaman güncellemeyi planlamıyorsanız, o sürümün davranışını kendi özel koşullarınızda test edebilirsiniz, ancak sonuçlar farklı olabilir.

What are the circumstances where the same javac executable, when run on a different platform, will produce different bytecode?

Bu bilinemez.

Konfigürasyon yönetiminin soruyu sorma nedeniniz olup olmadığını bilmiyorum, ancak bu, ilgilenmek için anlaşılabilir bir neden. Bayt kodlarını karşılaştırmak meşru bir BT kontrolüdür, ancak yalnızca sınıf dosyalarının değişip değişmediğini belirlemek için, kaynak dosyaların değişip değişmediğini belirlemek için değil.


0

Ben başka bir şekilde söylerdim.

İlk olarak, sorunun determinist olmakla ilgili olmadığını düşünüyorum:

Elbette deterministiktir: Bilgisayar biliminde rastgeleliğe ulaşmak zordur ve bir derleyicinin onu herhangi bir nedenle burada tanıtması için hiçbir neden yoktur.

İkinci olarak, "aynı kaynak kodu dosyası için bayt kodu dosyaları ne kadar benzer?" Şeklinde yeniden biçimlendirirseniz, Hayır , benzer olacaklarına güvenemezsiniz. .

Bundan emin olmanın iyi bir yolu, git aşamanızda .class'ı (veya benim durumumda .pyc'i) bırakmaktır. Ekibinizdeki farklı bilgisayarlar arasında, .py dosyasına herhangi bir değişiklik getirilmediğinde (ve yine de .pyc yeniden derlendiğinde) git bildirimlerinin .pyc dosyaları arasında değiştiğini fark edeceksiniz.

En azından ben öyle gözlemledim. Bu yüzden .gitignore dosyanıza * .pyc ve * .class ekleyin!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.