Bagaimana cara mengosongkan memori di Java?

146

Apakah ada cara untuk membebaskan memori di Jawa, mirip dengan free()fungsi C ? Atau apakah mengatur objek ke nol dan mengandalkan GC satu-satunya opsi?

Felix
sumber
151
Ok ... mari kita luruskan satu hal. Hanya karena Anda berpikir ada sesuatu yang merupakan praktik buruk dan bukan sesuatu untuk mendorong dilakukannya, tidak membuatnya layak untuk diperhitungkan. Ini adalah pertanyaan yang jelas dan valid, menanyakan apakah ada cara untuk melepaskan memori di Jawa tanpa mengandalkan pengumpulan sampah. Meskipun mungkin tidak disarankan dan umumnya tidak berguna atau ide yang baik, Anda tidak bisa tahu bahwa tidak ada skenario di mana mungkin diperlukan tanpa mengetahui apa yang diketahui Felix. Felix bahkan mungkin tidak berencana menggunakannya. Dia mungkin hanya ingin tahu apakah itu mungkin. Sama sekali tidak layak menerima suara.
Daniel Bingham
7
Untuk klarifikasi, itu ditujukan untuk siapa saja yang menolaknya - bukan komentar sebelumnya.
Daniel Bingham

Jawaban:

96

Java menggunakan memori yang dikelola, jadi satu-satunya cara Anda dapat mengalokasikan memori adalah dengan menggunakan newoperator, dan satu-satunya cara Anda dapat mengalokasikan memori adalah dengan mengandalkan pengumpul sampah.

Whitepaper manajemen memori ini (PDF) dapat membantu menjelaskan apa yang terjadi.

Anda juga dapat menelepon System.gc()untuk menyarankan agar pengumpul sampah segera beroperasi. Namun, Java Runtime membuat keputusan akhir, bukan kode Anda.

Menurut dokumentasi Java ,

Memanggil metode gc menunjukkan bahwa Java Virtual Machine mengeluarkan upaya untuk mendaur ulang objek yang tidak digunakan untuk membuat memori yang mereka tempati saat ini tersedia untuk digunakan kembali dengan cepat. Ketika kontrol kembali dari pemanggilan metode, Java Virtual Machine telah melakukan upaya terbaik untuk merebut kembali ruang dari semua objek yang dibuang.

Daniel Pryden
sumber
5
Itu memaksa Pengumpul Sampah untuk lari. Ini tidak memaksa untuk memori bebas meskipun ...
Pablo Santa Cruz
13
Tidak Pablo, itu tidak memaksa GC untuk berjalan.
Jesper
1
Saya telah diberitahu oleh orang yang sangat andal bahwa semua pengumpul sampah HotSpotVM mengabaikan System.gc()sepenuhnya.
Esko
1
Pada winXp java SE GC menjalankan setiap System.gc () atau hampir setiap tetapi API API tidak menjaminnya.
teodozjan
2
@Pablo Santa Cruz Apa maksudmu itu tidak membebaskan memori? Saya baru saja mengujinya di program saya yang tampaknya memiliki kebocoran dan penggunaan ram tampaknya stabil? Dan Daniel hanya mengatakan itu hanya menyarankan maka mengapa persentase ram yang digunakan selalu stabil setiap kali saya memanggil metode. Anda orang membingungkan saya.
65

Tidak ada yang tampaknya telah menyebutkan secara eksplisit mengatur referensi objek null, yang merupakan teknik yang sah untuk "membebaskan" memori yang mungkin ingin Anda pertimbangkan.

Misalnya, Anda menyatakan bahwa List<String>pada awal metode yang tumbuh menjadi sangat besar, tetapi hanya diperlukan hingga separuh jalan melalui metode. Anda bisa pada titik ini mengatur referensi Daftar nulluntuk memungkinkan pengumpul sampah untuk berpotensi mengklaim kembali objek ini sebelum metode selesai (dan referensi itu jatuh dari ruang lingkup tetap).

Perhatikan bahwa saya jarang menggunakan teknik ini dalam kenyataan tetapi perlu dipertimbangkan ketika berhadapan dengan struktur data yang sangat besar.

