老熟女激烈的高潮_日韩一级黄色录像_亚洲1区2区3区视频_精品少妇一区二区三区在线播放_国产欧美日产久久_午夜福利精品导航凹凸

重慶分公司,新征程啟航

為企業提供網站建設、域名注冊、服務器等服務

go語言一個字符占用空間,go語言內存占用

Go 空結構體 struct{} 的使用

struct是Go中的關鍵字,用于定義結構類型。

創新互聯公司主要從事網頁設計、PC網站建設(電腦版網站建設)、wap網站建設(手機版網站建設)、成都響應式網站建設公司、程序開發、網站優化、微網站、微信小程序開發等,憑借多年來在互聯網的打拼,我們在互聯網網站建設行業積累了豐富的成都做網站、網站建設、網站設計、網絡營銷經驗,集策劃、開發、設計、營銷、管理等多方位專業化運作于一體。

例如:

struct {}是一個無元素的結構體類型,通常在沒有信息存儲時使用。優點是大小為0,不需要內存來存儲struct {}類型的值。

struct {} {}是一個復合字面量,它構造了一個struct {}類型的值,該值也是空。

go中可以使用 unsafe.Sizeof 計算出一個數據類型實例需要占用的字節數。我們驗證一下:

也就是說空結構體實例不占用任何內存空間。

Go 語言標準庫沒有提供 Set 的實現,通常使用 map 來代替。事實上,對于集合來說,只需要 map 的鍵,而不需要值。

聲明為聲明為 map[string]struct{} ,由于struct{}是空,不關心內容,這樣map便改造為set 。

map可以通過“comma ok”機制來獲取該key是否存在,例如 _, ok := map["key"] ,如果沒有對應的值,ok為false。可以通過定義成 map[string]struct{} 的形式,值不再占用內存。其值僅有兩種狀態,有或無。如果定義的是 map[string]bool ,則結果有true、false或沒有三種狀態,而且即使是將值設置為 bool 類型,也會多占據 1 個字節。因此呢,將 map 作為集合(Set)使用時,可以將值類型定義為空結構體,僅作為占位符使用即可。

基于channels發送消息有兩個重要方面:發了消息、發了什么消息。一個強調了通訊的發生,一個強調了通訊的內容。當我們更希望強調通訊發生的時刻時,我們將它稱為 消息事件 。有些消息事件并不攜帶額外的信息,它僅僅是用作兩個goroutine之間的同步,這時候我們可以用 struct{} 空結構體作為channels元素的類型。用來通知子協程(goroutine)執行任務,或只用來控制協程并發度。

在部分場景下,結構體只包含方法,不包含任何的字段。這時候我們就可以使用空結構體。

其實,上面的calculateInt 可以是任何類型,如 type calculateInt bool ,但是struct{}不占用任何空間,邏輯上也更合理,因此還是它最好。

Go語言的%d,%p,%v等占位符的使用

這些是死知識,把常用的記住,不常用的直接查表就行了

golang 的fmt 包實現了格式化I/O函數,類似于C的 printf 和 scanf。

type Human struct {

Name string

}

var people = Human{Name:"zhangsan"}

golang沒有 '%u' 點位符,若整數為無符號類型,默認就會被打印成無符號的。

寬度與精度的控制格式以Unicode碼點為單位。寬度為該數值占用區域的最小寬度;精度為小數點之后的位數。

操作數的類型為int時,寬度與精度都可用字符 '*' 表示。

對于 %g/%G 而言,精度為所有數字的總數,例如:123.45,%.4g 會打印123.5,(而 %6.2f 會打印123.45)。

%e 和 %f 的默認精度為6

對大多數的數值類型而言,寬度為輸出的最小字符數,如果必要的話會為已格式化的形式填充空格。

而以字符串類型,精度為輸出的最大字符數,如果必要的話會直接截斷。

使用起來很簡單,一般配合fmt.Printf()使用,因為fmt的Printf()是有格式的輸出,切忌使用Println(),否則將會以字符串的形式輸出。

查看原文: golang fmt格式“占位符”

Go 語言內存管理(三):逃逸分析

Go 語言較之 C 語言一個很大的優勢就是自帶 GC 功能,可 GC 并不是沒有代價的。寫 C 語言的時候,在一個函數內聲明的變量,在函數退出后會自動釋放掉,因為這些變量分配在棧上。如果你期望變量的數據可以在函數退出后仍然能被訪問,就需要調用 malloc 方法在堆上申請內存,如果程序不再需要這塊內存了,再調用 free 方法釋放掉。Go 語言不需要你主動調用 malloc 來分配堆空間,編譯器會自動分析,找出需要 malloc 的變量,使用堆內存。編譯器的這個分析過程就叫做逃逸分析。

