Kebocoran memori Python [tertutup]


180

Saya memiliki skrip yang berjalan lama yang, jika dibiarkan berjalan cukup lama, akan menghabiskan semua memori pada sistem saya.

Tanpa merinci skrip, saya punya dua pertanyaan:

  1. Apakah ada "Praktik Terbaik" untuk diikuti, yang akan membantu mencegah kebocoran terjadi?
  2. Teknik apa yang ada untuk men-debug kebocoran memori di Python?

5
Saya merasa resep ini bermanfaat.
David Schein

Tampaknya terlalu banyak mencetak data yang berguna
Casebash

1
@Casebash: Jika fungsi itu mencetak apa pun yang Anda lakukan dengan serius salah. Ini daftar objek dengan __del__metode yang tidak lagi dirujuk kecuali untuk siklus mereka. Siklus tidak dapat diputus, karena masalah dengan __del__. Memperbaikinya!
Helmut Grohne

Jawaban:



83

Saya mencoba sebagian besar opsi yang disebutkan sebelumnya tetapi menemukan paket kecil dan intuitif ini menjadi yang terbaik: pympler

Cukup mudah untuk melacak objek yang tidak dikumpulkan oleh sampah, periksa contoh kecil ini:

instal paket via pip install pympler

from pympler.tracker import SummaryTracker
tracker = SummaryTracker()

# ... some code you want to investigate ...

tracker.print_diff()

Output menunjukkan kepada Anda semua objek yang telah ditambahkan, ditambah memori yang mereka konsumsi.

Output sampel:

                                 types |   # objects |   total size
====================================== | =========== | ============
                                  list |        1095 |    160.78 KB
                                   str |        1093 |     66.33 KB
                                   int |         120 |      2.81 KB
                                  dict |           3 |       840 B
      frame (codename: create_summary) |           1 |       560 B
          frame (codename: print_diff) |           1 |       480 B

Paket ini menyediakan sejumlah fitur lainnya. Periksa dokumentasi pympler , khususnya bagian Mengidentifikasi kebocoran memori .


5
Perlu dicatat bahwa ini pymplerbisa menjadi PERLAHAN . Jika Anda melakukan sesuatu semi-realtime, itu benar-benar dapat melumpuhkan kinerja aplikasi Anda.
Nama Palsu

@sebpiq anehnya, hal yang sama terjadi pada saya ... apakah Anda tahu mengapa ini terjadi? Pandangan cepat pada kode sumber tidak memberi wawasan nyata.
linusg

25

Biarkan saya merekomendasikan alat mem_top yang saya buat

Itu membantu saya untuk memecahkan masalah serupa

Itu hanya langsung menunjukkan tersangka atas kebocoran memori dalam program Python


1
itu benar ... tetapi itu memberikan sangat sedikit cara penggunaan / hasil penjelasan
me_

@me_, alat ini memiliki bagian "Penggunaan" dan "Menjelaskan hasil". Haruskah saya menambahkan penjelasan seperti "ref adalah jumlah referensi dari objek, tipe adalah jumlah objek dari tipe ini, byte adalah ukuran dari objek" - bukankah terlalu jelas untuk mendokumentasikan ini?
Denis Ryzhkov

dokumen penggunaan alat memberikan satu baris yang mengatakan "dari waktu ke waktu: logging.debug (mem_top ())", sedangkan penjelasannya tentang hasil adalah pengalaman pelacakan kesalahan kehidupan nyata penulis tanpa konteks ... itu bukan spesifikasi teknis yang memberitahu dev persis apa yang mereka lihat ... Saya tidak mengetuk jawaban Anda ... itu menunjukkan tersangka tingkat tinggi sebagai tagihan ... itu tidak memberikan dokumentasi yang memadai untuk sepenuhnya memahami hasil penggunaan ... misalnya , dalam output "Menjelaskan Hasil" mengapa "GearmanJobRequest" jelas merupakan masalah? tidak ada penjelasan mengapa ...
me_

1
Saya kira saya secara tidak sengaja mengetuk alat Anda, Anda adalah penulis ... tidak ada pelanggaran yang dimaksudkan ...
me_

6
@me_, saya baru saja menambahkan langkah selanjutnya ke "Penggunaan", menambahkan bagian "Penghitung", menambahkan penjelasan mengapa sebenarnya Gearman adalah tersangka dalam contoh kehidupan nyata, mendokumentasikan setiap parameter opsional "mem_top ()" dalam kode, dan mengunggah ini semua sebagai v0.1.7 - silakan lihat apakah ada hal lain yang dapat ditingkatkan. Terima kasih! )
Denis Ryzhkov