Adamski
sumber
8
Jika Anda benar-benar melakukan banyak pekerjaan pada objek yang hanya digunakan untuk bagian dari metode saya sarankan juga; metode Anda terlalu kompilasi, memecah metode menjadi bagian sebelum dan sesudah, atau menggunakan blok untuk bagian pertama kode (yang kemudian lebih berguna untuk skrip pengujian)
Peter Lawrey
5
Tempat di mana pengaturan referensi objek ke null adalah penting ketika itu direferensikan dari objek berumur panjang lainnya (atau mungkin dari var statis). Misalnya, jika Anda memiliki array objek besar yang berumur panjang, dan Anda berhenti menggunakan salah satu objek tersebut, Anda harus menetapkan referensi array menjadi nol untuk membuat objek tersedia untuk GC.
Hot Licks
22
System.gc(); 

Menjalankan pemulung.

Memanggil metode gc menunjukkan bahwa Java Virtual Machine mengeluarkan upaya untuk mendaur ulang objek yang tidak digunakan untuk membuat memori yang mereka tempati saat ini tersedia untuk digunakan kembali dengan cepat. Ketika kontrol kembali dari pemanggilan metode, Java Virtual Machine telah melakukan upaya terbaik untuk merebut kembali ruang dari semua objek yang dibuang.

Tidak direkomendasikan.

Sunting: Saya menulis respons asli pada 2009. Sekarang 2015.

Pengumpul sampah semakin membaik dalam ~ 20 tahun di Jawa. Pada titik ini, jika Anda secara manual memanggil pengumpul sampah, Anda mungkin ingin mempertimbangkan pendekatan lain:

  • Jika Anda memaksakan GC pada sejumlah mesin, mungkin ada baiknya titik penyeimbang muatan menjauh dari mesin saat ini, menunggu sampai selesai melayani klien yang terhubung, batas waktu setelah beberapa periode untuk menggantung koneksi, dan kemudian hanya sulit -mulai JVM. Ini adalah solusi yang mengerikan, tetapi jika Anda sedang melihat System.gc (), restart yang dipaksakan mungkin bisa menjadi penghenti sementara.
  • Pertimbangkan untuk menggunakan pengumpul sampah yang berbeda. Misalnya, kolektor G1 (baru dalam enam tahun terakhir) adalah model jeda rendah; itu menggunakan lebih banyak CPU secara keseluruhan, tetapi apakah itu terbaik untuk tidak pernah memaksa berhenti pada eksekusi. Karena CPU server sekarang hampir semuanya memiliki banyak core, ini adalah A Really Good Tradeoff yang tersedia.
  • Lihatlah flag Anda menggunakan memori tuning. Terutama di versi Java yang lebih baru, jika Anda tidak memiliki banyak objek berjalan jangka panjang, pertimbangkan untuk memperbesar ukuran newgen di heap. newgen (young) adalah tempat objek baru dialokasikan. Untuk server web, semua yang dibuat untuk permintaan diletakkan di sini, dan jika ruang ini terlalu kecil, Java akan menghabiskan waktu ekstra untuk memperbarui objek ke memori yang lebih lama, di mana mereka lebih mahal untuk dibunuh. (Jika newgen sedikit terlalu kecil, Anda akan membayarnya.) Misalnya, di G1:
    • XX: G1NewSizePercent (default ke 5; mungkin tidak masalah.)
    • XX: G1MaxNewSizePercent (default ke 60; mungkin menaikkan ini.)
  • Pertimbangkan untuk memberi tahu pemulung bahwa Anda tidak setuju dengan jeda yang lebih lama. Ini akan menyebabkan GC berjalan lebih sering, untuk memungkinkan sistem menjaga sisa kendala itu. Di G1:
    • XX: MaxGCPauseMillis (default ke 200.)
