Sebuah artikel menerangkan peruntukan memori dalam Go
Hari ini saya akan berkongsi dengan anda beberapa perkara pengetahuan umum tentang pengurusan memori dalam Go.
# 1. Tiga komponen utama memperuntukkan memori
Go Proses memperuntukkan memori terutamanya diuruskan oleh tiga komponen utama iaitu dari atas ke bawah ialah:
mheap
. dalam Apabila program bermula, ia mula-mula akan memohon blok memori yang besar daripada sistem pengendalian dan menyerahkannya kepada pengurusan global struktur
. Bagaimana untuk menguruskannya secara khusus? mheap akan membahagikan sekeping memori yang besar ini kepada blok memori kecil dengan spesifikasi yang berbeza, yang kami panggil mspan Mengikut spesifikasi yang berbeza, terdapat kira-kira 70 jenis mspan boleh dikatakan sangat halus, cukup untuk memenuhi keperluan pelbagai kenangan objek. Jadi spesifikasi mspan ni, besar dan kecil, bercampur-campur, mesti susah nak urus kan?Jadi terdapat komponen peringkat seterusnya mcentral
mcentral
Memulakan program Go akan memulakan banyak mcentral, dan setiap mcentral hanya bertanggungjawab untuk menguruskan mspans spesifikasi tertentu. Setaraf dengan mcentral, yang melaksanakan pengurusan halus mspan berdasarkan mheap. Tetapi mcentral boleh dilihat secara global dalam program Go, jadi setiap kali coroutine datang ke mcentral untuk memohon ingatan, ia perlu dikunci. Boleh dijangkakan bahawa jika setiap coroutine datang ke mcentral untuk memohon ingatan, overhed penguncian dan pelepasan yang kerap akan menjadi sangat besar. 🎜🎜Oleh itu proksi sekunder mcentral diperlukan untuk menahan tekanan ini🎜mcache
Dalam program Go, setiap utasM
akan diikat pada pemprosesP, hanya berbilang pemprosesan boleh dilakukan untuk menjalankan satu goroutine
, setiapP
akan diikat Satu dipanggil mcache
cache setempat. M
会绑定给一个处理器P
,在单一粒度的时间里只能做多处理运行一个goroutine
,每个P
都会绑定一个叫 mcache
的本地缓存。
当需要进行内存分配时,当前运行的goroutine
会从mcache
中查找可用的mspan
。从本地mcache
goroutine
akan bermula daripada Cari tersedia <code style="font-size: inherit;line-height: inherit;overflow-wrap: break-word; padding: 2px dalam mcache
4px ;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(226, 36, 70);latar belakang: rgb(248, 248, 248);">mspan. Daripada localTidak perlu mengunci apabila memperuntukkan memori dalam mcache
. Strategi peruntukan ini lebih cekap. mspan supply chain
Jumlah mspan dalam mcache tidak selalu mencukupi Apabila bekalan melebihi permintaan, mcache akan memohon lebih mspans dari mcentral lagi, jika jumlah mspans dalam mcentral tidak mencukupi akan juga Akan memohon mspan daripada mheap atasannya. Untuk menjadi lebih ekstrem, apakah yang perlu kita lakukan jika mspan dalam mheap tidak dapat memenuhi permintaan memori program? Maka tidak ada cara lain, mheap hanya boleh tanpa segan silu memohon kepada abang sistem operasi.Proses pembekalan di atas hanya terpakai untuk senario di mana blok memori adalah kurang daripada 64KB Sebabnya ialah Go tidak boleh menggunakan cache setempat thread pekerja untuk memperuntukkan bilangan halaman memori yang sepadan (setiap saiz halaman ialah 8KB) ke. program. mcache
和全局中心缓存 mcentral
上管理超过 64KB 的内存分配,所以对于那些超过 64KB 的内存申请,会直接从堆上(mheap
# 2. Apakah memori timbunan dan ingatan tindanan?
Mengikut kaedah pengurusan ingatan (peruntukan dan kitar semula) yang berbeza, ingatan boleh dibahagikan kepadamemori timbunan dan memori tindanan.
Jadi apa bezanya?Timbunan ingatan: Pengalokasi memori dan pengumpul sampah bertanggungjawab untuk mengitar semula
Timbunan ingatan: Diperuntukkan dan dikeluarkan secara automatik oleh pengkompil
Apabila program sedang berjalan, mungkin terdapat berbilang timbunan kenangan, tetapi pasti terdapat banyak timbunan kenangan. Hanya akan ada satu timbunan memori. Setiap memori tindanan diduduki secara bebas oleh benang atau coroutine, jadi mengagihkan memori daripada tindanan tidak memerlukan penguncian, dan memori tindanan akan dikitar semula secara automatik selepas fungsi tamat, dan prestasinya lebih tinggi daripada memori timbunan. Dan bagaimana pula dengan ingatan timbunan? Memandangkan berbilang benang atau coroutine mungkin memohon untuk ingatan daripada timbunan pada masa yang sama, memohon untuk ingatan dalam timbunan memerlukan penguncian untuk mengelakkan konflik, dan ingatan timbunan memerlukan campur tangan GC (pengumpulan sampah) selepas fungsi itu tamat bilangan operasi GC akan merendahkan prestasi program secara serius.# 3. Keperluan analisis melarikan diri
Dapat dilihat bahawa untuk meningkatkan prestasi program, peruntukan memori pada heap harus diminimumkan, yang dapat mengurangkan tekanan pada GC. Dalam menentukan sama ada pembolehubah diperuntukkan memori pada timbunan atau pada timbunan, walaupun pendahulu telah meringkaskan beberapa peraturan, terpulang kepada pengaturcara untuk sentiasa memberi perhatian kepada isu ini semasa pengekodan, dan keperluan untuk pengaturcara Agak tinggi. Nasib baik, pengkompil Go juga membuka fungsi analisis melarikan diri Menggunakan analisis melarikan diri, anda boleh mengesan secara langsung semua pembolehubah yang diperuntukkan pada timbunan oleh pengaturcara anda (fenomena ini dipanggil melarikan diri). Kaedahnya adalah untuk melaksanakan arahan berikutgo build -gcflags '-m -l' demo.go # 或者再加个 -m 查看更详细信息 go build -gcflags '-m -m -l' demo.go
# Peraturan lokasi peruntukan memori
Jika anda menggunakan alat analisis melarikan diri, anda sebenarnya boleh menentukan secara manual pembolehubah yang diperuntukkan pada timbunan.
Jadi apakah peraturan ini?
Selepas ringkasan, terdapat empat situasi seperti berikut
Mengikut skop penggunaan pembolehubah
Mengikut sama ada jenis pembolehubah
- ditentukan kepada yang diduduki saiz pembolehubah
- Berdasarkan Adakah panjang pembolehubah ditentukan Seterusnya, mari analisa dan sahkan satu persatu
Mengikut skop penggunaan pembolehubah
func foo() int {
v := 1024
return v
}
func main() {
m := foo()
fmt.Println(m)
}
Salin selepas log masuk kita boleh lulus go build -gcflags '-m -l' demo.go </code > untuk melihat hasil analisis melarikan diri, di mana<code style="font-size: inherit;line-height: inherit;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin -kanan: 2px ;margin-kiri: 2px;warna: rgb(226, 36, 70);latar belakang: rgb(248, 248, 248);">-m
adalah untuk mencetak maklumat analisis melarikan diri, -l melumpuhkan pengoptimuman sebaris.
Daripada keputusan analisis, kami tidak melihat sebarang arahan melarikan diri tentang pembolehubah v, menunjukkan bahawa ia tidak terlepas dan diperuntukkan pada timbunan. func foo() int { v := 1024 return v } func main() { m := foo() fmt.Println(m) }
$ go build -gcflags '-m -l' demo.go # command-line-arguments ./demo.go:12:13: ... argument does not escape ./demo.go:12:13: m escapes to heap
func foo() *int { v := 1024 return &v } func main() { m := foo() fmt.Println(*m) // 1024 }
go build -gcflags '-m -l' demo.go
来查看逃逸分析的结果,其中 -m
是打印逃逸分析的信息,-l
则是禁止内联优化。从分析的结果我们并没有看到任何关于 v 变量的逃逸说明,说明其并没有逃逸,它是分配在栈上的。
$ go build -gcflags '-m -l' demo.go # command-line-arguments ./demo.go:6:2: moved to heap: v ./demo.go:12:13: ... argument does not escape ./demo.go:12:14: *m escapes to heap
而如果该变量还需要在函数范围之外使用,如果还在栈上分配,那么当函数返回的时候,该变量指向的内存空间就会被回收,程序势必会报错,因此对于这种变量只能在堆上分配。
比如下边这个例子,返回的是指针
func foo() []int { a := []int{1,2,3} return a } func main() { b := foo() fmt.Println(b) }
从逃逸分析的结果中可以看到 moved to heap: v
Melarikan diri daripada Anda boleh lihat dalam hasil analisisdialihkan ke heap: v
, pembolehubah v ialah memori yang diperuntukkan daripada heap , dan Terdapat perbezaan yang jelas antara senario di atas.
$ go build -gcflags '-m -l' demo.go # command-line-arguments ./demo.go:6:2: moved to heap: v ./demo.go:12:13: ... argument does not escape ./demo.go:12:14: *m escapes to heap
除了返回指针之外,还有其他的几种情况也可归为一类:
第一种情况:返回任意引用型的变量:Slice 和 Map
func foo() []int { a := []int{1,2,3} return a } func main() { b := foo() fmt.Println(b) }
逃逸分析结果
$ go build -gcflags '-m -l' demo.go # command-line-arguments ./demo.go:6:12: []int literal escapes to heap ./demo.go:12:13: ... argument does not escape ./demo.go:12:13: b escapes to heap
第二种情况:在闭包函数中使用外部变量
func Increase() func() int { n := 0 return func() int { n++ return n } } func main() { in := Increase() fmt.Println(in()) // 1 fmt.Println(in()) // 2 }
逃逸分析结果
$ go build -gcflags '-m -l' demo.go # command-line-arguments ./demo.go:6:2: moved to heap: n ./demo.go:7:9: func literal escapes to heap ./demo.go:15:13: ... argument does not escape ./demo.go:15:16: in() escapes to heap
根据变量类型是否确定
在上边例子中,也许你发现了,所有编译输出的最后一行中都是 m escapes to heap
。
奇怪了,为什么 m 会逃逸到堆上?
其实就是因为我们调用了 fmt.Println()
函数,它的定义如下
func Println(a ...interface{}) (n int, err error) { return Fprintln(os.Stdout, a...) }
可见其接收的参数类型是 interface{}
,对于这种编译期不能确定其参数的具体类型,编译器会将其分配于堆上。
根据变量的占用大小
最开始的时候,就介绍到,以 64KB 为分界线,我们将内存块分为 小内存块 和 大内存块。
小内存块走常规的 mspan 供应链申请,而大内存块则需要直接向 mheap,在堆区申请。
以下的例子来说明
func foo() { nums1 := make([]int, 8191) // < 64KB for i := 0; i < 8191; i++ { nums1[i] = i } } func bar() { nums2 := make([]int, 8192) // = 64KB for i := 0; i < 8192; i++ { nums2[i] = i } }
给 -gcflags
多加个 -m
可以看到更详细的逃逸分析的结果
$ go build -gcflags '-m -l' demo.go # command-line-arguments ./demo.go:5:15: make([]int, 8191) does not escape ./demo.go:12:15: make([]int, 8192) escapes to heap
那为什么是 64 KB 呢?
我只能说是试出来的 (8191刚好不逃逸,8192刚好逃逸),网上有很多文章千篇一律的说和 ulimit -a
中的 stack size
有关,但经过了解这个值表示的是系统栈的最大限制是 8192 KB,刚好是 8M。
$ ulimit -a -t: cpu time (seconds) unlimited -f: file size (blocks) unlimited -d: data seg size (kbytes) unlimited -s: stack size (kbytes) 8192
我个人实在无法理解这个 8192 (8M) 和 64 KB 是如何对应上的,如果有朋友知道,还请指教一下。
根据变量长度是否确定
由于逃逸分析是在编译期就运行的,而不是在运行时运行的。因此避免有一些不定长的变量可能会很大,而在栈上分配内存失败,Go 会选择把这些变量统一在堆上申请内存,这是一种可以理解的保险的做法。
func foo() { length := 10 arr := make([]int, 0 ,length) // 由于容量是变量,因此不确定,因此在堆上申请 } func bar() { arr := make([]int, 0 ,10) // 由于容量是常量,因此是确定的,因此在栈上申请 }
Atas ialah kandungan terperinci Sebuah artikel menerangkan peruntukan memori dalam Go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



Dalam Go, kitaran hayat fungsi termasuk definisi, pemuatan, pemautan, pemulaan, panggilan dan skop pembolehubah dibahagikan kepada tahap fungsi dan tahap blok Pembolehubah dalam fungsi boleh dilihat secara dalaman, manakala pembolehubah dalam blok hanya kelihatan dalam blok .

Dalam Go, anda boleh menggunakan ungkapan biasa untuk memadankan cap masa: susun rentetan ungkapan biasa, seperti yang digunakan untuk memadankan cap masa ISO8601: ^\d{4}-\d{2}-\d{2}T \d{ 2}:\d{2}:\d{2}(\.\d+)?(Z|[+-][0-9]{2}:[0-9]{2})$ . Gunakan fungsi regexp.MatchString untuk menyemak sama ada rentetan sepadan dengan ungkapan biasa.

Dalam Go, mesej WebSocket boleh dihantar menggunakan pakej gorila/soket web. Langkah khusus: Wujudkan sambungan WebSocket. Hantar mesej teks: Panggil WriteMessage(websocket.TextMessage,[]bait("Mesej")). Hantar mesej binari: panggil WriteMessage(websocket.BinaryMessage,[]bait{1,2,3}).

Bahasa Go dan Go adalah entiti yang berbeza dengan ciri yang berbeza. Go (juga dikenali sebagai Golang) terkenal dengan kesesuaiannya, kelajuan penyusunan pantas, pengurusan memori dan kelebihan merentas platform. Kelemahan bahasa Go termasuk ekosistem yang kurang kaya berbanding bahasa lain, sintaks yang lebih ketat dan kekurangan penaipan dinamik.

Kebocoran memori boleh menyebabkan memori program Go terus meningkat dengan: menutup sumber yang tidak lagi digunakan, seperti fail, sambungan rangkaian dan sambungan pangkalan data. Gunakan rujukan yang lemah untuk mengelakkan kebocoran memori dan objek sasaran untuk pengumpulan sampah apabila ia tidak lagi dirujuk dengan kuat. Menggunakan go coroutine, memori tindanan coroutine akan dikeluarkan secara automatik apabila keluar untuk mengelakkan kebocoran memori.

Lihat dokumentasi fungsi Go menggunakan IDE: Tuding kursor pada nama fungsi. Tekan kekunci pintas (GoLand: Ctrl+Q; VSCode: Selepas memasang GoExtensionPack, F1 dan pilih "Go:ShowDocumentation").

Dalam Golang, pembalut ralat membolehkan anda membuat ralat baharu dengan menambahkan maklumat kontekstual kepada ralat asal. Ini boleh digunakan untuk menyatukan jenis ralat yang dilemparkan oleh perpustakaan atau komponen yang berbeza, memudahkan penyahpepijatan dan pengendalian ralat. Langkah-langkahnya adalah seperti berikut: Gunakan fungsi ralat. Balut untuk membalut ralat asal kepada ralat baharu. Ralat baharu mengandungi maklumat kontekstual daripada ralat asal. Gunakan fmt.Printf untuk mengeluarkan ralat yang dibalut, memberikan lebih konteks dan kebolehtindakan. Apabila mengendalikan pelbagai jenis ralat, gunakan fungsi ralat. Balut untuk menyatukan jenis ralat.

Unit menguji fungsi serentak adalah penting kerana ini membantu memastikan kelakuan mereka yang betul dalam persekitaran serentak. Prinsip asas seperti pengecualian bersama, penyegerakan dan pengasingan mesti dipertimbangkan semasa menguji fungsi serentak. Fungsi serentak boleh diuji unit dengan mensimulasikan, menguji keadaan perlumbaan dan mengesahkan keputusan.
