searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Ceph在64K pagesize内核下tcmalloc内存泄露问题

2023-05-26 02:38:12
334
0

问题现象

在linux内核pagesize为64K配置,部署运行ceph软件,设置osd进程的内存上限为2G,在长时间的读写情况下,发现ceph-osd的内存占用会超过2G,不会被回收。如下图所示。

图1 ceph-osd进程内存占用超过2G的限制

原因分析

通过导出osd的heap的信息,如下图,从图中可以看出,use by application是2G左右,由于osd有内存上限2G的设置,符合预期。page heap freelist为4G,这部分内存是osd释放交还给tcmalloc的内存,是可以被tcmalloc分配给osd使用或者释放给os的。

图2 ceph-osd heap状态

tcmalloc中的page heap freelist的内存会被定期释放给os,观测了很久也没看见page heap freelist的内存减少。并且ceph osd中实现了释放的接口,ceph daemon osd.97 heap release可以直接释放,也没有效果。分析tcmalloc的内存释放流程,释放过程的运行栈如下图所示。最终释放内存给os是在TCMalloc_SystemRelease中通过madvise释放给os的。

图3 osd中tcmalloc内存释放运行栈

从PageHeap::ReleaseAtLeastNPages的实现看,此函数主要是按页进行内存的释放,直到完成了num_pages页的释放就停止。每次释放都是接着上一次的release_index_进行的。但是第484行,当released_len == 0时,就直接退出了,并不会继续循环了,此时release_index_也不会更新了,下一次释放还是从release_index_开始,下次还是直接退出了。内存就无法释放给os了。接着往下分析什么情况下released_len == 0。

图4 PageHeap::ReleaseAtLeastNPages的实现

从TCMalloc_SystemRelease的实现看,输入需要释放的地址和长度。释放之前需要对齐到页。只有一个完整的页才能被释放,当length小于一页的时候,是不会释放的。上图中x1表示length=32768,不会被释放。这种情况在内核pagesize为64K,tcmalloc page size小于64K(默认8K)下,很容易出现。在内核pagesize为4K的情况下就不会出现此种情况。

图5 TCMalloc_SystemRelease的实现

解决方案

在PageHeap::ReleaseAtLeastNPages中忽略掉released_len为0的情况,继续运行(当然也可以在released_len为0时,release_index_++,然后再退出,但是与函数实现的意图不太相符),修改代码如下:

Length PageHeap::ReleaseAtLeastNPages(Length num_pages) {

  Length released_pages = 0;

  int count = 0;

  // Round robin through the lists of free spans, releasing the last

  // span in each list.  Stop after releasing at least num_pages

  // or when there is nothing more to release.

  while (released_pages < num_pages && stats_.free_bytes > 0) {

    for (int i = 0; i < kMaxPages+1 && released_pages < num_pages;

         i++, release_index_++, count++) {

      if (release_index_ > kMaxPages) release_index_ = 0;

      SpanList* slist = (release_index_ == kMaxPages) ?

          &large_ : &free_[release_index_];

      if (!DLL_IsEmpty(&slist->normal)) {

        Length released_len = ReleaseLastNormalSpan(slist);

        // Some systems do not support release

        //if (released_len == 0) return released_pages;

        released_pages += released_len;

      }

    }

    if (count == kMaxPages+1)

        break;

  }

  return released_pages;

}

 

# 直接用这个包,这里面已经合入了修复代码

wget https://github.com/gperftools/gperftools/releases/download/gperftools-2.8/gperftools-2.8.tar.gz --no-check-certificate

0条评论
作者已关闭评论
刘****滨
2文章数
1粉丝数
刘****滨
2 文章 | 1 粉丝
刘****滨
2文章数
1粉丝数
刘****滨
2 文章 | 1 粉丝
原创

Ceph在64K pagesize内核下tcmalloc内存泄露问题

2023-05-26 02:38:12
334
0

问题现象

在linux内核pagesize为64K配置,部署运行ceph软件,设置osd进程的内存上限为2G,在长时间的读写情况下,发现ceph-osd的内存占用会超过2G,不会被回收。如下图所示。

图1 ceph-osd进程内存占用超过2G的限制

原因分析

通过导出osd的heap的信息,如下图,从图中可以看出,use by application是2G左右,由于osd有内存上限2G的设置,符合预期。page heap freelist为4G,这部分内存是osd释放交还给tcmalloc的内存,是可以被tcmalloc分配给osd使用或者释放给os的。

图2 ceph-osd heap状态

tcmalloc中的page heap freelist的内存会被定期释放给os,观测了很久也没看见page heap freelist的内存减少。并且ceph osd中实现了释放的接口,ceph daemon osd.97 heap release可以直接释放,也没有效果。分析tcmalloc的内存释放流程,释放过程的运行栈如下图所示。最终释放内存给os是在TCMalloc_SystemRelease中通过madvise释放给os的。

图3 osd中tcmalloc内存释放运行栈

从PageHeap::ReleaseAtLeastNPages的实现看,此函数主要是按页进行内存的释放,直到完成了num_pages页的释放就停止。每次释放都是接着上一次的release_index_进行的。但是第484行,当released_len == 0时,就直接退出了,并不会继续循环了,此时release_index_也不会更新了,下一次释放还是从release_index_开始,下次还是直接退出了。内存就无法释放给os了。接着往下分析什么情况下released_len == 0。

图4 PageHeap::ReleaseAtLeastNPages的实现

从TCMalloc_SystemRelease的实现看,输入需要释放的地址和长度。释放之前需要对齐到页。只有一个完整的页才能被释放,当length小于一页的时候,是不会释放的。上图中x1表示length=32768,不会被释放。这种情况在内核pagesize为64K,tcmalloc page size小于64K(默认8K)下,很容易出现。在内核pagesize为4K的情况下就不会出现此种情况。

图5 TCMalloc_SystemRelease的实现

解决方案

在PageHeap::ReleaseAtLeastNPages中忽略掉released_len为0的情况,继续运行(当然也可以在released_len为0时,release_index_++,然后再退出,但是与函数实现的意图不太相符),修改代码如下:

Length PageHeap::ReleaseAtLeastNPages(Length num_pages) {

  Length released_pages = 0;

  int count = 0;

  // Round robin through the lists of free spans, releasing the last

  // span in each list.  Stop after releasing at least num_pages

  // or when there is nothing more to release.

  while (released_pages < num_pages && stats_.free_bytes > 0) {

    for (int i = 0; i < kMaxPages+1 && released_pages < num_pages;

         i++, release_index_++, count++) {

      if (release_index_ > kMaxPages) release_index_ = 0;

      SpanList* slist = (release_index_ == kMaxPages) ?

          &large_ : &free_[release_index_];

      if (!DLL_IsEmpty(&slist->normal)) {

        Length released_len = ReleaseLastNormalSpan(slist);

        // Some systems do not support release

        //if (released_len == 0) return released_pages;

        released_pages += released_len;

      }

    }

    if (count == kMaxPages+1)

        break;

  }

  return released_pages;

}

 

# 直接用这个包,这里面已经合入了修复代码

wget https://github.com/gperftools/gperftools/releases/download/gperftools-2.8/gperftools-2.8.tar.gz --no-check-certificate

文章来自个人专栏
Ceph相关
2 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0