Menganalisis Penggunaan Memori: Java vs C ++ Dapat Diabaikan?

9

Bagaimana penggunaan memori objek integer yang ditulis dalam Java membandingkan \ kontras dengan penggunaan memori objek integer yang ditulis dalam C ++? Apakah perbedaannya dapat diabaikan? Tidak ada perbedaan? Perbedaan besar? Saya menduga itu sama karena int adalah int terlepas dari bahasanya (?)

Alasan mengapa saya bertanya ini adalah karena saya membaca tentang pentingnya mengetahui kapan persyaratan memori suatu program akan mencegah programmer dari memecahkan masalah yang diberikan.

Yang membuat saya terpesona adalah jumlah memori yang diperlukan untuk membuat objek Java tunggal. Ambil contoh, objek integer. Perbaiki saya jika saya salah tetapi objek integer Java membutuhkan 24 byte memori:

  • 4 byte untuk variabel inst instance
  • 16 byte overhead (referensi ke kelas objek, info pengumpulan sampah & info sinkronisasi)
  • 4 byte bantalan

Sebagai contoh lain, array Java (yang diimplementasikan sebagai objek) membutuhkan 48 + byte:

  • 24 byte informasi header
  • 16 byte overhead objek
  • Panjangnya 4 byte
  • 4 byte untuk bantalan
  • ditambah memori yang dibutuhkan untuk menyimpan nilai

Bagaimana penggunaan memori ini dibandingkan dengan kode yang sama yang ditulis dalam C ++?

Dulu saya tidak menyadari tentang penggunaan memori program C ++ dan Java yang saya tulis, tetapi sekarang saya mulai belajar tentang algoritma, saya memiliki apresiasi yang lebih besar untuk sumber daya komputer.

Anthony
sumber
6
Apa itu "objek integer" di C ++? int? Jika demikian, Anda harus membandingkannya dengan intdi Java, tidak Integer- selama int C ++ Anda 32bits.
Mat
+1 Jika saya membuat kelas c ++ yang hanya memiliki satu variabel int, maka instantiated
Anthony
3
int bukan int - itu tergantung platform
1
C ++ dengan hanya satu anggota int biasanya tidak memiliki overhead. Ini akan menggunakan ruang persis sebanyak yang digunakan platform untuk menyimpan int (biasanya 4 byte pada platform PC saat ini).
Dirk Holsopple
+1 untuk seorang programmer Java yang ingin tahu tentang memori. Lebih dari segalanya, kesadaran akan memori adalah faktor terpenting yang menentukan kinerja arsitektur modern.
imallett

Jawaban:

15

Itu tergantung pada platform dan implementasinya.

C ++ menjamin bahwa ukurannya charpersis satu byte dan lebar minimal 8 bit. Kemudian, ukuran a short intsetidaknya 16 bit dan tidak lebih kecil dari char. Ukuran suatu intsetidaknya sebesar ukuran short int. Ukuran long intminimal 32 bit dan tidak lebih kecil dari int.

sizeof(char) == 1; sizeof(long int) >= sizeof(int) >= sizeof(short int) >= sizeof(bool) >= sizeof(char).

Model memori sebenarnya dari C ++ sangat ringkas dan dapat diprediksi . Misalnya tidak ada metadata pada objek, array atau pointer. Struktur dan kelas berdekatan seperti halnya array, tetapi bantalan dapat ditempatkan jika perlu dan diperlukan.

Sejujurnya, perbandingan semacam itu paling konyol karena penggunaan memori Java lebih tergantung pada implementasi Java daripada pada kode yang dijalankannya.

zxcdw
sumber
1
Benar, tetapi demi perbandingan, saya katakan kita harus mengasumsikan tipe integer berukuran setara (bahkan jika mereka hanya tersedia dengan nama yang berbeda). Bagaimanapun, ukuran yang berbeda berarti semantik yang berbeda, dan pada banyak (tidak semua) platform umum ukurannya identik, atau bilangan bulat dengan ukuran yang sama tersedia dengan nama yang berbeda.
Catatan untuk OP: Anda mungkin lebih baik memilih sendiri ukuran bilangan bulat - jika Anda ingin int 32-bit di C ++, Anda dapat menggunakan int32_t.
K.Steff
9

Sebagian besar jawaban tampaknya mengabaikan beberapa poin yang cukup besar.

Pertama, di banyak Jawa, Anda hampir tidak pernah melihat mentah int- hampir semua penggunaan Integer, jadi fakta bahwa sebuah intbisa (tentang) ukuran yang sama dengan intdi C atau C ++ hampir tidak relevan, kecuali untuk itu ( menurut pengalaman saya, kecil) persentase kode yang hanya menggunakan intbukan Integer.