所以你在一個函數中通過 dict := make(map[string]int) 創建一個 map 變量,其背后的數據是放在棧空間上還是堆空間上,是不一定的。這要看編譯器分析的結果。

可逃逸分析并不是百分百準確的,它有缺陷。有的時候你會發現有些變量其實在棧空間上分配完全沒問題的,但編譯后程序還是把這些數據放在了堆上。如果你了解 Go 語言編譯器逃逸分析的機制,在寫代碼的時候就可以有意識地繞開這些缺陷,使你的程序更高效。

Go 語言雖然在內存管理方面降低了編程門檻,即使你不了解堆棧也能正常開發,但如果你要在性能上較真的話,還是要掌握這些基礎知識。

這里不對堆內存和棧內存的區別做太多闡述。簡單來說就是, 棧分配廉價,堆分配昂貴。 棧空間會隨著一個函數的結束自動釋放,堆空間需要時間 GC 模塊不斷地跟蹤掃描回收。如果對這兩個概念有些迷糊,建議閱讀下面 2 個文章:

這里舉一個小例子,來對比下堆棧的差別:

stack 函數中的變量 i 在函數退出會自動釋放;而 heap 函數返回的是對變量 i 的引用,也就是說 heap() 退出后,表示變量 i 還要能被訪問,它會自動被分配到堆空間上。

他們編譯出來的代碼如下:

邏輯的復雜度不言而喻,從上面的匯編中可看到, heap() 函數調用了 runtime.newobject() 方法,它會調用 mallocgc 方法從 mcache 上申請內存,申請的內部邏輯前面文章已經講述過。堆內存分配不僅分配上邏輯比棧空間分配復雜,它最致命的是會帶來很大的管理成本,Go 語言要消耗很多的計算資源對其進行標記回收(也就是 GC 成本)。

Go 編輯器會自動幫我們找出需要進行動態分配的變量,它是在編譯時追蹤一個變量的生命周期,如果能確認一個數據只在函數空間內訪問,不會被外部使用,則使用棧空間,否則就要使用堆空間。

我們在 go build 編譯代碼時,可使用 -gcflags '-m' 參數來查看逃逸分析日志。

以上面的兩個函數為例,編譯的日志輸出是:

日志中的 i escapes to heap 表示該變量數據逃逸到了堆上。

需要使用堆空間,所以逃逸,這沒什么可爭議的。但編譯器有時會將 不需要 使用堆空間的變量,也逃逸掉。這里是容易出現性能問題的大坑。網上有很多相關文章,列舉了一些導致逃逸情況,其實總結起來就一句話:

多級間接賦值容易導致逃逸 。

這里的多級間接指的是,對某個引用類對象中的引用類成員進行賦值。Go 語言中的引用類數據類型有 func , interface , slice , map , chan , *Type(指針) 。

記住公式 Data.Field = Value ,如果 Data , Field 都是引用類的數據類型,則會導致 Value 逃逸。這里的等號 = 不單單只賦值,也表示參數傳遞。

根據公式,我們假設一個變量 data 是以下幾種類型,相應的可以得出結論:

下面給出一些實際的例子:

如果變量值是一個函數,函數的參數又是引用類型,則傳遞給它的參數都會逃逸。

上例中 te 的類型是 func(*int) ,屬于引用類型,參數 *int 也是引用類型,則調用 te(j) 形成了為 te 的參數(成員) *int 賦值的現象,即 te.i = j 會導致逃逸。代碼中其他幾種調用都沒有形成 多級間接賦值 情況。

同理,如果函數的參數類型是 slice , map 或 interface{} 都會導致參數逃逸。

匿名函數的調用也是一樣的,它本質上也是一個函數變量。有興趣的可以自己測試一下。

只要使用了 Interface 類型(不是 interafce{} ),那么賦值給它的變量一定會逃逸。因為 interfaceVariable.Method() 先是間接的定位到它的實際值,再調用實際值的同名方法,執行時實際值作為參數傳遞給方法。相當于 interfaceVariable.Method.this = realValue

向 channel 中發送數據,本質上就是為 channel 內部的成員賦值,就像給一個 slice 中的某一項賦值一樣。所以 chan *Type , chan map[Type]Type , chan []Type , chan interface{} 類型都會導致發送到 channel 中的數據逃逸。

