이미 다양한 자료구조들을 지원하고 있지만 연습삼아 만들어 본다.
go는 gc가 되는데다가 함수 내에서 새 할당을 하면 알아서 힙에서 뽑아다 준다. 이는 추후 gc에서 제거될 것인데 생명주기를 잘 생각해봐야겠다. 런타임 명시적 GC가 필요할 수도 있고 단순히 nil 처리만 해서 go에서 맡길 수도 있을 것 같다. nil처리 하지 않으면 어찌 될 것인가? gc가 제거하지 못하고 런타임 내내 메모리 릭이 될 것인가? 아직은 잘 모르겠다. 예컨데 구조체의 자식에 다른 구조체가 포인팅 되어 있다. 부모 구조체를 nil 처리해서 gc가 잡아 지웠다. 자식은? 아마도 쫒아다니며 nil 처리를 해야 하지 않을런지. 데몬형 SW라면 이 구조체의 생명주기를 go가 판단할 방법이 없다. (데몬형이 아니라면 종료 시점을 죽음으로 보면 될 것이다) 방법이 완전 없는 것은 아니다. gc가 자식들을 다 찾아다니도록 go가 만들어져있다면 가능하겠다. go는 대머리가 팀원(=천재)인 어벤저스 팀이 만든 언어이므로 가능할지도 모른다. 이 메커니즘을 뭘로 검색해야 할 지 모르겠다. 그러니까 영어를 공부하자. 기승전영어.
키 쫑 검사는 하지 않는 단방향 리스트다.
내용 추가한다.
코드 하단부에 reset()를 넣었다. 얘는 연결 리스트를 통으로 지우는데 체인 족보를 다 훑어서 nil처리 하지 않는다. 걍 대가리 꼬리만 nil처리했다. gc 상황을 보면 부모를 nil 하는 것만으로 족보를 모두 gc하고 있다. go는 대단하다. 역시 팀원 중에 대머리가 있으면 짱이다.
go build ll.go
GODEBUG="gctrace=1" ./ll
package main
import (
"os"
"runtime/debug"
"fmt"
"runtime"
)
type item struct {
next *item
k string
v string
}
var head *item = nil
var tail *item = nil
func add(k string, v string) {
if head == nil {
head = &item{nil, k, v}
tail = head
return
}
new_item := item{nil, k, v}
tail.next = &new_item
tail = tail.next
}
func del(k string) {
pos := head
prev := pos
for {
if pos == nil {
return
} else {
if pos.k == k {
if pos == head {
head = pos.next
}
if pos == tail {
tail = prev
}
prev.next = pos.next
pos = nil
// pos.next = nil
return
}
prev = pos
pos = pos.next
}
}
}
func all() {
pos := head
for {
if pos == nil {
return
}
println(pos.k, pos.v)
pos = pos.next
}
}
func reset() {
head = nil
tail = nil
}
func get(k string) (v interface{}) {
pos := head
for {
if pos == nil {
v = nil
return
} else {
if pos.k == k {
v = pos.v
return
}
pos = pos.next
}
}
}
func main() {
println("hell")
add("eee", "ddd")
println("eee 찾기")
a := get("eee")
if a != nil {
println("찾았다", a.(string))
} else {
println("없다 없어1")
}
println("전체 지움1111")
reset()
a = get("eee")
if a != nil {
println("찾았다", a.(string))
} else {
println("없다 없어2")
}
a = get("eee")
if a != nil {
println("찾았다", a.(string))
} else {
println("없다 없어3")
}
add("a", "1")
add("b", "2")
println("전체 순회")
println("마지막 지움")
del("b")
a = get("b")
if a != nil {
println("찾았다", a.(string))
} else {
println("없다 없어2222")
}
a = get("a")
if a != nil {
println("찾았다", a.(string))
} else {
println("없다 없어")
}
del("a")
add("c", "3")
a = get("c")
if a != nil {
println("찾았다", a.(string))
} else {
println("없다 없어")
}
add("d", "4")
add("e", "5")
add("f", "6")
del("e")
all()
del("f")
println("---")
all()
del("c")
println("---")
all()
add("g", "6")
add("v", "7")
add("m", "8")
println("---")
all()
del("v")
println("---11")
all()
// del("eee")
for i := 0; i < 1000000; i++ {
add(fmt.Sprintf("하하하%d", i), fmt.Sprintf("히히히히 %d", i * 2))
}
debug.FreeOSMemory()
println("------------------------1")
for i := 5000; i < 1000000; i++ {
del(fmt.Sprintf("하하하%d", i))
}
reset()
runtime.GC()
debug.FreeOSMemory()
println("------------------------2")
runtime.GC()
debug.FreeOSMemory()
println("------------------------3")
println(get("하하하10").(string))
os.Exit(0)
}
reset 안 하면
scvg-1: inuse: 97, idle: 0, sys: 97, released: 0, consumed: 97 (MB)
gc 8 @0.479s 4%: 0.001+54+0.11 ms clock, 0.015+0/41/13+0.95 ms cpu, 91->91->91 MB, 183 MB goal, 8 P (forced)
gc 9 @0.537s 5%: 0.002+56+0.096 ms clock, 0.017+0/56/0.15+0.76 ms cpu, 91->91->91 MB, 183 MB goal, 8 P (forced)
scvg-1: 0 MB released
scvg-1: inuse: 97, idle: 0, sys: 97, released: 0, consumed: 97 (MB)
------------------------2
gc 10 @0.597s 5%: 0.016+55+0.095 ms clock, 0.13+0/38/16+0.76 ms cpu, 91->91->91 MB, 183 MB goal, 8 P (forced)
gc 11 @0.655s 6%: 0.002+56+0.095 ms clock, 0.017+0/56/0.15+0.76 ms cpu, 91->91->91 MB, 183 MB goal, 8 P (forced)
scvg-1: 0 MB released
scvg-1: inuse: 97, idle: 0, sys: 97, released: 0, consumed: 97 (MB)
------------------------3
reset 하면
gc 7 @0.331s 4%: 0.005+55+0.10 ms clock, 0.043+0/55/0.39+0.84 ms cpu, 93->93->91 MB, 173 MB goal, 8 P (forced)
scvg-1: 1 MB released
scvg-1: inuse: 98, idle: 1, sys: 99, released: 1, consumed: 98 (MB)
gc 8 @0.390s 4%: 0.002+0.12+0.10 ms clock, 0.019+0/0.12/0.12+0.86 ms cpu, 91->91->0 MB, 183 MB goal, 8 P (forced)
gc 9 @0.394s 4%: 0.002+0.045+0.096 ms clock, 0.023+0/0.067/0+0.77 ms cpu, 0->0->0 MB, 4 MB goal, 8 P (forced)
scvg-1: 99 MB released
scvg-1: inuse: 0, idle: 99, sys: 99, released: 99, consumed: 0 (MB)
------------------------2
gc 10 @0.398s 4%: 0.001+0.10+0.056 ms clock, 0.014+0/0.11/0.086+0.45 ms cpu, 0->0->0 MB, 4 MB goal, 8 P (forced)
gc 11 @0.398s 4%: 0.024+0.044+0.017 ms clock, 0.19+0/0.035/0.051+0.14 ms cpu, 0->0->0 MB, 4 MB goal, 8 P (forced)
scvg-1: inuse: 0, idle: 99, sys: 99, released: 99, consumed: 0 (MB)
심지어는 nil 처리 족보 훑는다고 뺑이치면 뺑이친다고 느려지지만 그냥 gc에 맡기면 쑝힛툭땡하고 끝난다... 헐..