Haruskah kita menghindari pembuatan objek di Jawa?

243

Saya diberitahu oleh seorang rekan bahwa dalam pembuatan objek Java adalah operasi paling mahal yang bisa Anda lakukan. Jadi saya hanya bisa menyimpulkan untuk membuat objek sesedikit mungkin.

Ini agaknya mengalahkan tujuan pemrograman berorientasi objek. Jika kita tidak membuat objek maka kita hanya menulis satu gaya C kelas panjang, untuk optimasi?

Slamice
sumber
39
"Pembuatan objek Java adalah operasi termahal yang dapat Anda lakukan" . Pikiran berbagi sumber klaim itu?
Songo
92
Dan - operasi paling mahal dibandingkan dengan apa? Dibandingkan dengan menghitung pi hingga 900 digit atau dibandingkan dengan meningkatkan int?
jasonk
4
Mungkinkah mereka telah berbicara tentang bagian tertentu dari penciptaan objek tertentu? Saya sedang berpikir bagaimana Apple menggunakan antrian untuk tableViewCells. Mungkin kolega Anda menyarankan untuk membuat beberapa objek dan menggunakannya kembali karena beberapa overhead yang terkait dengan objek-objek tertentu? Hanya mencoba mencari tahu mengapa mereka membuat klaim seperti itu.
Tyler DeWitt
3
Ini adalah salah satu hal paling lucu yang pernah saya dengar tentang pemrograman.)
Mert Akcakaya
22
Aritmatik juga cukup mahal; kita harus menghindari perhitungan, oh, dan memulai JVM benar-benar mahal jadi kita tidak boleh memulai JVM apa pun :-).
James Anderson

Jawaban:

478

Rekan Anda tidak tahu apa yang mereka bicarakan.

Operasi Anda yang paling mahal adalah mendengarkan mereka . Mereka menyia-nyiakan waktu Anda untuk mengarahkan Anda ke informasi yang lebih dari satu dekade kedaluwarsa (sejak tanggal asli jawaban ini diposting) serta Anda harus menghabiskan waktu memposting di sini dan meneliti Internet untuk kebenaran.

Semoga mereka hanya memuntahkan sesuatu yang mereka dengar atau baca dari lebih dari satu dekade yang lalu dan tidak tahu yang lebih baik. Saya akan mengambil apa pun yang mereka katakan sebagai tersangka juga, ini harus menjadi kesalahan yang diketahui oleh siapa pun yang tetap up to date.

Semuanya adalah Obyek (kecuali primitives)

Segala sesuatu selain primitif ( int, long, double, dll) adalah Objek di Jawa. Tidak ada cara untuk menghindari pembuatan Objek di Jawa.

Penciptaan objek di Jawa karena strategi alokasi memorinya lebih cepat daripada C ++ dalam banyak kasus dan untuk semua tujuan praktis dibandingkan dengan segala hal lain di JVM dapat dianggap "bebas" .

Pada awal tahun 2000-an awal implementasi JVM memang memiliki beberapa overhead kinerja dalam alokasi Obyek yang sebenarnya. Ini tidak terjadi sejak setidaknya 2005.

Jika Anda menyetel -Xmsuntuk mendukung semua memori yang Anda butuhkan agar aplikasi Anda dapat berjalan dengan benar, GC mungkin tidak pernah harus menjalankan dan menyapu sebagian besar sampah dalam implementasi GC modern, program yang berumur pendek mungkin tidak pernah GC sama sekali.

Itu tidak mencoba dan memaksimalkan ruang kosong, yang merupakan herring merah, itu memaksimalkan kinerja runtime. Jika itu berarti JVM Heap hampir 100% dialokasikan sepanjang waktu, maka jadilah itu. Memori tumpukan JVM gratis tidak memberi Anda apa-apa hanya duduk di sana saja.

Ada kesalahpahaman bahwa GC akan membebaskan memori kembali ke seluruh sistem dengan cara yang bermanfaat, ini sepenuhnya salah!

Tumpukan JVM tidak tumbuh dan menyusut sehingga sisa sistem secara positif dipengaruhi oleh memori bebas di Tumpukan JVM . -Xmsmengalokasikan SEMUA dari apa yang ditentukan pada startup dan heuristiknya adalah untuk tidak pernah benar-benar melepaskan memori itu kembali ke OS untuk dibagikan dengan proses OS lainnya sampai instance JVM berhenti sepenuhnya. -Xms=1GB -Xmx=1GBmengalokasikan 1GB RAM terlepas dari berapa banyak objek yang sebenarnya dibuat pada waktu tertentu. Ada beberapa pengaturan yang memungkinkan persentase memori tumpukan untuk dilepaskan, tetapi untuk semua tujuan praktis JVM tidak pernah mampu melepaskan cukup memori ini agar hal ini dapat terjadijadi tidak ada proses lain yang bisa mendapatkan kembali memori ini, sehingga sisa dari sistem tidak mendapat manfaat dari JVM Heap menjadi gratis juga. RFE untuk ini "diterima" 29-NOV-2006, tetapi belum pernah dilakukan. Ini adalah perilaku yang tidak dianggap masalah oleh siapa pun yang berwenang.