這本來也是情理之中的,發送給 channel 的數據是要與其他函數分享的,為了保證發送過去的指針依然可用,只能使用堆分配。

可變參數如 func(arg ...string) 實際與 func(arg []string) 是一樣的,會增加一層訪問路徑。這也是 fmt.Sprintf 總是會使參數逃逸的原因。

例子非常多,這里不能一一列舉,我們只需要記住分析方法就好,即,2 級或更多級的訪問賦值會 容易 導致數據逃逸。這里加上 容易 二字是因為隨著語言的發展,相信這些問題會被慢慢解決,但現階段,這個可以作為我們分析逃逸現象的依據。

下面代碼中包含 2 種很常規的寫法,但他們卻有著很大的性能差距,建議自己想下為什么。

Benchmark 和 pprof 給出的結果:

熟悉堆棧概念可以讓我們更容易看透 Go 程序的性能問題,并進行優化。

多級間接賦值會導致 Go 編譯器出現不必要的逃逸,在一些情況下可能我們只需要修改一下數據結構就會使性能有大幅提升。這也是很多人不推薦在 Go 中使用指針的原因,因為它會增加一級訪問路徑,而 map , slice , interface{} 等類型是不可避免要用到的,為了減少不必要的逃逸,只能拿指針開刀了。

大多數情況下,性能優化都會為程序帶來一定的復雜度。建議實際項目中還是怎么方便怎么寫,功能完成后通過性能分析找到瓶頸所在,再對局部進行優化。

golang內存擴容

一般來說當內存空間span不足時,需要進行擴容。而在擴容前需要將當前沒有剩余空間的內存塊相關狀態解除,以便后續的垃圾回收期能夠進行掃描和回收,接著在從中間部件(central)提取新的內存塊放回數組中。

需要注意由于中間部件有scan和noscan兩種類型,則申請的內存空間最終獲取的可能是其兩倍,并由heap堆進行統一管理。中間部件central是通過兩個鏈表來管理其分配的所有內存塊:

1、empty代表“無法使用”狀態,沒有剩余的空間或被移交給緩存的內存塊

2、noempty代表剩余的空間,并這些內存塊能夠提供服務

由于golang垃圾回收器使用的累增計數器(heap.sweepgen)來表達代齡的:

從上面內容可以看到每次進行清理操作時 該計數器 +2

再來看下mcentral的構成

當通過mcentral進行空間span獲取時,第一步需要到noempty列表檢查剩余空間的內存塊,這里面有一點需要說明主要是垃圾回收器的掃描過程和清理過程是同時進行的,那么為了獲取更多的可用空間,則會在將分配的內存塊移交給cache部件前,先完成清理的操作。第二步當noempty沒有返回時,則需要檢查下empty列表(由于empty里的內存塊有可能已被標記為垃圾,這樣可以直接清理,對應的空間則可直接使用了)。第三步若是noempty和empty都沒有申請到,這時需要堆進行申請內存的

通過上面的源碼也可以看到中間部件central自身擴容操作與大對象內存分配差不多類似。

在golang中將長度小于16bytes的對象稱為微小對象(tiny),最常見的就是小字符串,一般會將這些微小對象組合起來,并用單塊內存存儲,這樣能夠有效的減少內存浪費。

當微小對象需要分配空間span,首先緩存部件會按指定的規格(tiny size class)取出一塊內存,若容量不足,則重新提取一塊;前面也提到會將微小對象進行組合,而這些組合的微小對象是不能包含指針的,因為垃圾回收的原因,一般都是當前存儲單元里所有的微小對象都不可達時,才會將該塊內存進行回收。

而當從緩沖部件cache中獲取空間span時, 是通過偏移位置(tinyoffset)先來判斷剩余空間是否滿足需求。若是可以的話則以此計算并返回內存地址;若是空間不足,則提取新的內存塊,直接返回起始地址便可; 最后在對比新舊兩塊內存,空間大的那塊則會被保留。

Golang 1.14中內存分配、清掃和內存回收

Golang的內存分配是由golang runtime完成,其內存分配方案借鑒自tcmalloc。

主要特點就是

本文中的element指一定大小的內存塊是內存分配的概念,并為出現在golang runtime源碼中

本文講述x8664架構下的內存分配

Golang 內存分配有下面幾個主要結構

Tiny對象是指內存尺寸小于16B的對象,這類對象的分配使用mcache的tiny區域進行分配。當tiny區域空間耗盡時刻,它會從mcache.alloc[tinySpanClass]指向的mspan中找到空閑的區域。當然如果mcache中span空間也耗盡,它會觸發從mcentral補充mspan到mcache的流程。