Dean J
sumber
1
Mengomentari posting saya sendiri, ini sering tidak melakukan apa-apa, dan menyebutnya berulang kali dapat menyebabkan JVM menjadi tidak stabil dan yang lainnya. Mungkin juga menabrak anjing Anda; pendekatan dengan hati-hati.
Dean J
1
Saya akan sangat menekankan pada bagian "menyarankan" dari "Memanggil metode gc menunjukkan bahwa JVM memperluas upaya"
matt b
2
@Jesper, jawaban Dean menyatakan "menyarankan". Bahkan dia memposting dokumentasi yang tepat dari javadocs metode ...
matt b
2
@Software Monkey: Ya, saya bisa saja mengeditnya. Tapi karena Dean J jelas aktif (memposting hanya beberapa menit yang lalu), saya pikir itu sopan untuk memintanya melakukannya. Jika tidak, saya akan kembali ke sini dan melakukan edit dan menghapus komentar saya.
Daniel Pryden
1
Kita juga layak mengatakan MENGAPA tidak dianjurkan. Jika JVM memperhatikan "saran" untuk menjalankan GC, itu hampir pasti akan membuat aplikasi Anda berjalan lebih lambat, mungkin dengan banyak perintah besarnya!
Stephen C
11

* "Saya pribadi mengandalkan variabel nulling sebagai pengganti untuk penghapusan yang tepat di masa depan. Misalnya, saya meluangkan waktu untuk membatalkan semua elemen array sebelum benar-benar menghapus (membuat null) array itu sendiri."

Ini tidak perlu. Cara kerja Java GC adalah ia menemukan objek yang tidak memiliki referensi padanya, jadi jika saya memiliki Obyek x dengan referensi (= variabel) a yang menunjuk padanya, GC tidak akan menghapusnya, karena ada referensi ke objek itu:

a -> x

Jika Anda batal dari ini terjadi:

a -> null
     x

Jadi sekarang x tidak memiliki referensi yang menunjuk ke sana dan akan dihapus. Hal yang sama terjadi ketika Anda mengatur referensi ke objek yang berbeda dari x.

Jadi jika Anda memiliki array array yang merujuk ke objek x, y dan z dan variabel a yang merujuk ke array, tampilannya seperti itu:

a -> arr -> x
         -> y
         -> z

Jika Anda batal dari ini terjadi:

a -> null
     arr -> x
         -> y
         -> z

Jadi GC menemukan arr tidak memiliki referensi yang ditetapkan dan menghapusnya, yang memberi Anda struktur ini:

a -> null
     x
     y
     z

Sekarang GC menemukan x, y dan z dan menghapusnya juga. Menghapus setiap referensi dalam array tidak akan membuat sesuatu yang lebih baik, itu hanya akan menggunakan waktu dan ruang CPU dalam kode (yang mengatakan, tidak akan ada salahnya lebih jauh dari itu. GC masih akan dapat melakukan cara yang seharusnya ).

Dakkaron
sumber
5

Alasan yang sah untuk ingin membebaskan memori dari program apa pun (java atau tidak) adalah untuk membuat lebih banyak memori tersedia untuk program lain pada tingkat sistem operasi. Jika aplikasi java saya menggunakan 250MB saya mungkin ingin memaksanya turun ke 1MB dan membuat 249MB tersedia untuk aplikasi lain.

Yios
sumber
Jika Anda perlu secara eksplisit membebaskan sepotong 249MB, dalam program Java, manajemen memori tidak akan menjadi hal pertama yang ingin saya kerjakan.
Marc DiMillo
3
Tetapi membebaskan penyimpanan di dalam tumpukan Java Anda tidak (dalam kasus umum) membuat penyimpanan tersedia untuk aplikasi lain.
Hot Licks
5

Untuk memperluas jawaban dan komentar oleh Yiannis Xanthopoulos dan Hot Licks (maaf, saya belum bisa berkomentar!), Anda dapat mengatur opsi VM seperti contoh ini:

-XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30

Dalam jdk 7 saya ini kemudian akan melepaskan memori VM yang tidak digunakan jika lebih dari 30% dari tumpukan menjadi bebas setelah GC ketika VM idle. Anda mungkin perlu menyetel parameter ini.

