<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] # 簡介 * `sync.Pool是一個可以存或取的臨時對象集合` * `sync.Pool可以安全被多個線程同時使用,保證線程安全` * `注意、注意、注意,sync.Pool中保存的任何項都可能隨時不做通知的釋放掉,所以不適合用于像socket長連接或數據庫連接池。` * `sync.Pool主要用途是增加臨時對象的重用率,減少GC負擔` # 關于堆和棧 程序會從操作系統申請一塊內存,而這塊內存也會被分成堆和棧。棧可以簡單得理解成一次函數調用內部申請到的內存,它們會隨著函數的返回把內存還給系統。 ~~~ func F() { temp := make([]int, 0, 20) ... } ~~~ 類似于上面代碼里面的temp變量,只是內函數內部申請的臨時變量,并不會作為返回值返回,它就是被編譯器申請到棧里面。**申請到棧內存好處:函數返回直接釋放,不會引起垃圾回收,對性能沒有影響。** ~~~ func F() []int{ a := make([]int, 0, 20) return a } ~~~ 而上面這段代碼,申請的代碼一模一樣,但是申請后作為返回值返回了,編譯器會認為變量之后還會被使用,當函數返回之后并不會將其內存歸還,那么它就會被申請到堆上面了。**申請到堆上面的內存才會引起垃圾回收。** ~~~ func F() { a := make([]int, 0, 20) b := make([]int, 0, 20000) l := 20 c := make([]int, 0, l) } ~~~ a和b代碼一樣,就是申請的空間不一樣大,但是它們兩個的命運是截然相反的。a前面已經介紹過,會申請到棧上面,而b,由于申請的內存較大,**編譯器會把這種申請內存較大的變量轉移到堆上面。即使是臨時變量,申請過大也會在堆上面申請。** 而c,對我們而言其含義和a是一致的,**但是編譯器對于這種不定長度的申請方式,也會在堆上面申請,即使申請的長度很短。** 實際項目基本都是通過c := make(\[\]int, 0, l)來申請內存,長度都是不確定的。自然而然這些變量都會申請到堆上面了 簡單得說,就是程序要從操作系統申請一塊比較大的內存,內存分成小塊,通過鏈表鏈接。每次程序申請內存,就從鏈表上面遍歷每一小塊,找到符合的就返回其地址,沒有合適的就從操作系統再申請。如果申請內存次數較多,而且申請的大小不固定,就會引起內存碎片化的問題。申請的堆內存并沒有用完,但是用戶申請的內存的時候卻沒有合適的空間提供。這樣會遍歷整個鏈表,還會繼續向操作系統申請內存。這就能解釋我一開始描述的問題,**申請一塊內存變成了慢語句。** **申請內存變成了慢語句,解決方法就是使用臨時對象池** # 臨時對象池 如何解決這個問題,首先想到的就是對象池。Golang在`sync`里面提供了對象池`Pool`。一般大家都叫這個為對象池,而我喜歡叫它臨時對象池。因為每次垃圾回收會把池子里面不被引用的對象回收掉。 > `func (p *Pool) Get() interface{}` 需要注意的是,`Get`方法會把返回的對象從池子里面刪除。所以用完了的對象,還是得重新放回池子 ~~~ package main import ( "fmt" "sync" "time" ) // 一個[]byte的對象池,每個對象為一個[]byte var bytePool = sync.Pool{ New: func() interface{} { b := make([]byte, 1024) return &b }, } func main() { a := time.Now().Unix() // 不使用對象池 for i := 0; i < 1000000000; i++ { obj := make([]byte, 1024) _ = obj } b := time.Now().Unix() // 使用對象池 for i := 0; i < 1000000000; i++ { obj := bytePool.Get().(*[]byte) bytePool.Put(obj) } c := time.Now().Unix() fmt.Println("without pool ", b-a, "s") fmt.Println("with pool ", c-b, "s") } ~~~ 輸出 ~~~ without pool 20 s with pool 15 s ~~~ ~~~ package main import ( "fmt" "sync" ) // 一個[]byte的對象池,每個對象為一個[]byte var bytePool = sync.Pool{ New: func() interface{} { b := make([]byte, 8) return &b }, } func main() { fmt.Printf("%T\n", bytePool) fmt.Printf("%+v\n", bytePool) obj := bytePool.Get().(*[]byte) fmt.Printf("%T\n", obj) fmt.Printf("%v\n", obj) } ~~~ 輸出 ~~~ sync.Pool {noCopy:{} local:<nil> localSize:0 New:0x1090180} *[]uint8 &[0 0 0 0 0 0 0 0] ~~~ # 何時使用pool **只有當每個對象占用內存較大時候,用pool才會改善性能** 對比1(起步階段): ~~~ package main import ( "fmt" "sync" "time" ) // 一個[]byte的對象池,每個對象為一個[]byte var bytePool = sync.Pool{ New: func() interface{} { b := make([]byte, 1) return &b }, } func main() { a := time.Now().Unix() // 不使用對象池 for i := 0; i < 1000000000; i++ { obj := make([]byte, 1) _ = obj } b := time.Now().Unix() // 使用對象池 for i := 0; i < 1000000000; i++ { obj := bytePool.Get().(*[]byte) bytePool.Put(obj) } c := time.Now().Unix() fmt.Println("without pool ", b-a, "s") fmt.Println("with pool ", c-b, "s") } ~~~ 輸出 ~~~ without pool 0 s with pool 17 s ~~~ 可以看到,當\[\]byte只有1個元素時候,用pool性能反而更差 對比2(追趕階段): ~~~ package main import ( "fmt" "sync" "time" ) // 一個[]byte的對象池,每個對象為一個[]byte var bytePool = sync.Pool{ New: func() interface{} { b := make([]byte, 800) return &b }, } func main() { a := time.Now().Unix() // 不使用對象池 for i := 0; i < 1000000000; i++ { obj := make([]byte, 800) _ = obj } b := time.Now().Unix() // 使用對象池 for i := 0; i < 1000000000; i++ { obj := bytePool.Get().(*[]byte) bytePool.Put(obj) } c := time.Now().Unix() fmt.Println("without pool ", b-a, "s") fmt.Println("with pool ", c-b, "s") } ~~~ 輸出 ~~~ without pool 16 s with pool 17 s ~~~ 可以看到,飛機快趕上跑車了 對比3(超越階段): ~~~ package main import ( "fmt" "sync" "time" ) // 一個[]byte的對象池,每個對象為一個[]byte var bytePool = sync.Pool{ New: func() interface{} { b := make([]byte, 8000) return &b }, } func main() { a := time.Now().Unix() // 不使用對象池 for i := 0; i < 1000000000; i++ { obj := make([]byte, 8000) _ = obj } b := time.Now().Unix() // 使用對象池 for i := 0; i < 1000000000; i++ { obj := bytePool.Get().(*[]byte) bytePool.Put(obj) } c := time.Now().Unix() fmt.Println("without pool ", b-a, "s") fmt.Println("with pool ", c-b, "s") } ~~~ 輸出 ~~~ without pool 128 s with pool 17 s ~~~ 可以看到2個特征 1. 當每個對象的內存小于一定量的時候,不使用pool的性能秒殺使用pool;當內存處于某個量的時候,不使用pool和使用pool性能相當;當內存大于某個量的時候,使用pool的優勢就顯現出來了 2. 不使用pool,那么對象占用內存越大,性能下降越厲害;使用pool,無論對象占用內存大還是小,性能都保持不變。可以看到pool有點像飛機,雖然起步比跑車慢,但后勁十足。 即:pool適合占用內存大且并發量大的場景。當內存小并發量少的時候,使用pool適得其反 # 知識點 ~~~ package main import ( "fmt" "sync" ) // 一個[]int的對象池,每個對象為一個[]int var intPool = sync.Pool{ New: func() interface{} { b := make([]int, 8) return &b }, } func main() { // 不使用對象池 for i := 1; i < 3; i++ { obj := make([]int, 8) obj[i] = i fmt.Printf("obj%d: %T %+v\n", i, obj, obj) } fmt.Println("-----------") // 使用對象池 for i := 1; i < 3; i++ { obj := intPool.Get().(*[]int) (*obj)[i] = i fmt.Printf("obj%d: %T %+v\n", i, obj, obj) intPool.Put(obj) } } ~~~ 輸出 ~~~ obj1: []int [0 1 0 0 0 0 0 0] obj2: []int [0 0 2 0 0 0 0 0] ----------- obj1: *[]int &[0 1 0 0 0 0 0 0] obj2: *[]int &[0 1 2 0 0 0 0 0] ~~~ 可以看到,pool的Get和Put真的是從池里獲得和放入池里,否則不會出現Get獲得的變量是舊的變量(即之前通過Put放入的) 如果把上面代碼中的`intPool.Put(obj)`這行刪掉,那么輸出就是 ~~~ obj1: []int [0 1 0 0 0 0 0 0] obj2: []int [0 0 2 0 0 0 0 0] ----------- obj1: *[]int &[0 1 0 0 0 0 0 0] obj2: *[]int &[0 0 2 0 0 0 0 0] ~~~ 1. Pool的目的是緩存已分配但未使用的項目以備后用 2. 多協程并發安全 3. 緩存在Pool里的item會沒有任何通知情況下隨時被移除,以緩解GC壓力 4. 池提供了一種方法來緩解跨多個客戶端的分配開銷。 5. 不是所有場景都適合用Pool,如果釋放鏈表是某個對象的一部分,并由這個對象維護,而這個對象只由一個客戶端使用,在這個客戶端工作完成后釋放鏈表,那么用Pool實現這個釋放鏈表是不合適的。 官方對Pool的目的描述: Pool設計用意是在全局變量里維護的釋放鏈表,尤其是被多個 goroutine 同時訪問的全局變量。使用Pool代替自己寫的釋放鏈表,可以讓程序運行的時候,在恰當的場景下從池里重用某項值。sync.Pool一種合適的方法是,為臨時緩沖區創建一個池,多個客戶端使用這個緩沖區來共享全局資源。另一方面,如果釋放鏈表是某個對象的一部分,并由這個對象維護,而這個對象只由一個客戶端使用,在這個客戶端工作完成后釋放鏈表,那么用Pool實現這個釋放鏈表是不合適的。 **Pool的正確用法** 在Put之前重置,在Get之后重置
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看