小對象是指對象尺寸在(16B,32KB]之間的對象,這類對象的分配原則是:

1、首先根據對象尺寸將對象歸為某個SpanClass上,這個SpanClass上所有的element都是一個統一的尺寸。

2、從mcache.alloc[SpanClass]找到mspan,看看有無空閑的element,如果有分配成功。如果沒有繼續。

3、從mcentral.allocSpan[SpanClass]的nonempty和emtpy中找到合適的mspan,返回給mcache。如果沒有找到就進入mcentral.grow()—mheap.alloc()分配新的mspan給mcentral。

大對象指尺寸超出32KB的對象,此時直接從mheap中分配,不會走mcache和mcentral,直接走mheap.alloc()分配一個SpanClass==0 的mspan表示這部分分配空間。

對于程序分配常用的tiny和小對象的分配,可以通過無鎖的mcache提升分配性能。mcache不足時刻會拿mcentral的鎖,然后從mcentral中充mspan 給mcache。大對象直接從mheap 中分配。

在x8664環境上,golang管理的有效的程序虛擬地址空間實質上只有48位。在mheap中有一個pages pageAlloc成員用于管理golang堆內存的地址空間。golang從os中申請地址空間給自己管理,地址空間申請下來以后,golang會將地址空間根據實際使用情況標記為free或者alloc。如果地址空間被分配給mspan或大對象后,那么被標記為alloc,反之就是free。

Golang認為地址空間有以下4種狀態:

Golang同時定義了下面幾個地址空間操作函數:

在mheap結構中,有一個名為pages成員,它用于golang 堆使用虛擬地址空間進行管理。其類型為pageAlloc

pageAlloc 結構表示的golang 堆的所有地址空間。其中最重要的成員有兩個:

在golang的gc流程中會將未使用的對象標記為未使用,但是這些對象所使用的地址空間并未交還給os。地址空間的申請和釋放都是以golang的page為單位(實際以chunk為單位)進行的。sweep的最終結果只是將某個地址空間標記可被分配,并未真正釋放地址空間給os,真正釋放是后文的scavenge過程。

在gc mark結束以后會使用sweep()去嘗試free一個span;在mheap.alloc 申請mspan時刻,也使用sweep去清掃一下。

清掃mspan主要涉及到下面函數

如上節所述,sweep只是將page標記為可分配,但是并未把地址空間釋放;真正的地址空間釋放是scavenge過程。

真正的scavenge是由pageAlloc.scavenge()—sysUnused()將掃描到待釋放的chunk所表示的地址空間釋放掉(使用sysUnused()將地址空間還給os)

golang的scavenge過程有兩種:


當前名稱:go語言一個字符占用空間,go語言內存占用
分享鏈接:http://www.xueling.net.cn/article/hcpgje.html

其他資訊

在線咨詢
服務熱線
服務熱線:028-86922220
TOP
主站蜘蛛池模板: 精品一区二区日韩 | 久久99国产一区二区三区 | 成年人av在线免费观看 | 日本免费一区二区三区最新 | 少妇做爰免费视频网站裸体艺术 | 久久久久青草 | 国产日韩精品一区二区三区在线 | 日韩人成 | av不卡观看 | 国产高潮又爽又刺激的视频 | 91麻豆精品国产91久久久更新资源速度超快 | 亚洲啪啪aⅴ一区二区三区9色 | 伊人操操| 8x福利第一导航 | 久久青青草原AV免费观看 | wwww.黄| 台湾佬中文娱乐久久久 | 国语少妇高潮对白在线 | 日本a视频在线播放 | 成人看片黄A免费看在线 | 亚洲免费av一区二区三区 | 网友自拍露脸国语对白 | 亚洲丰满熟女一区二区v | www.伊人 | 久久夜色精品国产亚洲 | 三级国产三级在线 | A一级片中文字幕 | 四虎永久在线高清国产精品 | 一级肉体全黄裸片免费观看 | yjizz视频| h成年动漫在线看网站 | 超碰在线进入 | 国产精品高清视亚洲一区二区 | 一区二区色| 国产亚洲日韩欧美另类第八页 | 欧美激情精品久久久久久久久久 | 精品亚洲永久免费精品鬼片影片 | 黑人强伦姧人妻久久 | 一极毛片 | 亚洲第一区在线观看 | 亚洲av日韩av激情亚洲 |