Walaupun saya tidak melihatnya ditekankan pada tautan di bawah, perhatikan bahwa beberapa pemulung mungkin tidak mematuhi parameter ini dan secara default java dapat memilih salah satu dari ini untuk Anda, seandainya Anda memiliki lebih dari satu inti (maka argumen UseG1GC di atas ).

Argumen VM

Pembaruan: Untuk java 1.8.0_73 Saya telah melihat JVM sesekali melepaskan jumlah kecil dengan pengaturan default. Tampaknya hanya melakukannya jika ~ 70% dari heap tidak digunakan .. tidak tahu apakah akan lebih agresif melepaskan jika OS pada memori fisik rendah.

nsandersen
sumber
4

Saya sudah melakukan eksperimen tentang ini.

Memang benar bahwa System.gc();hanya menyarankan untuk menjalankan Pengumpul Sampah.

Tetapi memanggil System.gc();setelah mengatur semua referensi null, akan meningkatkan kinerja dan pekerjaan memori.

Hemant Yadav
sumber
Saya pikir Anda tidak bisa mengatakan dengan pasti bahwa "memanggil System.gc (); setelah mengatur semua referensi ke nol, akan meningkatkan kinerja dan pekerjaan memori.". Karena ada kompleksitas komputasi yang sangat besar dari System.gc (). Dan bahkan setelah memanggil System.gc () & memang mengumpulkan sampah, jvm mungkin tidak mengembalikan memori ke OS atau Sistem. JVM dapat menyimpan memori untuk referensi di masa mendatang. Lihat jawaban ini .
Md. Abu Nafee Ibna Zahid
3

Jika Anda benar-benar ingin mengalokasikan dan membebaskan blok memori, Anda dapat melakukan ini dengan ByteBuffers langsung. Bahkan ada cara non-portabel untuk membebaskan memori.

Namun, seperti yang telah disarankan, hanya karena Anda harus membebaskan memori dalam C, bukan berarti itu ide yang baik untuk melakukan ini.

Jika Anda merasa memiliki kasus penggunaan yang baik gratis (), harap sertakan dalam pertanyaan sehingga kami dapat melihat apa yang ingin Anda lakukan, kemungkinan besar ada cara yang lebih baik.

Peter Lawrey
sumber
3

Seluruhnya dari javacoffeebreak.com/faq/faq0012.html

Utas dengan prioritas rendah menangani pengumpulan sampah secara otomatis untuk pengguna. Selama waktu idle, utas dapat dipanggil, dan itu dapat mulai membebaskan memori yang sebelumnya dialokasikan untuk suatu objek di Jawa. Tapi jangan khawatir - itu tidak akan menghapus objek Anda!

Ketika tidak ada referensi ke suatu objek, itu menjadi permainan yang adil bagi pengumpul sampah. Daripada memanggil beberapa rutin (seperti gratis di C ++), Anda cukup menetapkan semua referensi ke objek ke nol, atau menetapkan kelas baru ke referensi.

Contoh:

public static void main(String args[])
{
  // Instantiate a large memory using class
  MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192);

  // Do some work
  for ( .............. )
  {
      // Do some processing on myClass
  }

  // Clear reference to myClass
  myClass = null;

  // Continue processing, safe in the knowledge
  // that the garbage collector will reclaim myClass
}

Jika kode Anda akan meminta sejumlah besar memori, Anda mungkin ingin meminta pengumpul sampah memulai ruang reklamasi, daripada membiarkannya melakukannya sebagai utas prioritas rendah. Untuk melakukan ini, tambahkan berikut ini ke kode Anda

System.gc();

Pengumpul sampah akan berusaha untuk mendapatkan kembali ruang kosong, dan aplikasi Anda dapat terus mengeksekusi, dengan sebanyak mungkin memori direklamasi (masalah fragmentasi memori mungkin berlaku pada platform tertentu).

nama tampilan
sumber
1

Dalam kasus saya, karena kode Java saya dimaksudkan untuk porting ke bahasa lain dalam waktu dekat (Terutama C ++), saya setidaknya ingin membayar layanan bibir untuk mengosongkan memori dengan benar sehingga membantu proses porting nanti.