Ada kesalahpahaman bahwa membuat banyak objek kecil yang berumur pendek menyebabkan JVM berhenti untuk jangka waktu yang lama, ini sekarang salah juga

Algoritma GC saat ini sebenarnya dioptimalkan untuk membuat banyak banyak objek kecil yang berumur pendek, yang pada dasarnya adalah 99% heuristik untuk objek Java di setiap program. Upaya di Object Pooling benar-benar akan membuat kinerja JVM lebih buruk dalam banyak kasus.

Satu-satunya Objek yang perlu dikumpulkan hari ini adalah Obyek yang merujuk pada sumber daya terbatas yang berada di luar JVM; Soket, File, Koneksi Basis Data, dll dan dapat digunakan kembali. Objek biasa tidak dapat digabungkan dalam arti yang sama seperti dalam bahasa yang memungkinkan Anda akses langsung ke lokasi memori. Objek caching adalah konsep yang berbeda dan mungkin atau mungkin tidak apa yang beberapa orang naif menyebut pooling , dua konsep yang tidak sama dan tidak boleh digabungkan.

Algoritma GC modern tidak memiliki masalah ini karena mereka tidak mengalokasikan pada jadwal, mereka mendelokasi ketika memori bebas diperlukan dalam generasi tertentu. Jika tumpukan cukup besar, maka tidak ada alokasi yang cukup lama untuk menyebabkan jeda.

Berorientasi Objek Bahasa dinamis mengalahkan C bahkan sekarang berhari-hari pada pengujian sensitif komputasi.

Komunitas
sumber
275
+1: "Operasi Anda yang paling mahal adalah mendengarkan mereka ...". Terbaik yang pernah saya dengar selama beberapa waktu.
rjnilsson
21
@DeadMG, bahkan dengan overhead GC kumulatif, Java bisa lebih cepat daripada C ++ (misalnya, karena pemadatan tumpukan, meminimalkan kesalahan cache untuk struktur data tertentu).
SK-logic
13
@ SK-logic: Karena GC non-deterministik, sangat sulit untuk membuktikannya. Adapun untuk meminimalkan kesalahan cache, sulit untuk melakukannya ketika setiap objek harus menjadi tipuan lain, membuang ruang cache dan meningkatkan waktu eksekusi. Namun, saya berpendapat bahwa hanya dengan menggunakan pengalokasi yang sesuai di C ++ seperti kumpulan objek atau arena memori, Anda dapat dengan mudah mencocokkan atau mengalahkan kinerja pengumpul sampah. Misalnya, Anda dapat menulis kelas arena memori yang akan berkinerja lebih cepat daripada _alloca, diamortisasi.
DeadMG
33
Pembuatan objek sekarang lebih murah daripada dulu. Ini tidak gratis. Siapa pun yang memberi tahu Anda bahwa itu bohong. Dan tautan tentang bahasa OO yang mengalahkan C adalah respons berlebihan terhadap seseorang yang benar-benar berusaha untuk belajar.
jasonk
17
Karena jawaban semacam inilah kami berakhir dengan kode jelek. Jawaban yang tepat adalah membuat objek di Jawa adalah membuat objek Java dan menginisialisasi itu. Bagian pertama murah, kedua bisa sangat mahal. Orang harus selalu melihat apa yang terjadi pada konstruktor sebelum menggunakan newkata kunci di tempat hotspot. Saya telah melihat orang menggunakan new ImageIcon(Image)dalam paint()metode objek Swing, yang cukup mahal dan membuat seluruh UI yang super lamban. Jadi itu bukan jawaban hitam dan putih, pikirkan sebelum Anda menggunakan newsuatu tempat.
qwertzguy
94

Intinya: Jangan kompromi desain Anda untuk mengambil jalan pintas membuat objek. Hindari membuat objek yang tidak perlu. Jika masuk akal, desain untuk menghindari operasi yang berlebihan (dalam bentuk apa pun).

