程序包調(diào)試了包含程序在運行時進(jìn)行調(diào)試功能,本節(jié)就針對api進(jìn)行一一講解
- 1.強(qiáng)制進(jìn)行垃圾回收
- 2.設(shè)置垃圾回收的目標(biāo)百分比
- 3.設(shè)置被單個go協(xié)程調(diào)用椢芷颍可使用的內(nèi)存最大值
- 4.設(shè)置go程序可以使用的最大操作系統(tǒng)線程數(shù)
- 5.設(shè)置程序請求運行是只觸發(fā)panic,而不崩潰
- 6.垃圾收集信息的寫入stats中
- 7.將內(nèi)存分配堆和其中對象的描述寫入文件中
- 8.獲取go協(xié)程調(diào)用棧蹤跡
- 9.將堆棧蹤跡打印到標(biāo)準(zhǔn)錯誤
1.強(qiáng)制進(jìn)行垃圾回收
func FreeOSMemory()
FreeOSMemory強(qiáng)制進(jìn)行一次垃圾收集因妙,以釋放盡量多的內(nèi)存回操作系統(tǒng)诅妹。(即使沒有調(diào)用芜抒,運行時環(huán)境也會在后臺任務(wù)里逐漸將內(nèi)存釋放給系統(tǒng))
package main
import (
"runtime"
"fmt"
"time"
)
func main() {
var dic = new(map[string]string)
runtime.SetFinalizer(dic, func(dic *map[string]string) {
fmt.Println("內(nèi)存回收")
})
time.Sleep(time.Second)
}
執(zhí)行執(zhí)行完畢,dic對象沒有被執(zhí)行回收操作,下面我們調(diào)用這個方法,runtime.SetFinalizer 對象內(nèi)存釋放觸發(fā)這個方法
package main
import (
"runtime"
"fmt"
"time"
"runtime/debug"
)
func main() {
var dic = new(map[string]string)
runtime.SetFinalizer(dic, func(dic *map[string]string) {
fmt.Println("內(nèi)存回收")
})
debug.FreeOSMemory()
time.Sleep(time.Second)
}
2.將堆棧蹤跡打印到標(biāo)準(zhǔn)錯誤
func SetGCPercent(percent int) int
SetGCPercent設(shè)定垃圾收集的目標(biāo)百分比:當(dāng)新申請的內(nèi)存大小占前次垃圾收集剩余可用內(nèi)存大小的比率達(dá)到設(shè)定值時珍策,就會觸發(fā)垃圾收集。SetGCPercent返回之前的設(shè)定宅倒。初始值設(shè)定為環(huán)境變量GOGC的值攘宙;如果沒有設(shè)置該環(huán)境變量,初始值為100拐迁。percent參數(shù)如果是負(fù)數(shù)值蹭劈,會關(guān)閉垃圾收集
package main
import (
"runtime"
"fmt"
"time"
"runtime/debug"
)
func main() {
fmt.Println(debug.SetGCPercent(1))
// 1
var dic = make([]byte,100,100)
runtime.SetFinalizer(&dic, func(dic *[]byte) {
fmt.Println("內(nèi)存回收1")
})
// 立即回收
runtime.GC()
// 2
var s = make([]byte,100,100)
runtime.SetFinalizer(&s, func(dic *[]byte) {
fmt.Println("內(nèi)存回收2")
})
// 3
d := make([]byte,300,300)
for index,_ := range d {
d[index] = 'a'
}
fmt.Println(d)
time.Sleep(time.Second)
}
解釋一下
1處我們創(chuàng)建了一塊內(nèi)存空間100字節(jié),只有我們調(diào)用了runtime.GC()
立即回收了內(nèi)存线召,2處我們又創(chuàng)建了一塊100字節(jié)的內(nèi)存铺韧,等待回收,當(dāng)我們執(zhí)行到3處的時候缓淹,創(chuàng)建了一個300字節(jié)的內(nèi)存,已大于垃圾回收剩余內(nèi)存,所以系統(tǒng)繼續(xù)立即回收內(nèi)存哈打。
3.設(shè)置被單個go協(xié)程調(diào)用椝樱可使用的內(nèi)存最大值
func SetMaxStack(bytes int) int
import (
"fmt"
"time"
)
func main() {
for i:=0;i < 1000;i++{
go print()
}
time.Sleep(time.Second)
}
func print(){
fmt.Println("1")
}
我們在main函數(shù)中使用for循環(huán)啟用了1000個go協(xié)程,下面是正常的輸出
接下來我們來限制一下棧的內(nèi)存
package main
import (
"fmt"
"time"
"runtime/debug"
)
func main() {
debug.SetMaxStack(1)
for i:=0;i < 1000;i++{
go print()
}
time.Sleep(time.Second)
}
func print(){
fmt.Println("1")
}
fmt.Println(debug.SetMaxStack(1))
查看到默認(rèn)系統(tǒng)為1000 000 000 字節(jié)
系統(tǒng)報了一個棧溢出的錯誤,這個方法的主要作用是限制無限遞歸go成帶來的災(zāi)難料仗,默認(rèn)的設(shè)置32位系統(tǒng)是250MB,64位為1GB
4.設(shè)置go程序可以使用的最大操作系統(tǒng)線程數(shù)
func SetMaxThreads(threads int) int
import (
"fmt"
"time"
"runtime/debug"
)
func main() {
debug.SetMaxThreads(1)
go print()
time.Sleep(time.Second)
}
func print(){
fmt.Println("1")
}
我們把程序的組大可使用的線程(不是協(xié)程)數(shù)設(shè)置為1湾盗,如果程序試圖超過這個限制,程序就會崩潰,初始設(shè)置為10000個線程
什么時候會創(chuàng)建新的線程呢?
現(xiàn)有的線程阻塞立轧,cgo或者runtime.LockOSThread函數(shù)阻塞其他go協(xié)程
5.設(shè)置程序請求運行是只觸發(fā)panic,而不崩潰
func SetPanicOnFault(enabled bool) bool
SetPanicOnFault控制程序在不期望(非nil)的地址出錯時的運行時行為格粪。這些錯誤一般是因為運行時內(nèi)存破壞的bug引起的,因此默認(rèn)反應(yīng)是使程序崩潰氛改。使用內(nèi)存映射的文件或進(jìn)行內(nèi)存的不安全操作的程序可能會在非nil的地址出現(xiàn)錯誤帐萎;SetPanicOnFault允許這些程序請求運行時只觸發(fā)一個panic,而不是崩潰平窘。SetPanicOnFault只用于當(dāng)前的go程
package main
import (
"fmt"
"time"
"runtime/debug"
)
func main() {
go print()
time.Sleep(time.Second)
fmt.Println("ddd")
}
func print(){
defer func() {recover()}()
fmt.Println(debug.SetPanicOnFault(true))
var s *int = nil
*s = 34
}
我們發(fā)現(xiàn)指針為nil 發(fā)生了panic 但是我們進(jìn)行了恢復(fù),程序繼續(xù)執(zhí)行
6.垃圾收集信息的寫入stats中
func ReadGCStats(stats *GCStats)
我們看一下CGStats的結(jié)構(gòu)
type GCStats struct {
LastGC time.Time // 最近一次垃圾收集的時間
NumGC int64 // 垃圾收集的次數(shù)
PauseTotal time.Duration // 所有暫停收集垃圾消耗的總時間
Pause []time.Duration // 每次暫停收集垃圾的消耗的時間
PauseQuantiles []time.Duration
}
我們寫一個示例演示一下用法
package main
import (
"fmt"
"runtime/debug"
"runtime"
)
func main() {
data := make([]byte,1000,1000)
println(data)
runtime.GC()
var stats debug.GCStats
debug.ReadGCStats(&stats)
fmt.Println(stats.NumGC)
fmt.Println(stats.LastGC)
fmt.Println(stats.Pause)
fmt.Println(stats.PauseTotal)
fmt.Println(stats.PauseEnd)
}
7.將內(nèi)存分配堆和其中對象的描述寫入文件中
func WriteHeapDump(fd uintptr)
WriteHeapDump將內(nèi)存分配堆和其中對象的描述寫入給定文件描述符fd指定的文件吓肋。
堆轉(zhuǎn)儲格式參見http://golang.org/s/go13heapdump
package main
import (
"runtime/debug"
"runtime"
"os"
)
func main() {
fd,_ := os.OpenFile("/Users/xujie/go/src/awesomeProject/main/log.txt",os.O_RDWR|os.O_CREATE,0666)
debug.WriteHeapDump(fd.Fd())
data := make([]byte,10,10)
println(data)
runtime.GC()
}
8.獲取go協(xié)程調(diào)用棧蹤跡
func Stack() []byte
Stack 返回格式化的go程的調(diào)用棧蹤跡。 對于每一個調(diào)用棧瑰艘,它包括原文件的行信息和PC值是鬼;對go函數(shù)還會嘗試獲取調(diào)用該函數(shù)的函數(shù)或方法,及調(diào)用所在行的文本
package main
import (
"fmt"
"runtime/debug"
"time"
)
func main() {
go print()
time.Sleep(time.Second)
}
func print(){
fmt.Println(string(debug.Stack()))
}
我們可以使用runtime包中的方法查看更相信的內(nèi)容
package main
import (
"time"
"runtime"
"fmt"
)
func main() {
go print()
time.Sleep(time.Second)
}
func print(){
callers := make([]uintptr,100)
n:=runtime.Callers(1,callers)
for _,pc:= range callers[:n]{
funcPc := runtime.FuncForPC(pc)
fmt.Println(funcPc.Name())
fmt.Println(funcPc.FileLine(pc))
}
}
9.將Stack返回信息打印到標(biāo)準(zhǔn)錯誤輸出
func PrintStack()
package main
import (
"time"
"runtime/debug"
)
func main() {
go print()
time.Sleep(time.Second)
}
func print(){
debug.PrintStack()
}