Java dökümü ek yük getirir mi? Neden?


105

Bir türden nesneleri diğerine attığımızda herhangi bir ek yük var mı? Veya derleyici her şeyi çözer ve çalışma zamanında herhangi bir maliyet olmaz mı?

Bu genel bir şey mi yoksa farklı durumlar var mı?

Örneğin, her öğenin farklı bir türe sahip olabileceği bir Object [] dizimiz olduğunu varsayalım. Ancak, örneğin 0 öğesinin bir Double, 1. öğenin bir Dize olduğundan her zaman emin oluruz. (Bunun yanlış bir tasarım olduğunu biliyorum, ancak bunu yapmam gerektiğini varsayalım.)

Java'nın tür bilgileri çalışma zamanında hala saklanıyor mu? Ya da derlemeden sonra her şey unutulur ve eğer (Çift) elemanlar [0] yaparsak, sadece işaretçiyi takip edeceğiz ve bu 8 baytı bir çift olarak yorumlayacağız, her ne ise?

Java'da türlerin nasıl yapıldığı konusunda pek emin değilim. Kitaplar veya makaleler hakkında herhangi bir öneriniz varsa, o zaman da teşekkürler.


Örnek ve dökümün performansı oldukça iyi. Java7'de soruna farklı yaklaşımlarla ilgili bazı zamanlamaları burada yayınladım: stackoverflow.com/questions/16320014/…
Wheezil

Bu diğer sorunun çok iyi cevapları var stackoverflow.com/questions/16741323/…
user454322

Yanıtlar:


78

2 tür döküm vardır:

Örtülü çevrim, bir türden daha geniş bir türe çevirdiğinizde, bu otomatik olarak yapılır ve ek yük yoktur:

String s = "Cast";
Object o = s; // implicit casting

Daha geniş bir türden daha dar bir türe geçtiğinizde açık döküm. Bu durumda, çevirimi açıkça şu şekilde kullanmanız gerekir:

Object o = someObject;
String s = (String) o; // explicit casting

Bu ikinci durumda, çalışma zamanında ek yük vardır, çünkü iki türün kontrol edilmesi gerekir ve dökümün uygun olmaması durumunda, JVM bir ClassCastException oluşturmalıdır.

Alındığı JavaWorld: döküm maliyeti

Döküm , türler arasında - özellikle burada ilgilendiğimiz döküm işlemi türü için referans türleri arasında dönüştürme yapmak için kullanılır.

Yukarı yayın işlemleri (Java Dil Spesifikasyonunda genişletme dönüştürmeleri olarak da adlandırılır) bir alt sınıf referansını bir üst sınıf referansına dönüştürür. Bu döküm işlemi normalde otomatiktir, çünkü her zaman güvenlidir ve doğrudan derleyici tarafından uygulanabilir.

Downcast işlemleri (Java Dil Spesifikasyonunda daraltma dönüştürmeleri olarak da adlandırılır) bir üst sınıf referansını bir alt sınıf referansına dönüştürür. Java, dökümün geçerli olduğundan emin olmak için çalışma zamanında kontrol edilmesini gerektirdiğinden, bu döküm işlemi yürütme ek yükü oluşturur. Başvurulan nesne, çevrim için hedef türünün veya bu türün bir alt sınıfının bir örneği değilse, denenen dönüştürmeye izin verilmez ve bir java.lang.ClassCastException oluşturması gerekir.


102
O JavaWorld makalesi 10+ yıllık, bu yüzden en iyi tuzunuzdan çok büyük bir tanesiyle performans hakkında yaptığı açıklamaları alıyorum.
skaffman

1
@skaffman, Aslında yaptığı herhangi bir açıklamayı (performansına bakılmaksızın) bir tuz tanesi ile alırdım.
Pacerier

Eğer çevrilen nesneyi referansa atamazsam ve sadece onun üzerinde metodu çağırırsam aynı durum olur mu? like((String)o).someMethodOfCastedClass()
Parth Vishvajit

4
Şimdi makale neredeyse 20 yaşında. Ve cevaplar da çok eski. Bu sorunun modern bir cevaba ihtiyacı var.
Raslanove

İlkel tipler nasıl olur? Demek istediğim, örneğin - int'ten kısaya çevrim benzer ek yüke neden oluyor mu?
luke1985

44

Makul bir Java uygulaması için:

Her nesnenin, diğer şeylerin yanı sıra, çalışma zamanı türüne bir işaretçi içeren bir başlığı vardır (örneğin Doubleveya String, ancak asla CharSequenceveya olamaz AbstractList). Çalışma zamanı derleyicisinin (genellikle Sun'ın durumunda HotSpot) türü statik olarak belirleyemediğini varsayarsak, üretilen makine kodu tarafından bazı kontrollerin yapılması gerekir.

İlk olarak, çalışma zamanı türüne ilişkin işaretçinin okunması gerekir. Bu, benzer bir durumda sanal bir yöntemi çağırmak için gereklidir.

Bir sınıf türüne çevrim için, siz vurana kadar tam olarak kaç tane üst sınıf olduğu bilinir java.lang.Object, bu nedenle tür, tür işaretçisinden (aslında HotSpot'taki ilk sekiz) sabit bir uzaklıktan okunabilir. Yine bu, sanal bir yöntem için bir yöntem işaretçisini okumaya benzer.

O zaman okunan değerin beklenen statik türle karşılaştırılması gerekir. Komut kümesi mimarisine bağlı olarak, başka bir komutun yanlış bir dalda dallanması (veya hata yapması) gerekecektir. 32 bit ARM gibi ISA'lar koşullu talimatlara sahiptir ve üzücü yolun mutlu yoldan geçmesini sağlayabilir.

Arayüzlerin çoklu kalıtımı nedeniyle arayüzler daha zordur. Genellikle arabirimlere yapılan son iki yayın, çalışma zamanı türünde önbelleğe alınır. İlk günlerde (on yıldan fazla bir süre önce), arayüzler biraz yavaştı, ancak bu artık geçerli değil.

Umarım bu tür şeylerin performansla büyük ölçüde alakasız olduğunu görebilirsiniz. Kaynak kodunuz daha önemlidir. Performans açısından, senaryonuzdaki en büyük darbe, her yerde nesne işaretçilerinin peşinden koşan önbellek kayıpları olabilir (tür bilgileri elbette yaygın olacaktır).


1
ilginç - bu, arayüz dışı sınıflar için Superclass sc = (Superclass) subclass yazarsam anlamına gelir; (jit yani: yükleme süresi) derleyicisinin, Superclass ve Subclass'ın her birinin "Class" üstbilgilerindeki Object'ten uzaklığı "statik olarak" koyacağını ve daha sonra basit bir add + Compare aracılığıyla sorunları çözebileceğini? - bu güzel ve hızlı :) Arayüzler için küçük bir hashtable veya btree'den daha kötü olmadığını varsayabilirim?
peterk

@peterk Sınıflar arasında çevrim yapmak için, hem nesne adresi hem de "vtbl" (yöntem işaretçileri tablosu, artı sınıf hiyerarşisi tablosu, arayüz önbelleği vb.) değişmez. Dolayısıyla [class] castı türü kontrol eder ve uygunsa başka hiçbir şey olmamalıdır.
Tom Hawtin - tackline

8

Örneğin, her öğenin farklı bir türe sahip olabileceği bir Object [] dizimiz olduğunu varsayalım. Ancak, örneğin 0 öğesinin bir Double, 1. öğenin bir Dize olduğundan her zaman emin oluruz. (Bunun yanlış bir tasarım olduğunu biliyorum, ancak bunu yapmam gerektiğini varsayalım.)

Derleyici, bir dizinin tek tek öğelerinin türlerini not etmez. Basitçe, her öğe ifadesinin türünün dizi öğesi türüne atanabilir olup olmadığını kontrol eder.

Java'nın tür bilgileri çalışma zamanında hala saklanıyor mu? Ya da derlemeden sonra her şey unutulur ve eğer (Çift) elemanlar [0] yaparsak, sadece işaretçiyi takip edeceğiz ve bu 8 baytı bir çift olarak yorumlayacağız, her ne ise?

Bazı bilgiler çalışma zamanında tutulur, ancak tek tek öğelerin statik türleri değildir. Bunu sınıf dosya formatına bakarak anlayabilirsiniz.

JIT derleyicisinin bazı atamalarda gereksiz tür kontrollerini ortadan kaldırmak için "kaçış analizi" kullanması teorik olarak mümkündür. Ancak, bunu önerdiğiniz ölçüde yapmak, gerçekçi optimizasyon sınırlarının ötesinde olacaktır. Bireysel unsurların türlerini analiz etmenin getirisi çok küçük olacaktır.

Ayrıca insanlar zaten böyle bir uygulama kodu yazmamalıdır.


1
İlkellerden ne haber? (float) Math.toDegrees(theta)Burada da önemli bir ek yük olacak mı?
SD

2
Bazı ilkel yayınlar için bir ek yük var. Önemli olup olmadığı bağlama bağlıdır.
Stephen C

6

Çalışma zamanında çevrim gerçekleştirmek için bayt kodu talimatı çağrılır checkcast. javapHangi talimatların oluşturulduğunu görmek için Java kodunu parçalarına ayırabilirsiniz .

Diziler için Java, tür bilgilerini çalışma zamanında tutar. Çoğu zaman, derleyici sizin için tür hatalarını yakalayacaktır, ancak ArrayStoreExceptionbir nesneyi bir dizide saklamaya çalışırken karşılaşacağınız durumlar vardır , ancak tür eşleşmez (ve derleyici onu yakalamadı) . Java dili Spec aşağıdaki örneği veriyor:

class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
    public static void main(String[] args) {
        ColoredPoint[] cpa = new ColoredPoint[10];
        Point[] pa = cpa;
        System.out.println(pa[1] == null);
        try {
            pa[0] = new Point();
        } catch (ArrayStoreException e) {
            System.out.println(e);
        }
    }
}

Point[] pa = cpaberi geçerli olan ColoredPointPoint bir alt sınıf, ancak pa[0] = new Point()geçerli değil.

Bu, çalışma zamanında hiçbir tür bilgisinin tutulmadığı genel türlerin tersidir. Derleyici checkcast, gerektiğinde talimatlar ekler .

Genel türler ve diziler için yazmadaki bu fark, dizileri ve genel türleri karıştırmayı genellikle uygunsuz hale getirir.


2

Teorik olarak, ek yükler var. Ancak, modern JVM'ler akıllıdır. Her uygulama farklıdır, ancak JIT'in hiçbir zaman bir çatışma olmayacağını garanti edebildiği zaman, döküm kontrollerini optimize ettiği bir uygulamanın var olabileceğini varsaymak mantıksız değildir. Hangi belirli JVM'lerin bunu sunduğuna gelince, size söyleyemedim. JIT optimizasyonunun özelliklerini kendim bilmek istediğimi itiraf etmeliyim, ancak bunlar JVM mühendislerinin endişelenmesi için.

Hikayenin ahlaki, önce anlaşılır bir kod yazmaktır. Yavaşlama yaşıyorsanız, sorununuzun profilini çıkarın ve tanımlayın. Oyuncu seçimi nedeniyle olmayacak ihtimaller iyidir. İHTİYACINIZ OLANA KADAR, onu optimize etmek için asla temiz ve güvenli kodu feda etmeyin.

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.