Bertentangan dengan sebagian besar jawaban - ya, alokasi objek TIDAK memiliki biaya yang terkait. Ini adalah biaya rendah, tetapi Anda harus menghindari membuat objek yang tidak perlu. Sama seperti Anda harus menghindari apa pun yang tidak perlu dalam kode Anda. Grafik objek besar membuat GC lebih lambat, menyiratkan waktu eksekusi yang lebih lama karena Anda mungkin akan memiliki lebih banyak panggilan metode, memicu lebih banyak cache CPU yang meleset, dan meningkatkan kemungkinan proses Anda ditukar ke disk dalam kasus RAM yang rendah.

Sebelum ada yang mengomel tentang ini menjadi kasus tepi - Saya telah membuat profil aplikasi yang, sebelum mengoptimalkan, menciptakan 20 + MB objek untuk memproses ~ 50 baris data. Ini bagus dalam pengujian, hingga Anda meningkatkan hingga seratus permintaan per menit dan tiba-tiba Anda membuat 2GB data per menit. Jika Anda ingin melakukan 20 reqs / detik Anda membuat 400MB objek dan kemudian membuangnya. 20 reqs / detik kecil untuk server yang layak.

jasonk
sumber
7
Saya dapat menambahkan contoh: Dalam beberapa kasus itu benar-benar tidak membuat perbedaan dalam hal kejelasan kode tetapi dapat membuat perbedaan lebih atau kurang besar dalam kinerja: Ketika membaca dari stream, misalnya, tidak jarang menggunakan sesuatu seperti while(something) { byte[] buffer = new byte[10240]; ... readIntoBuffer(buffer); ...yang mungkin boros dibandingkan dengan misalnya byte[] buffer = new byte[10240]; while(something) { ... readIntoBuffer(buffer); ....
JimmyB
4
+1: Pembuatan objek yang tidak perlu (atau lebih tepatnya membersihkan setelah dihapus) kadang-kadang bisa mahal. Grafik 3D / kode OpenGL dalam java adalah satu tempat di mana saya telah melihat optimasi dilakukan untuk meminimalkan jumlah objek yang dibuat karena GC jika tidak dapat menimbulkan kekacauan dengan framerate Anda.
Leo
1
Wow! 20+ MB untuk memproses 50 baris data? Kedengarannya gila. Bagaimanapun, apakah benda-benda itu berumur panjang? Karena itulah kasus yang penting bagi GC. Jika di sisi lain Anda hanya membahas persyaratan memori , itu tidak terkait dengan pengumpulan sampah atau efisiensi pembuatan objek ...
Andres F.
1
Objek berumur pendek (kurang dari 0,5detik dalam kasus umum). Volume sampah itu masih memengaruhi kinerja.
jasonk
2
Terima kasih untuk benar-benar berdiri teguh pada kenyataan, siapa pun yang hidup dengan efek arsitektur tanpa berpikir bisa sangat berhubungan.
JM Becker
60

Sebenarnya, karena strategi manajemen memori yang dimungkinkan oleh bahasa Jawa (atau bahasa yang dikelola lainnya), pembuatan objek sedikit lebih banyak daripada penambahan pointer di blok memori yang disebut generasi muda. Ini jauh lebih cepat daripada C, di mana pencarian untuk memori bebas harus dilakukan.

Bagian lain dari biaya adalah penghancuran objek, tetapi sulit untuk dibandingkan dengan C. Biaya koleksi didasarkan pada jumlah objek yang disimpan jangka panjang tetapi frekuensi koleksi didasarkan pada jumlah objek yang dibuat ... di pada akhirnya, ini masih jauh lebih cepat daripada manajemen memori C-style.

amara
sumber
7
+1 Meskipun pengumpul sampah "hanya berfungsi", setiap pemrogram Java harus belajar tentang pengumpul sampah generasi.
benzado
18
Versi Java yang lebih baru dapat melakukan analisis pelarian, yang berarti dapat mengalokasikan memori untuk objek yang tidak melarikan diri dari metode pada stack, sehingga membersihkannya gratis - pengumpul sampah tidak harus berurusan dengan objek tersebut , mereka secara otomatis dibuang ketika susunan bingkai metode dibatalkan (ketika metode kembali).
Jesper
4
Sebuah satu langkah berikutnya untuk analisis melarikan diri adalah untuk "mengalokasikan" memori untuk benda kecil murni dalam register (misalnya Pointobjek bisa muat di 2 register tujuan umum).
Joachim Sauer
6
@ Joachim Sauer: Itulah yang dilakukan dalam implementasi terbaru dari HotSpot VM. Ini disebut penggantian skalar.
someguy
1
@someguy: Saya sudah membacanya beberapa waktu lalu sebagai hal berikutnya, tetapi tidak menindaklanjuti untuk memeriksa apakah sudah dilakukan. Ini berita bagus untuk mendengar bahwa kita sudah memiliki ini.
Joachim Sauer
38

Poster-poster lain dengan tepat menunjukkan bahwa pembuatan objek sangat cepat di Jawa, dan Anda biasanya tidak perlu khawatir tentang hal itu dalam aplikasi Java normal.

Ada beberapa situasi yang sangat khusus di mana adalah merupakan ide yang baik untuk menghindari pembuatan obyek.

  • Saat Anda menulis aplikasi yang sensitif terhadap latensi dan ingin menghindari GC dijeda. Semakin banyak objek yang Anda hasilkan, semakin banyak GC terjadi dan semakin besar peluang jeda. Ini mungkin pertimbangan yang valid untuk game yang relevan, beberapa aplikasi media, kontrol robot, perdagangan frekuensi tinggi dll. Solusinya adalah dengan melakukan pra-alokasi semua objek / array yang Anda butuhkan di muka dan menggunakannya kembali. Ada perpustakaan yang berspesialisasi dalam menyediakan kemampuan semacam ini, misalnya Javolution . Tetapi bisa dibilang, jika Anda benar-benar peduli tentang latensi rendah Anda harus menggunakan C / C ++ / assembler daripada Java atau C # :-)
  • Menghindari primitif kotak (Double, Integer, dll.) Dapat menjadi optimasi mikro yang sangat bermanfaat dalam keadaan tertentu. Karena primitif tanpa kotak (dobel, int, dll.) Menghindari overhead per-objek, mereka jauh lebih cepat bekerja secara intensif CPU seperti pemrosesan numerik dll. Biasanya, array primitif berkinerja sangat baik di Jawa sehingga Anda ingin menggunakan ini untuk pengelompokan angka Anda alih-alih segala jenis benda lainnya.
  • Dalam situasi memori terbatas Anda ingin meminimalkan jumlah objek (aktif) yang dibuat karena masing-masing objek membawa overhead kecil (biasanya 8-16 byte tergantung pada implementasi JVM). Dalam keadaan seperti itu Anda harus lebih memilih sejumlah kecil objek besar atau array untuk menyimpan data Anda daripada sejumlah besar objek kecil.
mikera
sumber
3
Perlu dicatat bahwa analisis pelarian akan memungkinkan benda yang berumur pendek (dibatasi kehidupan) disimpan di tumpukan, benda-benda ini dapat dideallocated secara
Richard Tingle
1
@ Richard: analisis hebat - pelarian memberikan beberapa optimisasi yang sangat baik. Anda harus berhati-hati mengandalkannya: itu tidak dijamin terjadi di semua implementasi Java dan dalam semua keadaan. Jadi, Anda sering perlu benchmark untuk memastikan.
mikera
2
Juga, 100% analisis pelarian yang benar sama dengan masalah penghentian. Jangan mengandalkan analisis pelarian dalam situasi yang kompleks, karena kemungkinan akan memberikan jawaban yang salah setidaknya beberapa waktu.
Jules
Poin pertama dan terakhir tidak berlaku untuk objek kecil yang cepat dirilis, mereka mati di eden (pikirkan alokasi stack dalam C, cukup banyak gratis) dan tidak pernah mempengaruhi waktu GC atau alokasi memori. Sebagian besar alokasi objek di Jawa termasuk dalam kategori ini dan karenanya pada dasarnya gratis. Poin kedua masih valid.
Bill K
17

Ada inti kebenaran dalam apa yang dikatakan rekan Anda. Saya dengan hormat menyarankan masalah dengan pembuatan objek sebenarnya adalah pengumpulan sampah . Dalam C ++ programmer dapat mengontrol dengan tepat bagaimana memori dialokasikan. Program ini dapat mengakumulasi crud selama atau sesingkat yang diinginkannya. Selanjutnya, program C ++ dapat membuang crud menggunakan utas yang berbeda dari yang membuatnya. Dengan demikian utas yang saat ini berfungsi tidak pernah berhenti untuk membersihkan.

Sebaliknya, Java Virtual Machine (JVM) secara berkala menghentikan kode Anda untuk mendapatkan kembali memori yang tidak digunakan. Sebagian besar pengembang Java tidak pernah memperhatikan jeda ini, karena biasanya jarang dan sangat singkat. Semakin kasar Anda menumpuk atau semakin dibatasi JVM Anda, semakin sering jeda ini terjadi. Anda dapat menggunakan alat seperti VisualVM untuk memvisualisasikan proses ini.

Dalam versi terbaru Java, algoritma pengumpulan sampah (GC) dapat disetel . Sebagai aturan umum, semakin pendek Anda ingin membuat jeda, semakin mahal biaya overhead di mesin virtual (yaitu CPU dan memori yang dihabiskan mengoordinasikan proses GC).

Kapan hal ini penting? Setiap kali Anda peduli dengan tingkat respons sub-milidetik yang konsisten, Anda akan peduli dengan GC. Sistem perdagangan otomatis yang ditulis dalam Java sangat menyempurnakan JVM untuk meminimalkan jeda. Perusahaan yang sebaliknya akan menulis Java beralih ke C ++ dalam situasi di mana sistem harus sangat responsif sepanjang waktu.

Sebagai catatan, saya tidak memaafkan penghindaran objek secara umum! Default untuk pemrograman berorientasi objek. Sesuaikan pendekatan ini hanya jika GC menghalangi Anda, dan kemudian hanya setelah mencoba menyetel JVM untuk berhenti sebentar. Buku bagus tentang penyetelan kinerja Java adalah Java Performance oleh Charlie Hunt dan Binu John.

greg
sumber
10
Kebanyakan JVM modern mendukung algoritma pengumpulan sampah yang lebih baik daripada "stop the world". Waktu diamortisasi yang dihabiskan dalam pengumpulan sampah sering kurang dari apa yang dihabiskan secara eksplisit disebut malloc / gratis. Juga apakah manajemen memori C ++ berjalan di utas terpisah?
10
Versi Java yang lebih baru dari Oracle dapat melakukan analisis pelarian, yang berarti bahwa objek yang tidak lolos dari metode dialokasikan pada stack, yang membuat pembersihannya gratis - mereka secara otomatis dialokasikan saat metode kembali.
Jesper
2
@ ThorbjørnRavnAndersen: Benarkah? Apakah Anda benar-benar mengartikan GC pause- less , atau maksud Anda "GC biasanya-paralel-tetapi-terkadang-sedikit-jeda-kecil"? Saya tidak mengerti bagaimana GC tidak pernah bisa menghentikan program, namun memindahkan objek pada saat yang sama ... sepertinya, secara harfiah, tidak mungkin bagi saya ...
Mehrdad
2
@ ThorbjørnRavnAndersen: Bagaimana bisa "menghentikan dunia" tetapi tidak pernah "menghentikan JVM"? Itu tidak masuk akal ...
Mehrdad
2
@ ThorbjørnRavnAndersen: Tidak, saya tidak; Aku hanya tidak mengerti apa yang kamu katakan. Entah itu GC kadang-kadang menghentikan program, dalam hal ini - menurut definisi - bukan "tanpa jeda", atau tidak pernah menjeda program (karenanya, "tanpa jeda"), dalam hal ini saya tidak mengerti bagaimana itu sesuai dengan pernyataan "itu menghentikan dunia ketika tidak bisa mengikuti", karena itu tampaknya menyiratkan bahwa program dijeda saat GC beroperasi. Maukah Anda menjelaskan yang mana masalahnya (apakah itu diam atau tidak?), Dan bagaimana itu mungkin?
Mehrdad
11

Ada satu kasus di mana Anda mungkin berkecil dari membuat terlalu banyak objek di Jawa karena perancangan overhead untuk kinerja pada platform Android

Selain itu, jawaban di atas juga berlaku.

Peter Kelly
sumber
2
Baca lagi pertanyaannya. Itu tidak menentukan JVM di mana saja termasuk tag. Itu hanya menyebutkan Java.
Peter Kelly
9

GC disetel untuk banyak objek berumur pendek

yang mengatakan jika Anda dapat dengan mudah mengurangi alokasi objek Anda harus

salah satu contoh akan membangun string dalam satu lingkaran, cara yang naif adalah

String str = "";
while(someCondition){
    //...
    str+= appendingString;
}

yang membuat Stringobjek baru pada setiap +=operasi (ditambah a StringBuilderdan array char yang mendasarinya baru)

Anda dapat dengan mudah menulis ulang ini menjadi:

StringBuilder strB = new StringBuilder();
while(someCondition){
    //...
    strB.append(appendingString);
}
String str = strB.toString();

pola ini (hasil yang tidak berubah dan nilai tengah lokal yang bisa berubah) dapat diterapkan untuk hal-hal lain juga

tetapi selain itu Anda harus menarik profiler untuk menemukan hambatan sebenarnya daripada mengejar hantu

ratchet freak
sumber
keuntungan terbesar dalam hal ini StringBuilderpendekatan adalah pra-ukuran yang StringBuildersehingga tidak harus mengalokasikan array yang mendasari dengan StringBuilder(int)konstruktor. Ini menjadikannya alokasi tunggal , bukan 1+Nalokasi.
2
@JarrodRoberson StringBuilder setidaknya akan menggandakan kapasitas saat ini atau dengan kata lain kapasitas akan tumbuh secara eksponensial hanya untuk alokasi log (n)
ratchet freak
6

Joshua Bloch (salah satu pembuat platform Java) menulis dalam bukunya, Java Efektif pada tahun 2001:

Menghindari pembuatan objek dengan mempertahankan kolam objek Anda sendiri adalah ide yang buruk kecuali jika objek di kolam itu sangat berat. Contoh prototipikal dari objek yang tidak membenarkan kumpulan objek adalah koneksi database. Biaya membangun koneksi cukup tinggi sehingga masuk akal untuk menggunakan kembali objek-objek ini. Secara umum, mempertahankan objek Anda sendiri mengacaukan kode Anda, meningkatkan jejak memori, dan merusak kinerja. Implementasi JVM modern memiliki pengumpul sampah yang sangat dioptimalkan yang dengan mudah mengungguli kumpulan objek tersebut pada objek yang ringan.

Anthony Ananich
sumber
1
Bahkan dengan hal-hal seperti koneksi jaringan, yang penting adalah untuk tidak mempertahankan objek yang dialokasikan GC yang terkait dengan koneksi, tetapi untuk mempertahankan set koneksi yang ada di luar dunia yang dikelola GC. Ada saat-saat ketika mengumpulkan objek sendiri dapat membantu; sebagian besar dari mereka berasal dari kasus di mana sepasang referensi ke objek yang sama mungkin secara semantik setara dengan sepasang referensi ke objek yang identik tetapi berbeda, tetapi tetap memiliki beberapa keunggulan. Sebagai contoh, dua referensi ke string lima puluh ribu karakter yang sama dapat diperiksa untuk kesetaraan ...
supercat
2
... jauh lebih cepat daripada referensi ke dua string yang berbeda tetapi identik dari panjang itu.
supercat
5

Ini sangat tergantung pada aplikasi spesifik, sehingga sangat sulit untuk mengatakan secara umum. Namun, saya akan cukup terkejut jika penciptaan objek sebenarnya merupakan hambatan kinerja dalam suatu aplikasi. Meskipun lambat, manfaat gaya kode mungkin akan lebih besar daripada kinerjanya (kecuali jika itu benar-benar terlihat oleh pengguna)

Dalam kasus apa pun, Anda bahkan tidak perlu khawatir tentang hal-hal ini sampai Anda telah membuat profil kode Anda untuk menentukan hambatan kinerja yang sebenarnya alih-alih menebak. Sampai saat itu, Anda harus melakukan apa pun yang terbaik untuk keterbacaan kode, bukan kinerja.

Oleksi
sumber
Pada versi awal Jawa ada biaya besar yang dikeluarkan ketika pengumpul sampah membersihkan benda-benda yang dibuang. Versi terbaru Java telah sangat meningkatkan opsi GC, jadi pembuatan objek jarang menjadi masalah. Biasanya sistem web dibatasi oleh panggilan IO ke sistem eksternal kecuali jika Anda menghitung sesuatu yang benar-benar rumit pada server aplikasi sebagai bagian dari pemuatan halaman.
greg
3

Saya pikir kolega Anda pasti mengatakan dari perspektif penciptaan objek yang tidak perlu. Maksud saya jika Anda sering membuat objek yang sama maka lebih baik membagikan objek itu. Bahkan dalam kasus-kasus di mana pembuatan objek menjadi kompleks dan membutuhkan lebih banyak memori, Anda mungkin ingin mengkloning objek itu dan menghindari membuat proses pembuatan objek yang kompleks (Tapi itu tergantung pada kebutuhan Anda). Saya pikir pernyataan "Pembuatan objek itu mahal" harus diambil dalam konteks.

Sejauh menyangkut persyaratan memori JVM, tunggu Java 8, Anda bahkan tidak perlu menentukan -Xmx, pengaturan ruang meta akan mengurus kebutuhan memori JVM dan akan tumbuh secara otomatis.

AKS
sumber
Akan sangat konstruktif jika orang-orang memberikan komentar juga saat memilih jawaban apa pun. Lagipula kita semua di sini untuk berbagi pengetahuan, dan hanya menghakimi tanpa alasan yang tepat tidak akan melayani tujuan apa pun.
AKS
1
Stack exchange harus memiliki mekanisme untuk memberi tahu pengguna yang memilih jawaban apa pun, ketika komentar diposting pada jawaban itu.
AKS
1

Ada lebih banyak hal untuk membuat kelas daripada mengalokasikan memori. Ada juga inisialisasi, saya tidak yakin mengapa bagian itu tidak tercakup oleh semua jawaban. Kelas normal mengandung beberapa variabel, dan melakukan beberapa bentuk inisialisasi, dan itu tidak gratis. Bergantung pada apa kelasnya, ia dapat membaca file, atau melakukan sejumlah operasi lambat lainnya.

Jadi pertimbangkan saja apa yang dikerjakan oleh konstruktor kelas, sebelum memperkirakan apakah itu gratis atau tidak.

Alex
sumber
2
Kelas yang dirancang dengan baik tidak boleh melakukan pekerjaan berat di konstruktornya, hanya untuk alasan ini. Misalnya, kelas membaca file Anda dapat menurunkan biaya instantiasi dengan hanya memverifikasi bahwa file targetnya ada pada startup, dan menunda operasi file aktual hingga panggilan metode pertama yang membutuhkan data dari file.
GordonM
2
Jika biaya tambahan dipindahkan ke 'if (firstTime)', daripada membuat ulang kelas dalam satu lingkaran masih lebih mahal daripada menggunakan kembali kelas.
Alex
Saya hendak memposting jawaban yang sama, dan saya percaya ini adalah jawaban yang tepat. Begitu banyak orang yang dibutakan oleh perkataan itu bahwa membuat objek Java adalah murah sehingga mereka tidak menyadari bahwa seringkali konstruktor atau inisialisasi lebih lanjut bisa jauh dari murah. Misalnya kelas ImageIcon di Swing, bahkan jika Anda memberikan objek gambar yang dimuat sebelumnya, konstruktornya cukup mahal. Dan saya tidak setuju dengan @GordonM, banyak kelas dari JDK melakukan sebagian besar init di konstruktor, dan saya pikir itu membuat kode lebih ramping, dirancang lebih baik.
qwertzguy
1

Java GC sebenarnya sangat dioptimalkan dalam hal menciptakan banyak objek dengan cepat dengan cara "meledak". Dari apa yang saya mengerti mereka menggunakan pengalokasi sekuensial (pengalokasi O tercepat (1) tercepat dan paling sederhana untuk permintaan berukuran variabel) untuk "siklus ledakan" ke dalam ruang memori yang mereka sebut "ruang Eden", dan hanya jika objek tetap ada setelah siklus GC mereka pindah ke tempat di mana GC dapat mengumpulkannya satu per satu.

Dengan itu, jika kebutuhan kinerja Anda menjadi cukup kritis (seperti yang diukur dengan persyaratan pengguna-akhir yang nyata), maka objek melakukan overhead tetapi saya tidak akan memikirkannya begitu banyak dalam hal penciptaan / alokasi. Ini lebih berkaitan dengan lokalitas referensi dan ukuran tambahan dari semua objek di Jawa yang diperlukan untuk mendukung konsep-konsep seperti refleksi dan pengiriman dinamis ( Floatlebih besar dari float, sering sekitar 4 kali lebih besar pada 64-bit dengan persyaratan perataan, dan array Floattidak selalu dijamin untuk disimpan bersebelahan dari apa yang saya mengerti).

Salah satu hal paling menarik yang pernah saya lihat dikembangkan di Jawa yang membuat saya menganggapnya sebagai pesaing berat di bidang saya (VFX) adalah pelacak jalur standar multithreaded interaktif (tidak menggunakan caching irradian atau BDPT atau MLS atau apa pun) di CPU menyediakan pratinjau waktu-nyata yang konvergen lebih cepat ke gambar bebas-noise. Saya telah bekerja dengan para profesional di C ++ mendedikasikan karir mereka untuk hal-hal seperti itu dengan profiler mewah di tangan yang mengalami kesulitan melakukan itu.

Tapi saya mengintip kode sumber dan sementara itu menggunakan banyak objek dengan biaya sepele, bagian paling penting dari pelacak jalur (BVH dan segitiga dan bahan) sangat jelas dan sengaja menghindari objek yang mendukung array besar tipe primitif ( kebanyakan float[]dan int[]), yang membuatnya menggunakan memori jauh lebih sedikit dan menjamin lokalitas spasial untuk mendapatkan dari satu floatdalam array ke yang berikutnya. Saya tidak berpikir itu terlalu spekulatif untuk berpikir bahwa, jika penulis menggunakan tipe kotak sukaFloatdi sana, itu akan datang pada biaya yang agak besar untuk kinerjanya. Tapi kita sedang berbicara bagian yang paling kritis dari mesin itu, dan saya cukup yakin, mengingat betapa terampilnya pengembang mengoptimalkannya, bahwa dia mengukurnya dan menerapkan optimasi itu dengan sangat, sangat bijaksana, karena dia dengan senang hati menggunakan objek di tempat lain dengan biaya sepele untuk pelacak jalur realtime yang mengesankan.

Saya diberitahu oleh seorang rekan bahwa dalam pembuatan objek Java adalah operasi paling mahal yang bisa Anda lakukan. Jadi saya hanya bisa menyimpulkan untuk membuat objek sesedikit mungkin.

Bahkan di bidang yang sangat kritis terhadap kinerja seperti milik saya, Anda tidak akan menulis produk yang efisien jika Anda melukai diri sendiri di tempat-tempat yang tidak penting. Saya bahkan akan membuat klaim bahwa bidang yang paling kritis terhadap kinerja mungkin menempatkan permintaan yang lebih tinggi untuk produktivitas, karena kita membutuhkan semua waktu ekstra yang bisa kita dapatkan untuk menyetel titik-titik panas yang benar-benar penting dengan tidak membuang-buang waktu pada hal-hal yang tidak . Seperti contoh jejak lacak dari atas, penulis dengan terampil dan bijaksana menerapkan optimisasi semacam itu hanya ke tempat-tempat yang benar-benar penting, dan mungkin di belakang setelah pengukuran, dan masih dengan senang hati menggunakan objek di tempat lain.

Energi Naga
sumber
1
Paragraf pertama Anda ada di jalur yang benar. Ruang eden dan young menggunakan pengumpul salinan yang memiliki kira-kira. 0 biaya untuk benda mati. Saya sangat merekomendasikan presentasi ini . Di samping catatan, Anda menyadari pertanyaan ini dari 2012, kan?
JimmyJames
@JimmyJames Saya bosan dan suka menyaring semua pertanyaan, termasuk yang lama. Saya harap orang tidak keberatan dengan necromancy saya! :-D
Dragon Energy
@ JimmyJames. Apakah ini bukan lagi kasus bahwa tipe kotak memiliki persyaratan memori tambahan dan tidak dijamin berdekatan dalam array? Saya cenderung berpikir benda-benda itu murah di Jawa, tetapi satu kasus di mana mereka mungkin relatif mahal adalah seperti float[]vs Float[], di mana pemrosesan satu juta dari yang sebelumnya secara berurutan mungkin relatif lebih cepat daripada yang kedua.
Dragon Energy
1
Ketika saya pertama kali mulai memposting di sini saya juga melakukan hal yang sama. Hanya saja, jangan kaget ketika jawaban atas pertanyaan lama cenderung hanya mendapat sedikit perhatian.
JimmyJames
1
Saya cukup yakin bahwa jenis kotak harus dianggap menggunakan lebih banyak ruang daripada primitif. Ada potensi optimasi di sekitar itu tetapi saya secara umum, saya pikir itu seperti yang Anda gambarkan. Gil Tene (dari presentasi di atas) memiliki pembicaraan lain tentang proposal untuk menambahkan jenis koleksi khusus yang akan memberikan beberapa manfaat struct tetapi masih menggunakan objek. Ini ide yang menarik, saya tidak yakin dengan status JSR. Biaya ini sebagian besar disebabkan oleh waktu yang Anda singgung. Jika Anda dapat menghindari membiarkan benda 'melarikan diri', benda itu bahkan dapat ditumpuk dan tidak pernah menyentuh tumpukan.
JimmyJames
0

Seperti yang orang katakan, pembuatan objek bukan biaya besar di Jawa (tapi saya bertaruh lebih besar dari operasi paling sederhana seperti menambahkan, dll) dan Anda tidak boleh menghindarinya terlalu banyak.

Itu mengatakan itu masih biaya, dan kadang-kadang Anda mungkin menemukan diri Anda mencoba untuk memo objek sebanyak mungkin. Tetapi hanya setelah profiling menunjukkan bahwa ini adalah masalah.

Berikut ini adalah presentasi yang bagus mengenai topik ini: https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efisien-java-tutorial.pdf

rkj
sumber
0

Saya melakukan microbenchmark cepat tentang ini dan saya telah menyediakan sumber lengkap di github. Kesimpulan saya adalah apakah membuat objek itu mahal atau tidak bukan masalah, tetapi terus-menerus membuat objek dengan anggapan bahwa GC akan menangani hal-hal untuk Anda akan membuat aplikasi Anda memicu proses GC lebih cepat. GC adalah proses yang sangat mahal dan yang terbaik adalah menghindarinya sedapat mungkin dan tidak mencoba mendorongnya untuk memulai.

Archimedes Trajano
sumber
ini tampaknya tidak menawarkan sesuatu yang substansial atas poin yang dibuat dan dijelaskan dalam 15 jawaban sebelumnya. Hampir tidak ada konten yang bernilai menabrak pertanyaan yang diajukan lebih dari 2 tahun yang lalu dan telah mendapatkan jawaban yang sangat menyeluruh
nyamuk