go-runtime/debug

程序包調(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)
}
image.png

執(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)
}
image.png

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)
}
image.png

解釋一下
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é)程,下面是正常的輸出

image.png

接下來我們來限制一下棧的內(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")
}
image.png

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")
}
image.png

我們把程序的組大可使用的線程(不是協(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
}
image.png

我們發(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)
}
image.png

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))
  }
}
image.png

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()
}
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末紫新,一起剝皮案震驚了整個濱河市均蜜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芒率,老刑警劉巖囤耳,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異偶芍,居然都是意外死亡充择,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門匪蟀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來椎麦,“玉大人,你說我怎么就攤上這事材彪」劭妫” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵段化,是天一觀的道長嘁捷。 經(jīng)常有香客問我,道長显熏,這世上最難降的妖魔是什么雄嚣? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮喘蟆,結(jié)果婚禮上缓升,老公的妹妹穿的比我還像新娘夷磕。我一直安慰自己,他們只是感情好仔沿,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布坐桩。 她就那樣靜靜地躺著,像睡著了一般封锉。 火紅的嫁衣襯著肌膚如雪绵跷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天成福,我揣著相機(jī)與錄音碾局,去河邊找鬼。 笑死奴艾,一個胖子當(dāng)著我的面吹牛净当,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蕴潦,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼像啼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了潭苞?” 一聲冷哼從身側(cè)響起忽冻,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎此疹,沒想到半個月后僧诚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡蝗碎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年湖笨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹦骑。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡慈省,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出脊串,到底是詐尸還是另有隱情辫呻,我是刑警寧澤清钥,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布琼锋,位于F島的核電站,受9級特大地震影響祟昭,放射性物質(zhì)發(fā)生泄漏缕坎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一篡悟、第九天 我趴在偏房一處隱蔽的房頂上張望谜叹。 院中可真熱鬧匾寝,春花似錦、人聲如沸荷腊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽女仰。三九已至猜年,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疾忍,已是汗流浹背乔外。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留一罩,地道東北人杨幼。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像聂渊,于是被迫代替她去往敵國和親差购。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容