簡(jiǎn)單來(lái)說(shuō), SetMaxHeap 提供了一種可以設(shè)置固定觸發(fā)閾值的 GC (Garbage Collection垃圾回收)方式
專(zhuān)注于為中小企業(yè)提供網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)平和免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了成百上千企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
官方源碼鏈接
大量臨時(shí)對(duì)象分配導(dǎo)致的 GC 觸發(fā)頻率過(guò)高, GC 后實(shí)際存活的對(duì)象較少,
或者機(jī)器內(nèi)存較充足,希望使用剩余內(nèi)存,降低 GC 頻率的場(chǎng)景
GC 會(huì) STW ( Stop The World ),對(duì)于時(shí)延敏感場(chǎng)景,在一個(gè)周期內(nèi)連續(xù)觸發(fā)兩輪 GC ,那么 STW 和 GC 占用的 CPU 資源都會(huì)造成很大的影響, SetMaxHeap 并不一定是完美的,在某些場(chǎng)景下做了些權(quán)衡,官方也在進(jìn)行相關(guān)的實(shí)驗(yàn),當(dāng)前方案仍沒(méi)有合入主版本。
先看下如果沒(méi)有 SetMaxHeap ,對(duì)于如上所述的場(chǎng)景的解決方案
這里簡(jiǎn)單說(shuō)下 GC 的幾個(gè)值的含義,可通過(guò) GODEBUG=gctrace=1 獲得如下數(shù)據(jù)
這里只關(guān)注 128-132-67 MB 135 MB goal ,
分別為 GC開(kāi)始時(shí)內(nèi)存使用量 - GC標(biāo)記完成時(shí)內(nèi)存使用量 - GC標(biāo)記完成時(shí)的存活內(nèi)存量 本輪GC標(biāo)記完成時(shí)的 預(yù)期 內(nèi)存使用量(上一輪 GC 完成時(shí)確定)
引用 GC peace設(shè)計(jì)文檔 中的一張圖來(lái)說(shuō)明
對(duì)應(yīng)關(guān)系如下:
簡(jiǎn)單說(shuō)下 GC pacing (信用機(jī)制)
GC pacing 有兩個(gè)目標(biāo),
那么當(dāng)一輪 GC 完成時(shí),如何只根據(jù)本輪 GC 存活量去實(shí)現(xiàn)這兩個(gè)小目標(biāo)呢?
這里實(shí)際是根據(jù)當(dāng)前的一些數(shù)據(jù)或狀態(tài)去 預(yù)估 “未來(lái)”,所有會(huì)存在些誤差
首先確定 gc Goal goal = memstats.heap_marked + memstats.heap_marked*uint64(gcpercent)/100
heap_marked 為本輪 GC 存活量, gcpercent 默認(rèn)為 100 ,可以通過(guò)環(huán)境變量 GOGC=100 或者 debug.SetGCPercent(100) 來(lái)設(shè)置
那么默認(rèn)情況下 goal = 2 * heap_marked
gc_trigger 是與 goal 相關(guān)的一個(gè)值( gc_trigger 大約為 goal 的 90% 左右),每輪 GC 標(biāo)記完成時(shí),會(huì)根據(jù) |Ha-Hg| 和實(shí)際使用的 cpu 資源 動(dòng)態(tài)調(diào)整 gc_trigger 與 goal 的差值
goal 與 gc_trigger 的差值即為,為 GC 期間分配的對(duì)象所預(yù)留的空間
GC pacing 還會(huì)預(yù)估下一輪 GC 發(fā)生時(shí),需要掃描對(duì)象對(duì)象的總量,進(jìn)而換算為下一輪 GC 所需的工作量,進(jìn)而計(jì)算出 mark assist 的值
本輪 GC 觸發(fā)( gc_trigger ),到本輪的 goal 期間,需要盡力完成 GC mark 標(biāo)記操作,所以當(dāng) GC 期間,某個(gè) goroutine 分配大量?jī)?nèi)存時(shí),就會(huì)被拉去做 mark assist 工作,先進(jìn)行 GC mark 標(biāo)記賺取足夠的信用值后,才能分配對(duì)應(yīng)大小的對(duì)象
根據(jù)本輪 GC 存活的內(nèi)存量( heap_marked )和下一輪 GC 觸發(fā)的閾值( gc_trigger )計(jì)算 sweep assist 的值,本輪 GC 完成,到下一輪 GC 觸發(fā)( gc_trigger )時(shí),需要盡力完成 sweep 清掃操作
預(yù)估下一輪 GC 所需的工作量的方式如下:
繼續(xù)分析文章開(kāi)頭的問(wèn)題,如何充分利用剩余內(nèi)存,降低 GC 頻率和 GC 對(duì) CPU 的資源消耗
如上圖可以看出, GC 后,存活的對(duì)象為 2GB 左右,如果將 gcpercent 設(shè)置為 400 ,那么就可以將下一輪 GC 觸發(fā)閾值提升到 10GB 左右
前面一輪看起來(lái)很好,提升了 GC 觸發(fā)的閾值到 10GB ,但是如果某一輪 GC 后的存活對(duì)象到達(dá) 2.5GB 的時(shí)候,那么下一輪 GC 觸發(fā)的閾值,將會(huì)超過(guò)內(nèi)存閾值,造成 OOM ( Out of Memory ),進(jìn)而導(dǎo)致程序崩潰。
可以通過(guò) GOGC=off 或者 debug.SetGCPercent(-1) 來(lái)關(guān)閉 GC
可以通過(guò)進(jìn)程外監(jiān)控內(nèi)存使用狀態(tài),使用信號(hào)觸發(fā)的方式通知程序,或 ReadMemStats 、或 linkname runtime.heapRetained 等方式進(jìn)行堆內(nèi)存使用的監(jiān)測(cè)
可以通過(guò)調(diào)用 runtime.GC() 或者 debug.FreeOSMemory() 來(lái)手動(dòng)進(jìn)行 GC 。
這里還需要說(shuō)幾個(gè)事情來(lái)解釋這個(gè)方案所存在的問(wèn)題
通過(guò) GOGC=off 或者 debug.SetGCPercent(-1) 是如何關(guān)閉 GC 的?
gc 4 @1.006s 0%: 0.033+5.6+0.024 ms clock, 0.27+4.4/11/25+0.19 ms cpu, 428-428-16 MB, 17592186044415 MB goal, 8 P (forced)
通過(guò) GC trace 可以看出,上面所說(shuō)的 goal 變成了一個(gè)很詭異的值 17592186044415
實(shí)際上關(guān)閉 GC 后, Go 會(huì)將 goal 設(shè)置為一個(gè)極大值 ^uint64(0) ,那么對(duì)應(yīng)的 GC 觸發(fā)閾值也被調(diào)成了一個(gè)極大值,這種處理方式看起來(lái)也沒(méi)什么問(wèn)題,將閾值調(diào)大,預(yù)期永遠(yuǎn)不會(huì)再觸發(fā) GC
那么如果在關(guān)閉 GC 的情況下,手動(dòng)調(diào)用 runtime.GC() 會(huì)導(dǎo)致什么呢?
由于 goal 和 gc_trigger 被設(shè)置成了極大值, mark assist 和 sweep assist 也會(huì)按照這個(gè)錯(cuò)誤的值去計(jì)算,導(dǎo)致工作量預(yù)估錯(cuò)誤,這一點(diǎn)可以從 trace 中進(jìn)行證明
可以看到很詭異的 trace 圖,這里不做深究,該方案與 GC pacing 信用機(jī)制不兼容
記住,不要在關(guān)閉 GC 的情況下手動(dòng)觸發(fā) GC ,至少在當(dāng)前 Go1.14 版本中仍存在這個(gè)問(wèn)題
SetMaxHeap 的實(shí)現(xiàn)原理,簡(jiǎn)單來(lái)說(shuō)是強(qiáng)行控制了 goal 的值
注: SetMaxHeap ,本質(zhì)上是一個(gè)軟限制,并不能解決 極端場(chǎng)景 下的 OOM ,可以配合內(nèi)存監(jiān)控和 debug.FreeOSMemory() 使用
SetMaxHeap 控制的是堆內(nèi)存大小, Go 中除了堆內(nèi)存還分配了如下內(nèi)存,所以實(shí)際使用過(guò)程中,與實(shí)際硬件內(nèi)存閾值之間需要留有一部分余量。
對(duì)于文章開(kāi)始所述問(wèn)題,使用 SetMaxHeap 后,預(yù)期的 GC 過(guò)程大概是這個(gè)樣子
簡(jiǎn)單用法1
該方法簡(jiǎn)單粗暴,直接將 goal 設(shè)置為了固定值
注:通過(guò)上文所講,觸發(fā) GC 實(shí)際上是 gc_trigger ,所以當(dāng)閾值設(shè)置為 12GB 時(shí),會(huì)提前一點(diǎn)觸發(fā) GC ,這里為了描述方便,近似認(rèn)為 gc_trigger=goal
簡(jiǎn)單用法2
當(dāng)不關(guān)閉 GC 時(shí), SetMaxHeap 的邏輯是, goal 仍按照 gcpercent 進(jìn)行計(jì)算,當(dāng) goal 小于 SetMaxHeap 閾值時(shí)不進(jìn)行處理;當(dāng) goal 大于 SetMaxHeap 閾值時(shí),將 goal 限制為 SetMaxHeap 閾值
注:通過(guò)上文所講,觸發(fā) GC 實(shí)際上是 gc_trigger ,所以當(dāng)閾值設(shè)置為 12GB 時(shí),會(huì)提前一點(diǎn)觸發(fā) GC ,這里為了描述方便,近似認(rèn)為 gc_trigger=goal
切換到 go1.14 分支,作者選擇了 git checkout go1.14.5
選擇官方提供的 cherry-pick 方式(可能需要梯子,文件改動(dòng)不多,我后面會(huì)列出具體改動(dòng))
git fetch "" refs/changes/67/227767/3 git cherry-pick FETCH_HEAD
需要重新編譯Go源碼
注意點(diǎn):
下面源碼中的官方注釋說(shuō)的比較清楚,在一些關(guān)鍵位置加入了中文注釋
入?yún)ytes為要設(shè)置的閾值
notify 簡(jiǎn)單理解為 GC 的策略 發(fā)生變化時(shí)會(huì)向 channel 發(fā)送通知,后續(xù)源碼可以看出“策略”具體指哪些內(nèi)容
返回值為本次設(shè)置之前的 MaxHeap 值
$GOROOT/src/runtime/debug/garbage.go
$GOROOT/src/runtime/mgc.go
注:作者盡量用通俗易懂的語(yǔ)言去解釋 Go 的一些機(jī)制和 SetMaxHeap 功能,可能有些描述與實(shí)現(xiàn)細(xì)節(jié)不完全一致,如有錯(cuò)誤還請(qǐng)指出
struct是Go中的關(guān)鍵字,用于定義結(jié)構(gòu)類(lèi)型。
例如:
struct {}是一個(gè)無(wú)元素的結(jié)構(gòu)體類(lèi)型,通常在沒(méi)有信息存儲(chǔ)時(shí)使用。優(yōu)點(diǎn)是大小為0,不需要內(nèi)存來(lái)存儲(chǔ)struct {}類(lèi)型的值。
struct {} {}是一個(gè)復(fù)合字面量,它構(gòu)造了一個(gè)struct {}類(lèi)型的值,該值也是空。
go中可以使用 unsafe.Sizeof 計(jì)算出一個(gè)數(shù)據(jù)類(lèi)型實(shí)例需要占用的字節(jié)數(shù)。我們驗(yàn)證一下:
也就是說(shuō)空結(jié)構(gòu)體實(shí)例不占用任何內(nèi)存空間。
Go 語(yǔ)言標(biāo)準(zhǔn)庫(kù)沒(méi)有提供 Set 的實(shí)現(xiàn),通常使用 map 來(lái)代替。事實(shí)上,對(duì)于集合來(lái)說(shuō),只需要 map 的鍵,而不需要值。
聲明為聲明為 map[string]struct{} ,由于struct{}是空,不關(guān)心內(nèi)容,這樣map便改造為set 。
map可以通過(guò)“comma ok”機(jī)制來(lái)獲取該key是否存在,例如 _, ok := map["key"] ,如果沒(méi)有對(duì)應(yīng)的值,ok為false??梢酝ㄟ^(guò)定義成 map[string]struct{} 的形式,值不再占用內(nèi)存。其值僅有兩種狀態(tài),有或無(wú)。如果定義的是 map[string]bool ,則結(jié)果有true、false或沒(méi)有三種狀態(tài),而且即使是將值設(shè)置為 bool 類(lèi)型,也會(huì)多占據(jù) 1 個(gè)字節(jié)。因此呢,將 map 作為集合(Set)使用時(shí),可以將值類(lèi)型定義為空結(jié)構(gòu)體,僅作為占位符使用即可。
基于channels發(fā)送消息有兩個(gè)重要方面:發(fā)了消息、發(fā)了什么消息。一個(gè)強(qiáng)調(diào)了通訊的發(fā)生,一個(gè)強(qiáng)調(diào)了通訊的內(nèi)容。當(dāng)我們更希望強(qiáng)調(diào)通訊發(fā)生的時(shí)刻時(shí),我們將它稱(chēng)為 消息事件 。有些消息事件并不攜帶額外的信息,它僅僅是用作兩個(gè)goroutine之間的同步,這時(shí)候我們可以用 struct{} 空結(jié)構(gòu)體作為channels元素的類(lèi)型。用來(lái)通知子協(xié)程(goroutine)執(zhí)行任務(wù),或只用來(lái)控制協(xié)程并發(fā)度。
在部分場(chǎng)景下,結(jié)構(gòu)體只包含方法,不包含任何的字段。這時(shí)候我們就可以使用空結(jié)構(gòu)體。
其實(shí),上面的calculateInt 可以是任何類(lèi)型,如 type calculateInt bool ,但是struct{}不占用任何空間,邏輯上也更合理,因此還是它最好。
在使用Go語(yǔ)言的時(shí)候,碰到了需要對(duì)數(shù)組進(jìn)行去重操作的問(wèn)題。Java語(yǔ)言有Set集合這個(gè)數(shù)據(jù)結(jié)構(gòu),可以很方便的將數(shù)組轉(zhuǎn)為集合,但是Go語(yǔ)言沒(méi)有Set,如果僅僅是因?yàn)槿ブ夭僮骶褪謩?dòng)實(shí)現(xiàn)一個(gè)Set太繁瑣了。可以根據(jù)Go語(yǔ)言中的map的特性來(lái)簡(jiǎn)單實(shí)現(xiàn)一下
這個(gè)是對(duì)字符串?dāng)?shù)組去重操作。可以根據(jù)需要的類(lèi)型稍作修改即可。需要注意的是只支持可以作為map鍵的結(jié)構(gòu)進(jìn)行去重!
1、 string的定義
Golang中的string的定義在reflect包下的value.go中,定義如下:
StringHeader 是字符串的運(yùn)行時(shí)表示,其中包含了兩個(gè)字段,分別是指向數(shù)據(jù)數(shù)組的指針和數(shù)組的長(zhǎng)度。
// StringHeader is the runtime representation of a string.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
type StringHeader struct {
Data uintptr
Len int
}
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
2、string不可變
Golang中的字符串是不可變的,不能通過(guò)索引下標(biāo)的方式修改字符串中的數(shù)據(jù):
在這里插入圖片描述
運(yùn)行代碼,可以看到編譯器報(bào)錯(cuò),string是不可變的
在這里插入圖片描述
但是能不能進(jìn)行一些騷操作來(lái)改變?cè)氐闹的兀?/p>
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
a := "hello,world"
b := a[6:]
bptr := (*reflect.StringHeader) (unsafe.Pointer(b))
fmt.Println(a)
fmt.Println(b)
*(*byte)(unsafe.Pointer(bptr.Data)) = '.'
fmt.Println(a)
fmt.Println(b)
}
// 運(yùn)行結(jié)果
hello,world
world
unexpected fault address 0x49d7e3
fatal error: fault
[signal 0xc0000005 code=0x1 addr=0x49d7e3 pc=0x4779fa]
goroutine 1 [running]:
runtime.throw(0x49c948, 0x5)
C:/Program Files/Go/src/runtime/panic.go:1117 +0x79 fp=0xc0000dbe90 sp=0xc0000dbe60 pc=0x405fd9
runtime.sigpanic()
C:/Program Files/Go/src/runtime/signal_windows.go:245 +0x2d6 fp=0xc0000dbee8 sp=0xc0000dbe90 pc=0x4189f6
main.main()
F:/go_workspace/src/code/string_test/main.go:20 +0x13a fp=0xc0000dbf88 sp=0xc0000dbee8 pc=0x4779fa
runtime.main()
C:/Program Files/Go/src/runtime/proc.go:225 +0x256 fp=0xc0000dbfe0 sp=0xc0000dbf88 pc=0x4087f6
runtime.goexit()
C:/Program Files/Go/src/runtime/asm_amd64.s:1371 +0x1 fp=0xc0000dbfe8 sp=0xc0000dbfe0 pc=0x435da1
Process finished with the exit code 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
在上面的代碼中,因?yàn)樵趃o語(yǔ)言中不能進(jìn)行指針的加減運(yùn)算,因此取切片,讓b的Data指針指向’,'所在的位置。然后把"hello,world"中的逗號(hào)改為點(diǎn),但是發(fā)現(xiàn)還是不行,程序直接崩潰了??磥?lái)go語(yǔ)言中的指針得到了大大的限制,設(shè)計(jì)者并不想讓程序員過(guò)度使用指針來(lái)寫(xiě)出一些不安全的代碼。
3、使用string給另一個(gè)string賦值
Golang中的字符串的賦值并不是拷貝底層的字符串?dāng)?shù)組,而是數(shù)組指針和長(zhǎng)度字段的拷貝。例如:當(dāng)我們定義了一個(gè)字符串 a := “hello,world” 然后定義了 b := a 底層所做的操作只是創(chuàng)建了兩個(gè)StringHeader的結(jié)構(gòu)體,它們的Data字段都指向同一段數(shù)據(jù),如下圖:
在這里插入圖片描述
我們可以利用代碼來(lái)證實(shí)這一點(diǎn):
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
a := "hello,world"
b := a
fmt.Println(a)
fmt.Println(b)
aptr := (*reflect.StringHeader) (unsafe.Pointer(a))
bptr := (*reflect.StringHeader) (unsafe.Pointer(b))
fmt.Println("a ptr:", unsafe.Pointer(aptr.Data))
fmt.Println("b ptr:", unsafe.Pointer(bptr.Data))
}
// 運(yùn)行結(jié)果
hello, world
hello, world
a ptr: 0x6bdb76
b ptr: 0x6bdb76
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
在上面的代碼中,將a和b轉(zhuǎn)換為StringHeader類(lèi)型的指針,然后分別打印出,a和b的Data指針的值,發(fā)現(xiàn)是相同的
那么如果對(duì)a做切片賦值給b呢?
func main() {
a := "hello,world"
b := a[6:]
fmt.Println(a)
fmt.Println(b)
aptr := (*reflect.StringHeader) (unsafe.Pointer(a))
bptr := (*reflect.StringHeader) (unsafe.Pointer(b))
fmt.Println("a ptr:", unsafe.Pointer(aptr.Data))
fmt.Println("b ptr:", unsafe.Pointer(bptr.Data))
}
// 運(yùn)行結(jié)果
hello,world
world
a ptr: 0xd4d849
b ptr: 0xd4d84f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0xd4d849 - 0xd4d84f = 0x000006
顯然,也沒(méi)有分配新的數(shù)組并拷貝數(shù)據(jù),而是將原字符數(shù)組的指針的偏移賦給了b的StringHeader的Data
4、string重新賦值
如果對(duì)一個(gè)已經(jīng)賦值的字符串重新賦值,也不會(huì)修改原內(nèi)存空間,而是申請(qǐng)了新的內(nèi)存空間,對(duì)其賦值,并指向新的內(nèi)存空間。如下圖:
在這里插入圖片描述
也可以使用代碼來(lái)證實(shí)一下:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
a := "hello,world"
aptr := (*reflect.StringHeader) (unsafe.Pointer(a))
fmt.Println("a ptr:", unsafe.Pointer(aptr.Data))
fmt.Println("a len", aptr.Len)
a = "hello,golang"
newAPtr := (*reflect.StringHeader) (unsafe.Pointer(a))
fmt.Println("b ptr:", unsafe.Pointer(newAPtr.Data))
fmt.Println("b len:", newAPtr.Len)
}
// 運(yùn)行結(jié)果
a ptr: 0x3ed7f4
a len 11
b ptr: 0x3edb2c
b len: 12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
文章知識(shí)點(diǎn)與官方知識(shí)檔案匹配
Go技能樹(shù)九陰真經(jīng)字符串
2043 人正在系統(tǒng)學(xué)習(xí)中
點(diǎn)擊閱讀全文
打開(kāi)CSDN APP,看更多技術(shù)內(nèi)容
Golang底層原理剖析之string類(lèi)型與字符編碼_cheems~的博客_g...
string類(lèi)型 string結(jié)構(gòu) go語(yǔ)言中默認(rèn)使用的是UTF-8編碼 string由兩部分組成,一部分是指向字符串起始地址的指針,另一部分是字節(jié)個(gè)數(shù)len注意不是字符個(gè)數(shù),是字節(jié)個(gè)數(shù)!這個(gè)數(shù)據(jù)類(lèi)型占用16B空間,指向字符串起始地址的指針和存在字節(jié)個(gè)數(shù)的整...
繼續(xù)訪(fǎng)問(wèn)
...底層系列二(基礎(chǔ))_GoGo在努力的博客_golang string底層
1、 string的定義 Golang中的string的定義在reflect包下的value.go中,定義如下: StringHeader 是字符串的運(yùn)行時(shí)表示,其中包含了兩個(gè)字段,分別是指向數(shù)據(jù)數(shù)組的指針和數(shù)組的長(zhǎng)度。 // StringHeader is the runtime representation of a...
繼續(xù)訪(fǎng)問(wèn)
最新發(fā)布 Golang: []string 和 ...string
… 參數(shù)語(yǔ)法形成了可變參數(shù)的參數(shù)。它將接受零個(gè)或多個(gè)string參數(shù),并將它們作為切片引用
繼續(xù)訪(fǎng)問(wèn)
常用的Golang字符串(string)操作
文章目錄Golang中常用的字符串操作一、標(biāo)準(zhǔn)庫(kù)相關(guān)的Package二、常用字符串操作判斷是否為空字符串 正文 Golang中常用的字符串操作 一、標(biāo)準(zhǔn)庫(kù)相關(guān)的Package 二、常用字符串操作 判斷是否為空字符串 思路:直接判斷是否等于""空字符串,由于golang中字符串不能為nil,且為值類(lèi)型,所以直接與空字符串比較即可 舉例: str := "" if str == ""{...
繼續(xù)訪(fǎng)問(wèn)
golang中的string_xiaodongdonga的博客_golang ...string
golang中的string 在golang中,string其實(shí)是個(gè)結(jié)構(gòu)體,如圖 ,在這個(gè)結(jié)構(gòu)體中有兩個(gè)值,一個(gè)是指向字符數(shù)組的指針str,另一個(gè)是這個(gè)字符串的長(zhǎng)度len。 另外在golang中用的是UFT8邊長(zhǎng)編碼,里面字母是用一個(gè)字節(jié),西方的文字使用兩個(gè)字節(jié),...
繼續(xù)訪(fǎng)問(wèn)
Golang中對(duì)字符串string的相關(guān)操作_Codex_97的博客_go 字符...
golang中擁有string的內(nèi)置類(lèi)型,可以使用與普通slice類(lèi)型相似的性質(zhì)。 1.簡(jiǎn)介 strings包提供了很多操作字符串的簡(jiǎn)單函數(shù),通常一般的字符串操作都可以在這個(gè)包中找到。 strconv 包提供了基本數(shù)據(jù)類(lèi)型和字符串之間的轉(zhuǎn)換。在Go 中,沒(méi)有隱式...
繼續(xù)訪(fǎng)問(wèn)
go-string-set:一種在GoLang中創(chuàng)建字符串集的方法
概述 通過(guò)字符串設(shè)置方法。 用法 go get github.com/wojnosystems/go-string-set package main import ( "github.com/wojnosystems/go-string-set/string_set" "strings" ) func main () { myPeople := string_set . New () myPeople . Add ( "Bob" ) myPeople . Add ( "Jane" ) myPeople . Add ( "Gary" ) myPeople . Add ( "Bob" ) if myPeople . Exists ( "Bob" ) { // Bob exists in myPeople, so this code block execute
Golang:strings包和字符串操作
strings包 string類(lèi)型值是不可變的,如果想要獲得一個(gè)不一樣的字符串,就只能基于原字符串進(jìn)行剪裁,拼接等操作,然后將得到的新字符串存放到一塊聯(lián)系內(nèi)存中。 string值包含了指向底層字節(jié)數(shù)組頭部的指針值,以及該字節(jié)數(shù)組的長(zhǎng)度。在string值上做切片,就相當(dāng)于在其底層字節(jié)數(shù)組做切片。 而字符串拼接時(shí)(使用+),會(huì)把所有被拼接的字符串依次拷貝到一個(gè)嶄新且足夠大的連續(xù)內(nèi)存空間中,并把持有新...
繼續(xù)訪(fǎng)問(wèn)
golang中的strings.SplitN_盼盼編程的博客_strings.splitn
golang中的字符串操作strings.SplitN package main import ( "fmt" "strings" ) //golang字符串操作 func main(){ s := "hello world hello world" //str := "wo" //以str為分隔符,將s切分成多個(gè)子串,結(jié)果中**不包含*...
繼續(xù)訪(fǎng)問(wèn)
Golang_String常用操作_itzhuzhu.的博客_golang strings
func main() { var x string = "ITzhuzhu" var y string = "itzhuzhu" fmt.Println(strings.EqualFold(x, y)) //true fmt.Println(x == y) //false } 1 2 3 4 5 6 返回子串在字符串第一次出現(xiàn)的索引,如果沒(méi)有則...
繼續(xù)訪(fǎng)問(wèn)
Golang類(lèi)型的String()方法
作用 用于定制fmt.Println(x)、fmt.Printf("%v", x)、fmt.Print(x)時(shí)輸出的內(nèi)容。 示例 package main import "fmt" type Couple struct { Husband string Wife string } func (self Couple) String() string { return "(husband:" + self.Husband + ", wife:" + self.Wife +
繼續(xù)訪(fǎng)問(wèn)
golang的string
golang中的string是不可變的字節(jié)序列,零值是空字符串,默認(rèn)是UTF-8編碼。 golang中使用字符串最需要注意的是,golang中的字符串是字節(jié)序列,string == []byte,由于一個(gè)字符占用的字節(jié)數(shù)不確定,所以無(wú)法通過(guò)下標(biāo)[i]的方式穩(wěn)定地獲取對(duì)應(yīng)位置的字符。 也就意味著字符串的第i個(gè)字節(jié),不一定就是第i個(gè)字符,原因請(qǐng)參考 unicode與UTF-8 文字符號(hào)在golang中被稱(chēng)為rune,發(fā)音為/ru:n/,意思是符號(hào)。由于utf-8最多使用4個(gè)字節(jié)來(lái)編碼,所以rune類(lèi)型是int3
繼續(xù)訪(fǎng)問(wèn)
golang中的strings.Trim_盼盼編程的博客_strings.trim
golang中的字符串操作strings.Trim package main import ( "fmt" "strings" ) //golang字符串操作 func main(){ s := "Hello world hello world" str := "world" //var s = []string{"11","22","33"} //刪除s首尾...
繼續(xù)訪(fǎng)問(wèn)
Golang 字符串
Go語(yǔ)言的代碼是由Unicode字符組成的,它們都必須由Unicode編碼規(guī)范中的UTF-8編碼格式進(jìn)行編碼并存儲(chǔ),Unicode編碼規(guī)范中的編碼格式定義的是字符與字節(jié)序列之間的轉(zhuǎn)換方式。其中的UTF-8是一種可變寬的編碼方案,它會(huì)用一個(gè)或多個(gè)字節(jié)的二進(jìn)制數(shù)來(lái)表示某個(gè)字符,最多使用四個(gè)字節(jié)。Go語(yǔ)言中的一個(gè)string類(lèi)型值會(huì)由若干個(gè)Unicode字符組成,每個(gè)Unicode字符都可以由一個(gè)rune類(lèi)型的值來(lái)承載。stringforrange。...
繼續(xù)訪(fǎng)問(wèn)
golang字符串[]string(slice)去重
1、現(xiàn)實(shí)代碼 // []string 去重 func RemoveDuplicate(list []string) []string { // 這個(gè)排序很關(guān)鍵 sort.Strings(list) i := 0 var newlist = []string{""} for j := 0; j len(list); j++ { if strings.Compare(newlist[i], list[j]) == -1 { newlist = append(newlist, lis
繼續(xù)訪(fǎng)問(wèn)
深入理解golang string
golang string string的定義 // string is the set of all strings of 8-bit bytes, conventionally but not // necessarily representing UTF-8-encoded text. A string may be empty, but // not nil. Values of string type are immutable. type string string string里存儲(chǔ)的是字符
繼續(xù)訪(fǎng)問(wèn)
golang中string包
對(duì)于基本類(lèi)型來(lái)說(shuō),字符串所需要執(zhí)行的操作會(huì)比較復(fù)雜,所以一般語(yǔ)言都會(huì)額外封裝一些方法用于處理字符串,go語(yǔ)言標(biāo)準(zhǔn)庫(kù)中也存在這樣一個(gè)名為strings的庫(kù) 包含判斷,判斷一個(gè)字符中是否有相應(yīng)的某個(gè)子字符串是經(jīng)常遇到的一種字符串操作,再go語(yǔ)言中可以使用strings包中的兩個(gè)方法判斷 ...
繼續(xù)訪(fǎng)問(wèn)
golang中的strings.TrimLeft
golang中的字符串操作strings.TrimLeft package main import ( "fmt" "strings" ) //golang字符串操作 func main(){ s := "Hello world hello world" str := "Hello" //var s = []string{"11","22","33"} //刪除s頭部連續(xù)的包含在str中的字符串 .
繼續(xù)訪(fǎng)問(wèn)
golang入門(mén)time與string轉(zhuǎn)換, time加減時(shí)間, 兩個(gè)時(shí)間差
package mainimport ( "fmt" "time")var timeLayoutStr = "2006-01-02 15:04:05" //go中的時(shí)間格式化必須是這個(gè)時(shí)間//var timeLayoutStr = 2006/01/02 03:04:05 //合法, 格式可以改變//var timeLayoutStr = 2019/01/02 15:04:05 /...
繼續(xù)訪(fǎng)問(wèn)
Golang——string
1.string結(jié)構(gòu)體底層 由兩部分組成:指向底層[ ]byte數(shù)組的指針;長(zhǎng)度(類(lèi)似切片結(jié)構(gòu)體) 2.相同的string常量不會(huì)重復(fù)存儲(chǔ) //由s1、s2的指針指向一塊相同的只讀內(nèi)存,這段內(nèi)存以u(píng)tf-8編碼存放hello數(shù)組 s1 := "hello" s2 := "hello" 3.string常量會(huì)在編譯期分配到只讀段,對(duì)應(yīng)數(shù)據(jù)地址不可寫(xiě)入,故string不支持修改。 要修改必須轉(zhuǎn)[]byte,string和[]byte轉(zhuǎn)換,會(huì)將這段只讀內(nèi)存的數(shù)據(jù)復(fù)制到堆/棧上。 //wrong s := "he
繼續(xù)訪(fǎng)問(wèn)
熱門(mén)推薦 golang -----------字符串(rune,string,type)
一、內(nèi)存布局 字符串在Go語(yǔ)言?xún)?nèi)存模型中用一個(gè)2字長(zhǎng)的數(shù)據(jù)結(jié)構(gòu)表示。它包含一個(gè)指向字符串存儲(chǔ)數(shù)據(jù)的指針和一個(gè)長(zhǎng)度數(shù)據(jù)。因?yàn)閟tring類(lèi)型是不可變的,對(duì)于多字符串共享同一個(gè)存儲(chǔ)數(shù)據(jù)是安全的。切分操作str[i:j]會(huì)得到一個(gè)新的2字長(zhǎng)結(jié)構(gòu),一個(gè)可能不同的但仍指向同一個(gè)字節(jié)序列(即上文說(shuō)的存儲(chǔ)數(shù)據(jù))的指針和長(zhǎng)度數(shù)據(jù)。這意味著字符串切分可以在不涉及內(nèi)存分配或復(fù)制操作。這使得字符串切分的效率等同于...
繼續(xù)訪(fǎng)問(wèn)
Golang string 常用方法
strings.Compare(p1, p2) 判斷p1是否大于p2,大于:1,小于:-1,等于:0。2.strings.LastIndex(p1,p2) p2在p1中最后一次出現(xiàn)的位置,未出現(xiàn)返回-1。1.strings.Replace(p1,p2,p3,n) 將p1中的p2替換成p3,最多替換n個(gè)。1.strings.Index(p1,p2) p2在p1中第一次出現(xiàn)的位置,未出現(xiàn)返回-1。2.strings.ReplaceAll(p1,p2,p3) 將p1中的所有p2替換成p3。
繼續(xù)訪(fǎng)問(wèn)
golang中的字符串
在go中rune是一個(gè)unicode編碼點(diǎn)。 我們都知道UTF-8將字符編碼為1-4個(gè)字節(jié),比如我們常用的漢字,UTF-8編碼為3個(gè)字節(jié)。所以rune也是int32的別名。
繼續(xù)訪(fǎng)問(wèn)
golang之字符串
Go語(yǔ)言的字符有兩種類(lèi)型: 一種是byte型,或者叫uint8類(lèi)型,代表了ASCII碼的一個(gè)字符。 一種是rune類(lèi)型,或者叫int32類(lèi)型,代表一個(gè)UTF-8字符,當(dāng)需要處理中文、日文等unicode字符時(shí),則需要用到rune類(lèi)型。 ...
繼續(xù)訪(fǎng)問(wèn)
Golang的 string 類(lèi)型
一點(diǎn)睛 字符串就是一串固定長(zhǎng)度的字符連接起來(lái)的字符序列。Go 的字符串是由單個(gè)字節(jié)連接起來(lái)的。Go語(yǔ)言的字符串的字節(jié)使用 UTF-8 編碼標(biāo)識(shí) Unicode 文本。 二 string 使用注意事項(xiàng)和細(xì)節(jié) 1 Go 語(yǔ)言的字符串的字節(jié)使用 UTF-8 編碼標(biāo)識(shí) Unicode 文本,這樣 Golang 統(tǒng)一使用 UTF-8 編碼,中文亂碼問(wèn)題不會(huì)再困擾程序員。 2 字符串一旦賦值了,字符串就不能修改了,在 Go 中字符串是不可變的。 3 字符串的兩種表示形式。 a 雙引號(hào)——會(huì)識(shí)別轉(zhuǎn)義字符 .
繼續(xù)訪(fǎng)問(wèn)
Golang Strings 官方包常用方法,學(xué)會(huì)這些夠用了
1.判斷是否以某字符串打頭/結(jié)尾 2.字符串分割 3.返回子串索引 4.字符串連接 5.字符串轉(zhuǎn)化為大小寫(xiě) 6.統(tǒng)計(jì)某個(gè)字符在字符串出現(xiàn)的次數(shù) 7.判斷字符串的包含關(guān)系 8.字符串替換
import (
"fmt"
"reflect"
)
func reflecType(x interface{}){
v := reflect.TypeOf(x)
fmt.Println("type:%v\n", v)
fmt.Println("type name:%v , rtpe kind:%v \n", v.getName(), v.getType())
}
type Cat struct{}
//通過(guò)反射設(shè)置變量的值
func reflectSetValue1(x interface{}){
v := reflect.ValueOf(x)
if v.Kind() == reflect.Int64{
v.SetInt(200) //修改的是副本, reflect 包會(huì)引發(fā)panic
}
}
//通過(guò)反射設(shè)置變量的值
func reflectSetValue2(x interface{}){
v := reflect.ValueOf(x)
//反射中使用Elem()獲取指針對(duì)應(yīng)的值
if v.Elem().Kind() == reflect.Int64{
v.Elem().SetInt(200)
}
}
func main(){
var a float32 = 3.14
reflectType(a) //type name:float32 type kind:float32
var b int64 = 100
reflectType(b) // type name :int64 type kind :int64
var c = Cat{}
reflectType(c) // type name :Cat type kind :struct
reflectSetValue1(b)
fmt.Println(b) //依然為100
reflectSetValue2(b)
}