Saya pribadi mengandalkan variabel nulling sebagai pengganti untuk penghapusan yang tepat di masa depan. Sebagai contoh, saya meluangkan waktu untuk membatalkan semua elemen array sebelum benar-benar menghapus (membuat null) array itu sendiri.

Tapi kasus saya sangat khusus, dan saya tahu saya mendapat pukulan kinerja saat melakukan ini.

Oskuro
sumber
1

* "Misalnya, katakan Anda akan mendeklarasikan Daftar di awal metode yang ukurannya menjadi sangat besar, tetapi hanya diperlukan hingga separuh jalan melalui metode. Anda bisa pada titik ini menetapkan referensi Daftar ke nol untuk memungkinkan pemungut sampah untuk berpotensi mengklaim kembali objek ini sebelum metode selesai (dan referensi tetap berada di luar jangkauan). " *

Ini benar, tetapi solusi ini mungkin tidak dapat digeneralisasikan. Saat mengatur referensi objek Daftar ke null-akan- membuat memori tersedia untuk pengumpulan sampah, ini hanya berlaku untuk objek Daftar tipe primitif. Jika objek Daftar bukan berisi tipe referensi, pengaturan objek List = null tidak akan dereference -any- dari tipe referensi yang terkandung -in- daftar. Dalam kasus ini, mengatur Daftar objek = null akan menjadi yatim jenis referensi yang terkandung yang objeknya tidak akan tersedia untuk pengumpulan sampah kecuali algoritma pengumpulan sampah cukup pintar untuk menentukan bahwa objek telah yatim piatu.

Gothri
sumber
1
Ini sebenarnya tidak benar. Pengumpul sampah Jawa cukup pintar untuk menanganinya dengan benar. Jika Anda membatalkan Daftar (dan objek dalam Daftar tidak memiliki referensi lain), GC dapat mengklaim kembali semua objek dalam Daftar. Mungkin memilih untuk tidak melakukan itu pada saat ini, tetapi pada akhirnya akan mendapatkan kembali mereka. Hal yang sama berlaku untuk referensi siklik. Pada dasarnya, cara GC bekerja adalah mencari benda-benda yatim piatu dan kemudian mengambilnya kembali. Ini adalah seluruh pekerjaan GC. Cara Anda menggambarkannya akan membuat GC sama sekali tidak berguna.
Dakkaron
1

Algress java menyediakan pengumpulan sampah otomatis kadang-kadang Anda ingin tahu seberapa besar objek itu dan berapa banyak yang tersisa. Memori bebas menggunakan secara terprogram import java.lang;dan Runtime r=Runtime.getRuntime(); untuk mendapatkan nilai memori yang digunakan mem1=r.freeMemory();untuk membebaskan memori, panggil r.gc();metode dan panggilanfreeMemory()

Benjamin
sumber
1

Rekomendasi dari JAVA adalah menetapkan untuk null

Dari https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

Menetapkan nilai nol ke variabel yang tidak lagi diperlukan secara eksplisit membantu pengumpul sampah untuk mengidentifikasi bagian-bagian memori yang dapat direklamasi dengan aman. Meskipun Java menyediakan manajemen memori, itu tidak mencegah kebocoran memori atau menggunakan jumlah memori yang berlebihan.

Aplikasi dapat menyebabkan kebocoran memori dengan tidak merilis referensi objek. Melakukan hal itu mencegah pengumpul sampah Java dari merebut kembali benda-benda itu, dan menghasilkan peningkatan jumlah memori yang digunakan. Secara eksplisit membatalkan referensi ke variabel setelah penggunaannya memungkinkan pengumpul sampah untuk mendapatkan kembali memori.

Salah satu cara untuk mendeteksi kebocoran memori adalah dengan menggunakan alat profil dan mengambil snapshot memori setelah setiap transaksi. Aplikasi bebas kebocoran dalam kondisi mapan akan menampilkan memori tumpukan aktif yang stabil setelah pengumpulan sampah.

pengguna3869837
sumber