Apakah ada overhead saat kita mentransmisikan objek dari satu jenis ke jenis lainnya? Atau kompiler hanya menyelesaikan semuanya dan tidak ada biaya pada saat dijalankan?
Apakah ini hal yang umum, atau ada kasus yang berbeda?
Misalnya, kita memiliki larik Object [], di mana setiap elemen mungkin memiliki tipe yang berbeda. Tapi kita selalu tahu pasti bahwa, katakanlah, elemen 0 adalah Double, elemen 1 adalah String. (Saya tahu ini adalah desain yang salah, tetapi anggap saja saya harus melakukan ini.)
Apakah informasi tipe Java masih disimpan pada saat dijalankan? Atau semuanya dilupakan setelah kompilasi, dan jika kita melakukan elemen (Double) [0], kita hanya akan mengikuti pointer dan menafsirkan 8 byte tersebut sebagai double, apapun itu?
Saya sangat tidak jelas tentang bagaimana tipe dilakukan di Java. Jika Anda memiliki rekomendasi untuk buku atau artikel, terima kasih juga.
Jawaban:
Ada 2 jenis casting:
Transmisi implisit , saat Anda mentransmisikan dari satu tipe ke tipe yang lebih luas, yang dilakukan secara otomatis dan tidak ada overhead:
Transmisi eksplisit , saat Anda beralih dari tipe yang lebih luas ke tipe yang lebih sempit. Untuk kasus ini, Anda harus secara eksplisit menggunakan transmisi seperti itu:
Dalam kasus kedua ini, ada overhead dalam waktu proses, karena kedua jenis tersebut harus diperiksa dan jika transmisi tidak memungkinkan, JVM harus menampilkan ClassCastException.
Diambil dari JavaWorld: Biaya casting
sumber
((String)o).someMethodOfCastedClass()
Untuk implementasi Java yang wajar:
Setiap objek memiliki header yang berisi, antara lain, penunjuk ke jenis runtime (misalnya
Double
atauString
, tetapi tidak akan pernahCharSequence
atauAbstractList
). Dengan asumsi kompiler runtime (umumnya HotSpot dalam kasus Sun) tidak dapat menentukan tipe secara statis, beberapa pemeriksaan perlu dilakukan oleh kode mesin yang dihasilkan.Pertama, penunjuk ke jenis runtime perlu dibaca. Ini diperlukan untuk memanggil metode virtual dalam situasi yang sama.
Untuk mentransmisikan ke tipe kelas, diketahui dengan tepat berapa banyak kelas super yang ada sampai Anda menekannya
java.lang.Object
, sehingga tipe tersebut dapat dibaca pada offset konstan dari penunjuk tipe (sebenarnya delapan pertama di HotSpot). Sekali lagi ini analog dengan membaca penunjuk metode untuk metode virtual.Kemudian nilai baca hanya membutuhkan perbandingan dengan jenis cast statis yang diharapkan. Bergantung pada arsitektur set instruksi, instruksi lain perlu bercabang (atau kesalahan) pada cabang yang salah. ISA seperti ARM 32-bit memiliki instruksi bersyarat dan mungkin dapat membuat jalur sedih melewati jalur bahagia.
Antarmuka lebih sulit karena beberapa pewarisan antarmuka. Biasanya, dua cast terakhir ke antarmuka di-cache dalam tipe runtime. DI masa-masa awal (lebih dari satu dekade yang lalu), antarmuka agak lambat, tetapi itu tidak lagi relevan.
Mudah-mudahan Anda dapat melihat bahwa hal semacam ini sebagian besar tidak relevan dengan kinerja. Kode sumber Anda lebih penting. Dalam hal kinerja, hit terbesar dalam skenario Anda adalah kemungkinan cache luput dari pengejaran penunjuk objek di semua tempat (informasi jenis tentu saja akan umum).
sumber
Kompilator tidak mencatat tipe elemen individual dari sebuah array. Ini hanya memeriksa bahwa tipe dari setiap ekspresi elemen dapat ditetapkan ke tipe elemen array.
Beberapa informasi disimpan pada waktu proses, tetapi bukan tipe statis dari masing-masing elemen. Anda dapat mengetahui ini dari melihat format file kelas.
Secara teoritis mungkin bahwa compiler JIT dapat menggunakan "escape analysis" untuk menghilangkan pemeriksaan jenis yang tidak perlu di beberapa tugas. Namun, melakukan ini hingga tingkat yang Anda sarankan akan berada di luar batas pengoptimalan realistis. Imbalan dari menganalisis jenis elemen individu akan terlalu kecil.
Selain itu, orang tidak boleh menulis kode aplikasi seperti itu.
sumber
(float) Math.toDegrees(theta)
Apakah akan ada biaya tambahan yang signifikan di sini juga?Instruksi kode byte untuk melakukan casting saat runtime dipanggil
checkcast
. Anda dapat membongkar kode Java menggunakanjavap
untuk melihat instruksi apa yang dihasilkan.Untuk array, Java menyimpan informasi tipe pada waktu proses. Sebagian besar waktu, kompilator akan menangkap kesalahan tipe untuk Anda, tetapi ada kasus di mana Anda akan mengalami
ArrayStoreException
ketika mencoba menyimpan objek dalam array, tetapi jenisnya tidak cocok (dan kompilator tidak menangkapnya) . The bahasa Jawa spesifikasi memberikan contoh berikut:Point[] pa = cpa
valid karenaColoredPoint
merupakan subclass dari Point, tetapipa[0] = new Point()
tidak valid.Ini berlawanan dengan tipe generik, di mana tidak ada informasi tipe yang disimpan pada waktu proses. Kompilator memasukkan
checkcast
instruksi jika diperlukan.Perbedaan dalam pengetikan untuk tipe dan larik generik ini membuatnya sering tidak cocok untuk mencampur larik dan tipe generik.
sumber
Secara teori, ada overhead yang diperkenalkan. Namun, JVM modern itu cerdas. Setiap implementasi berbeda, tetapi bukan tidak masuk akal untuk mengasumsikan bahwa mungkin ada implementasi yang dioptimalkan JIT tanpa pengecekan ketika itu dapat menjamin bahwa tidak akan pernah ada konflik. Adapun JVM tertentu yang menawarkan ini, saya tidak bisa memberi tahu Anda. Saya harus mengakui bahwa saya sendiri ingin mengetahui secara spesifik pengoptimalan JIT, tetapi ini adalah hal yang perlu dikhawatirkan oleh teknisi JVM.
Moral dari cerita ini adalah menulis kode yang dapat dimengerti terlebih dahulu. Jika Anda mengalami pelambatan, buat profil dan identifikasi masalah Anda. Kemungkinannya bagus karena itu bukan karena casting. Jangan pernah mengorbankan kode yang bersih dan aman dalam upaya untuk mengoptimalkannya SAMPAI ANDA PERLU.
sumber