18

Modul Tracemalloc diintegrasikan sebagai modul bawaan mulai dari Python 3.4, dan tampaknya, itu juga tersedia untuk versi sebelumnya dari Python sebagai perpustakaan pihak ketiga (belum mengujinya).

Modul ini dapat menampilkan file dan jalur yang tepat yang mengalokasikan sebagian besar memori. IMHO, informasi ini jauh lebih berharga daripada jumlah instance yang dialokasikan untuk setiap jenis (yang akhirnya menjadi banyak tuple 99% dari waktu, yang merupakan petunjuk, tetapi hampir tidak membantu dalam kebanyakan kasus).

Saya sarankan Anda menggunakan tracemalloc dalam kombinasi dengan pyrasite . 9 kali dari 10, menjalankan potongan 10 teratas dalam cangkang pyrasite akan memberi Anda informasi dan petunjuk yang cukup untuk memperbaiki kebocoran dalam waktu 10 menit. Namun, jika Anda masih tidak dapat menemukan penyebab kebocorannya, shell-pyrasite yang dikombinasikan dengan alat-alat lain yang disebutkan di utas ini mungkin akan memberi Anda beberapa petunjuk lagi. Anda juga harus melihat pada semua pembantu tambahan yang disediakan oleh pyrasite (seperti penampil memori).



12

Anda harus secara khusus melihat data global atau statis Anda (data yang berumur panjang).

Ketika data ini tumbuh tanpa batasan, Anda juga bisa mendapatkan masalah dengan Python.

Pengumpul sampah hanya dapat mengumpulkan data, yang tidak dirujuk lagi. Tetapi data statis Anda dapat menghubungkan elemen data yang harus dibebaskan.

Masalah lain dapat berupa siklus memori, tetapi setidaknya secara teori pengumpul sampah harus menemukan dan menghilangkan siklus - setidaknya selama mereka tidak terikat pada beberapa data yang hidup lama.

Jenis data umur panjang apa yang secara khusus menyusahkan? Lihatlah daftar dan kamus - mereka dapat tumbuh tanpa batas. Dalam kamus Anda bahkan mungkin tidak melihat masalah yang datang karena ketika Anda mengakses dicts, jumlah kunci dalam kamus mungkin tidak memiliki visibilitas besar kepada Anda ...


7

Untuk mendeteksi dan menemukan kebocoran memori untuk proses yang berjalan lama, misalnya di lingkungan produksi, Anda sekarang dapat menggunakan stackimpact . Ini menggunakan tracemalloc di bawahnya. Info lebih lanjut di posting ini .

masukkan deskripsi gambar di sini


4

Sejauh praktik terbaik, perhatikan fungsi rekursif. Dalam kasus saya, saya mengalami masalah dengan rekursi (di mana tidak perlu). Contoh sederhana dari apa yang saya lakukan:

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    if my_flag:  # restart the function if a certain flag is true
        my_function()

def main():
    my_function()

beroperasi dengan cara rekursif ini tidak akan memicu pengumpulan sampah dan membersihkan sisa-sisa fungsi, sehingga setiap kali melalui penggunaan memori tumbuh dan tumbuh.

Solusi saya adalah dengan menarik panggilan rekursif dari my_function () dan memiliki main () menangani kapan harus memanggilnya lagi. dengan cara ini fungsi berakhir secara alami dan membersihkan sendiri.

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    .....
    return my_flag

def main():
    result = my_function()
    if result:
        my_function()

7
Menggunakan rekursi dengan cara ini juga akan rusak jika Anda menekan batas kedalaman rekursi karena Python tidak mengoptimalkan panggilan ekor. Secara default, ini adalah 1000 panggilan rekursif.
Lie Ryan

3

Tidak yakin tentang "Praktik Terbaik" untuk kebocoran memori di python, tetapi python harus menghapus memori sendiri oleh pengumpul sampah itu. Jadi terutama saya akan mulai dengan memeriksa daftar bundar beberapa pendek, karena mereka tidak akan diambil oleh pengumpul sampah.


3
atau referensi ke objek yang disimpan selamanya, dll.
matt b

3
Bisakah kalian berikan contoh daftar melingkar dan benda yang disimpan selamanya?
Daniel

2

Ini tidak berarti saran lengkap. Tetapi hal pertama yang perlu diingat ketika menulis dengan pemikiran untuk menghindari kebocoran memori masa depan adalah untuk memastikan bahwa apa pun yang menerima referensi untuk panggilan balik, harus menyimpan panggilan kembali itu sebagai referensi yang lemah.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.