Kedua, ukuran objek individu hampir tidak ada hubungannya dengan jejak memori suatu program secara keseluruhan. Di Jawa, jejak memori program terutama berkaitan dengan bagaimana pengumpul sampah disetel. Dalam kebanyakan kasus, GC disetel untuk memaksimalkan kecepatan, yang (sebagian besar) berarti menjalankan GC sesering mungkin.

Saya tidak memiliki tautan berguna saat ini, tetapi ada beberapa tes yang menunjukkan bahwa Java dapat berjalan dengan kecepatan yang sama dengan C, tetapi untuk melakukannya Anda harus menjalankan GC cukup jarang yang menggunakan sekitar 7 kali lebih banyak Penyimpanan. Itu bukan karena objek individual 7 kali lebih besar, tetapi karena GC bisa menjadi sangat mahal jika Anda melakukannya terlalu sering. Lebih buruk lagi, GC hanya dapat melepaskan memori ketika dapat "membuktikan" bahwa tidak ada lagi cara untuk mengakses suatu objek, bukan hanya ketika Anda tahu Anda selesai menggunakannya. Ini berarti bahwa bahkan jika Anda menjalankan GC lebih sering untuk meminimalkan penggunaan memori, Anda mungkin masih dapat merencanakan program tipikal memiliki jejak memori yang lebih besar. Dalam kasus seperti ini, Anda dapat mengurangi faktor menjadi 2 atau 3 alih-alih 7. Bahkan jika Anda secara drastis berlebihan, namun, jangan1 .

Tergantung pada situasinya, ada faktor lain yang mungkin atau mungkin tidak signifikan: memori yang ditempati oleh JVM itu sendiri. Ini kurang lebih tetap, sehingga sebagai persentase itu mungkin besar jika aplikasi tidak membutuhkan banyak penyimpanan sendiri, atau mungkin sangat kecil jika aplikasi perlu menyimpan banyak. Setidaknya di komputer saya, bahkan aplikasi Java yang paling sepele tampaknya menempati sesuatu seperti 20-25 megabyte (bisa lebih dari 1000x untuk program trivlal, atau hampir tak terukur kecil untuk yang besar).


1 Itu bukan untuk mengatakan bahwa tidak ada yang mungkin bisa menulis Java dengan footprint yang mendekati apa yang Anda dapatkan dengan C ++. Ini hanya untuk mengatakan bahwa hanya memiliki jumlah / ukuran objek yang sama, dan menjalankan GC sangat sering tidak akan membawa Anda ke sana sebagai aturan.

Jerry Coffin
sumber
7
Mengenai titik pertama Anda: Saya bukan orang Jawa, tapi Java API pernah saya lihat tidak pernah digunakan Integer(mengapa mereka?) Bukan int. Hanya koleksi generik yang tidak memiliki pilihan selain menggunakan Integerkarena penghapusan tipe, tetapi jika Anda peduli, Anda bisa menggantinya dengan implementasi khusus intatau tipe primitif apa pun yang Anda butuhkan. Dan kemudian ada tinju sementara untuk melewati kode pembungkus generik (mis. Semua yang membutuhkan Object[]). Selain itu, apakah Anda memiliki sumber untuk overhead ruang GC? Saya tidak benar-benar meragukannya, saya hanya ingin tahu.
9

Saya harap Anda menyadari bahwa semua ini sangat ditentukan oleh implementasi, baik untuk Java dan C ++. Yang sedang berkata, model objek Java membutuhkan sedikit ruang.

Objek C ++ tidak (umumnya) membutuhkan penyimpanan apa pun kecuali apa yang dibutuhkan anggota. Perhatikan bahwa (tidak seperti Java, di mana semua yang didefinisikan pengguna adalah tipe referensi), kode klien dapat menggunakan objek baik sebagai tipe nilai atau sebagai tipe referensi, yaitu suatu objek dapat menyimpan pointer / referensi ke objek lain, atau menyimpan objek secara langsung tanpa tipuan. Satu pointer tambahan per objek diperlukan jika ada virtualmetode, tetapi beberapa kelas yang berguna dirancang untuk bergaul tanpa polimorfisme dan tidak memerlukan ini. Tidak ada metadata GC dan tidak ada kunci per objek. Dengan demikian class IntWrapper { int x; public: IntWrapper(int); ... };objek tidak membutuhkan lebih banyak ruang daripada ints polos , dan dapat ditempatkan langsung (yaitu tanpa tipuan) dalam koleksi dan objek lainnya.

