In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-27 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces the pit of sync.Map in Golang, has a certain reference value, interested friends can refer to, I hope you can learn a lot after reading this article, the following let the editor take you to understand it.
Origin
Go 1.15 was released, and I updated this version as soon as possible. After all, I had some confidence in the stability of Go, so I produced it directly in the company.
As a result, OOM appeared within minutes of being online, so pprof heap, then rolled back quickly, and found that a block of memory that should have been freed at the end of a request has been retained and has been growing, as shown in the figure (linkBufferNode in the figure):
The only change in this launch is the upgrade of the Go version, and there are no other changes, so I started testing locally and found that it can be 100% reproduced locally.
Investigation process
Looking at the Release Note of Go 1.15, I found two highly suspected things:
Removed some GC Data, resulting in a 5% reduction in binary size
A new memory allocation algorithm.
So change runtime, turn off the new memory allocation algorithm, switch back to the old one, and so on, and find that the problem is still unsolved, and the phenomenon still exists.
So it really doesn't work, and the GODEBUG= "allocfreetrace=1 method" is presented, which can be seen by the naked eye from the log file of 100MB +. (the poignant process is omitted here)
Eventually my intuition told me that the problem might have something to do with the change to sync.Map in Go 1.15 (don't ask me why, it's really intuition, I can't say it).
Sample code
For ease of explanation, I wrote a minimum reproducible code, as follows:
Package main
Import (
"sync"
)
Var sm sync.Map
Func insertKeys () {
Keys: = make ([] interface {}, 0,10)
/ / Store some keys
For I: = 0; I < 10; iTunes + {
V: = make ([] int, 1000)
Keys = append (keys, & v)
Sm.Store (keys [I], struct {} {})
}
/ / delete some keys, but not all keys
For I, k: = range keys {
If I% 2 = 0 {
Continue
}
Sm.Delete (k)
}
}
Func shutdown () {
Sm.Range (func (key, value interface {}) bool {
/ / do something to key
Return true
})
}
Func main () {
InsertKeys ()
/ / do something...
Shutdown ()
}
Sync.Map changes in Go 1.15
In Go 1.15, sync.Map added a method LoadAndDelete, the specific issue is here: https://github.com/golang/go/issues/33762CL, here: https://go-review.googlesource.com/c/go/+/205899/.
Why am I sure it is caused by this change? It's simple: I dropped this change revert locally, and the problem is gone. All right, turn off the phone and get off work.
Of course, it's not that simple. I want to know why, so I begin to see which piece of it has been changed. (100000 words are omitted here)
It turns out that the key code is this:
/ / LoadAndDelete deletes the value for a key, returning the previous value if any.
/ / The loaded result reports whether the key was present.
Func (m * Map) LoadAndDelete (key interface {}) (value interface {}, loaded bool) {
Read, _: = m.read.Load (). (readOnly)
E, ok: = read.m [key]
If! ok & & read.amended {
M.mu.Lock ()
Read, _ = m.read.Load (). (readOnly)
E, ok = read.m [key]
If! ok & & read.amended {
E, ok = m.dirty [key]
/ / Regardless of whether the entry was present, record a miss: this key
/ / will take the slow path until the dirty map is promoted to the read
/ / map.
M.missLocked ()
}
M.mu.Unlock ()
}
If ok {
Return e.delete ()
}
Return nil, false
}
/ / Delete deletes the value for a key.
Func (m * Map) Delete (key interface {}) {
M.LoadAndDelete (key)
}
Func (e * entry) delete () (value interface {}, ok bool) {
For {
P: = atomic.LoadPointer (& e.p)
If p = = nil | | p = = expunged {
Return nil, false
}
If atomic.CompareAndSwapPointer (& e.p, p, nil) {
Return * (* interface {}) (p), true
}
}
}
In this code, you will find that when you Delete, you don't really delete the key, but take the entry from the key and set the entry to nil.
So, in our scenario, we put in a connection as a key, so the memory associated with that connection, such as buffer, can never be freed.
So why is there no problem in Go 1.14? The following is the code for Go 1.14:
/ / Delete deletes the value for a key.
Func (m * Map) Delete (key interface {}) {
Read, _: = m.read.Load (). (readOnly)
E, ok: = read.m [key]
If! ok & & read.amended {
M.mu.Lock ()
Read, _ = m.read.Load (). (readOnly)
E, ok = read.m [key]
If! ok & & read.amended {
Delete (m.dirty, key)
}
M.mu.Unlock ()
}
If ok {
E.delete ()
}
}
In Go 1.14, if key is in dirty, it will be deleted; coincidentally, we actually "misuse" sync.Map, and there is no read operation in our use, so all key is actually in dirty, so it will be deleted when Delete is called.
Note that no matter which version of Go, once key is upgraded to read, it will never be deleted.
Thank you for reading this article carefully. I hope the article "the pit of sync.Map in Golang" shared by the editor will be helpful to you. At the same time, I also hope you will support us and pay attention to the industry information channel. More related knowledge is waiting for you to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.