As previously discussed, one of the primary factors resulting in expensive Garbage Collection is the number of objects on heap. By optimizing our code to reduce the number of long-lived objects on heap, we can minimize the resources spent on GC, and improve our system performance.
如前所述,导致昂贵的垃圾收集的主要因素之一是堆上的对象数量。 通过优化代码以减少堆上的长期对象的数量,我们可以最大程度地减少在GC上花费的资源,并提高系统性能。
Here are some suggestions on how to do so:
以下是一些有关这样做的建议:
减少长寿物品的数量 (Reduce the number of long-living objects)
Rather than having objects live on the heap, they can be created as values rather than references on demand. For instance, if we need some data for each item in a user request, rather than precomputing and storing it in a long-lived map, we can compute it on a per-request basis to reduce the number of objects on the heap.
可以将对象创建为值而不是按需引用,而不是使对象驻留在堆上。 例如,如果我们需要用户请求中每个项目的一些数据,而不是将其预先计算并存储在长寿命的映射中,则可以基于每个请求进行计算以减少堆中的对象数量。
删除指针中的指针 (Remove pointers within pointers)
If we have a reference to an object, and the object itself contains further pointers, these are all considered individual objects on the heap even though they may be nested. By changing these nested values to be non-pointers, we can reduce the number of objects to be scanned.
如果我们有对一个对象的引用,并且该对象本身包含其他指针,则即使它们可能是嵌套的,也都将它们视为堆上的单个对象。 通过将这些嵌套值更改为非指针,我们可以减少要扫描的对象的数量。
避免不必要的字符串/字节数组分配 (Avoid unnecessary string/byte array allocations)
Since strings/bytes arrays are treated as pointers under the hood, each one is an object on the heap. If possible, try to represent these as other non-pointer values such as integers/floats, time.Time for dates, etc.
由于字符串/字节数组在幕后被视为指针,因此每个数组都是堆上的一个对象。 如果可能,请尝试将它们表示为其他非指针值,例如整数/浮点数,时间,日期时间等。
createCatalogMapstringstruct
createCatalogMapstringstruct
(pprof) list createCatalogMap
Total: 106261986
ROUTINE ======================== <CODE_PATH>
34768 84576835 (flat, cum) 79.59% of Total
. 63815675 233: product := BuildProduct(productID, productPrice, productSellerID)
. . 234: if productPrice < minProductPrice {
. . 235: minProductPrice = productPrice
. . 236: }
. 20726392 237: catalogListing := catalogs.CreateListing(product, contextFeatures)
. . 238:
. . 239: structKey := CatalogKeyStruct{
. . 240: ProductID: productID,
. . 241: SellerID: productSellerID,
. . 242: CatalogVersion: catalogListing.GetVersion(),
. . 243: }
34768 34768 244: catalogMap[structKey] = catalogListing
. . 245: return catalogMap
对象池 (Object pooling)
If your program tends to create a large number of short-lived objects in bursts, you may benefit from object pools, which can be used to allocate and free memory blocks manually. This can reduce the number of GC cycles needed, since the pool retains these objects for a longer scope, so we don’t need to keep allocating and cleaning up these objects.
如果您的程序倾向于突发创建大量短期对象,则您可能会受益于对象池,该对象池可用于手动分配和释放内存块。 由于池将这些对象保留更长的范围,因此可以减少所需的GC周期数,因此我们不需要继续分配和清理这些对象。
Note: Object pools can cause memory leaks if not used properly, so this is only recommended if you know what you’re doing
注意:如果使用不正确,对象池可能会导致内存泄漏,因此仅当您知道自己在做什么时才建议这样做
清理未使用的字段(Clean up unused fields)
boolfalseint0
boolfalseint0
By removing these fields, we lower the program’s memory usage, and make the code more readable and easy to understand.
通过删除这些字段,我们降低了程序的内存使用率,并使代码更具可读性和易于理解。
从堆中迁移数据 (Migrate data off the heap)
If we remove data from the heap, we drastically reduce the amount of work that the garbage collector needs to do. One option here is to migrate the data to an external source (for example, in a microservice architecture, we may have a performant key-value store that can be leveraged for such a use case). It’s important to consider the additional overhead of making a request to fetch the data from this external source.
如果我们从堆中删除数据,则将大大减少垃圾收集器需要完成的工作量。 这里的一种选择是将数据迁移到外部源(例如,在微服务体系结构中,我们可能具有可用于这种用例的高性能键值存储)。 重要的是要考虑发出请求从此外部源获取数据的额外开销。
Another option is to leverage an open-source Go package to store data off the heap, but still within our system’s memory. Here is one such package.
另一个选择是利用开源Go包在堆外存储数据,但仍将其存储在系统内存中。 这是一个这样的包裹。
Note: Using off-heap storage can cause problems if not used properly, so this is only recommended if you know what you’re doing
注意:如果使用不当,使用堆外存储会引起问题,因此仅当您知道自己在做什么时才建议这样做
重新排列结构以降低内存使用量(Rearrange your structs for lower memory usage)
GoodObjectBadObjectboolsboolint64bool
GoodObjectBadObjectboolsboolint64bool
Go Playground Linktype BadObject struct {
A bool
B int64
C bool
}type GoodObject struct {
A bool
C bool
B int64
}