Array rumit hanya karena tidak ada yang dibuat sebelumnya, setara dengan Java Array di C ++. Anda dapat dengan mudah mengalokasikan banyak objek dengan new[](sama sekali tidak ada overhead / metadata) tetapi tidak ada bidang panjang - implementasinya mungkin menyimpan satu tetapi Anda tidak dapat mengaksesnya. std::vectoradalah array dinamis dan karenanya memiliki overhead tambahan dan antarmuka yang lebih besar. std::arraydan array gaya-C (int arr[N];), perlu konstanta waktu kompilasi. Secara teori, seharusnya hanya penyimpanan objek ditambah bilangan bulat tunggal untuk panjangnya - tetapi karena Anda bisa mendapatkan pengubahan ukuran dinamis dan antarmuka berfitur lengkap dengan ruang ekstra yang sangat sedikit, Anda hanya perlu melakukannya dalam praktik. Perhatikan bahwa semua ini, serta semua koleksi lainnya, default untuk menyimpan objek berdasarkan nilai, sehingga menghemat tipuan dan ruang untuk referensi, dan meningkatkan perilaku cache. Anda harus menyimpan pointer secara eksplisit (yang pintar, tolong) untuk mendapatkan tipuan.

Perbandingan di atas tidak sepenuhnya adil, karena beberapa penghematan ini diberikan dengan tidak menyertakan fitur yang disertakan Java, dan yang setara dengan C ++ sering kurang dioptimalkan dibandingkan dengan yang setara dengan Java (*). Cara umum untuk mengimplementasikan virtualC ++ memaksakan overhead yang sama persis dengan cara umum untuk mengimplementasikan virtualdi Jawa. Untuk mendapatkan kunci, Anda memerlukan objek mutex fitur lengkap, yang kemungkinan besar lebih besar dari beberapa bit. Untuk mendapatkan penghitungan referensi ( tidaksetara dengan GC, dan tidak boleh digunakan seperti itu, tetapi terkadang bermanfaat), Anda memerlukan penunjuk cerdas yang menambahkan bidang jumlah referensi. Kecuali objek dibangun dengan hati-hati, jumlah referensi, objek pointer pintar, dan objek referensi berada di lokasi yang benar-benar terpisah, dan bahkan ketika Anda membuatnya dengan benar, pointer yang dibagikan mungkin (harus?) Masih berupa dua pointer, bukan satu. Kemudian lagi, gaya C ++ yang baik tidak menggunakan fitur-fitur ini cukup untuk masalah - dalam prakteknya, objek perpustakaan C ++ yang ditulis dengan baik menggunakan lebih sedikit. Itu tidak selalu berarti lebih sedikit penggunaan memori secara keseluruhan, tetapi itu berarti C ++ memiliki awal yang baik dalam hal ini.

(*) Misalnya, Anda bisa mendapatkan panggilan virtual, kode hash identitas dan penguncian dengan hanya satu kata untuk beberapa objek (dan dua kata untuk banyak objek lainnya) dengan menggabungkan informasi jenis dengan berbagai flag, dan menghapus bit kunci untuk objek yang tidak mungkin membutuhkan kunci. Lihat Implementasi Java Object Model (PDF) yang Efisien Ruang dan Waktu oleh David F. Bacon, Stephen J. Fink, dan David Grove untuk penjelasan terperinci tentang ini dan optimisasi lainnya.


sumber
3

Sebuah plain int, di java, membutuhkan ruang yang persis sama dengan intdi C ++, asalkan kedua implementasi menggunakan ukuran integer yang sama dan penyelarasan memori.

'Objek' int ( bilangan bulat kotak , yaitu turunan dari kelas Integer), membawa semua overhead turunan kelas di Jawa, jadi ini secara signifikan lebih besar daripada intdi dalam C ++. Namun, jika Anda harus melengkapi objek dalam C ++ dengan fitur yang sama dengan objek Java yang ada di luar kotak (polimorfisme, tinju, pengumpulan sampah, RTTI), maka Anda mungkin akan berakhir dengan objek yang sama ukuran.

Dan kemudian ada pertimbangan optimasi; karena model eksekusi dan paradigma pemrograman berbeda, kecil kemungkinannya bahwa masalah non-sepele akan dipecahkan sama dalam kedua bahasa, jadi membandingkan ukuran penyimpanan pada level ini tidak masuk akal.

Ya, objek Java membawa lebih banyak overhead secara default daripada kelas C ++, tetapi mereka datang dengan lebih banyak fitur, dan ini mengarah pada gaya pemrograman yang berbeda - seorang programmer yang baik dapat memanfaatkan sisi negatif dari kedua bahasa.

tammmer
sumber
+1 Lebih banyak overhead tetapi lebih banyak fitur di Jawa, saya mengerti sekarang, terima kasih
Anthony