2 minutes reading time
最近一个部署了 go 应用的服务器出现了 OOM 的现象,内存占用过高。
通过 Pyroscope 分析得出是因为 Minio 的 go sdk 中的 PutObject 函数占用了大量的内存。 Pyroscope 是什么,前面的文章已经介绍过了,这里就不过多介绍了。
接下来我们通过查看相关的源码来查看是什么原因。
// PutObject creates an object in a bucket.
//
// You must have WRITE permissions on a bucket to create an object.
//
// - For size smaller than 16MiB PutObject automatically does a
// single atomic PUT operation.
//
// - For size larger than 16MiB PutObject automatically does a
// multipart upload operation.
//
// - For size input as -1 PutObject does a multipart Put operation
// until input stream reaches EOF. Maximum object size that can
// be uploaded through this operation will be 5TiB.
//
// WARNING: Passing down '-1' will use memory and these cannot
// be reused for best outcomes for PutObject(), pass the size always.
//
// NOTE: Upon errors during upload multipart operation is entirely aborted.
func (ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64,
opts PutObjectOptions,
) (info UploadInfo, err error)
从方法的注释可以看出,当传递的大小为 -1 时,会进行多次 put 操作,直到输入流结束。 多次 put 操作的最大大小为 5TiB, 并且不能重用内存,导致占用大量内存。
接下来继续深入,我们看看这个函数的源码。
func OptimalPartInfo(objectSize int64, configuredPartSize uint64) (totalPartsCount int, partSize int64, lastPartSize int64, err error)
从函数中可以看出,这个函数的作用是计算出最佳的分片大小,并且计算出总的分片数量。 当大小为 -1 时,会使用最大的 5TiB。可以看出当没有指定对象大小时,每次会使用较大的内存。
在我们使用 MinIO sdk 时,在使用 PutObject 方法时,最好指定要上传的对象的大小,避免造成内存资源的浪费。