最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang進(jìn)程在docker中OOM后hang住問題解析

 更新時(shí)間:2022年10月21日 09:31:11   作者:硅基生命  
這篇文章主要介紹了golang進(jìn)程在docker中OOM后hang住問題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

golang版本:1.16

背景:golang進(jìn)程在docker中運(yùn)行,因?yàn)槭褂脙?nèi)存較多,經(jīng)常在內(nèi)存未達(dá)到docker上限時(shí),就被oom-kill,為了避免程序頻繁被殺,在docker啟動(dòng)時(shí)禁用了oom-kill,但是出現(xiàn)了新的問題。

現(xiàn)象:docker內(nèi)存用滿后,golang進(jìn)程hang住,無任何響應(yīng)(沒有額外內(nèi)存系統(tǒng)無法分配新的fd,無法服務(wù)),即使在程序內(nèi)置了內(nèi)存達(dá)到上限就重啟,也不會(huì)生效,只能kill

因?yàn)閜prof查看進(jìn)程內(nèi)存有很多是能在gc時(shí)釋放的,起初懷疑是golang進(jìn)程問題

在hang住之前,先登錄到docker上,寫一個(gè)golang測(cè)試程序,只申請(qǐng)一小段內(nèi)存后sleep,啟動(dòng)時(shí)加GODEBUG=GCTRACE=1打印gc信息,發(fā)現(xiàn)mark 階段stw耗時(shí)達(dá)到31s(31823+15+0.11 ms對(duì)應(yīng)STW Mark Prepare,Concurrent Marking,STW Mark Termination)

懷疑是不是申請(qǐng)內(nèi)存失敗后,沒有觸發(fā)oom退出。在golang標(biāo)準(zhǔn)庫(kù)中查看oom相關(guān)的邏輯

mgcwork.go:374

if s == nil {
   systemstack(func() {
      s = mheap_.allocManual(workbufAlloc/pageSize, spanAllocWorkBuf)
   })
   if s == nil {
      throw("out of memory")
   }
   // Record the new span in the busy list.
   lock(&work.wbufSpans.lock)
   work.wbufSpans.busy.insert(s)
   unlock(&work.wbufSpans.lock)
}

mheap分配內(nèi)存使用了mmap,繼續(xù)懷疑是mmap返回的錯(cuò)誤碼在docker中不是非0

func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) {
   sysStat.add(int64(n))
   p, err := mmap(v, n, _PROT_READ| _PROT_WRITE, _MAP_ANON| _MAP_FIXED| _MAP_PRIVATE, -1, 0)
   if err == _ENOMEM {
      throw("runtime: out of memory")
   }
   if p != v || err != 0 {
      throw("runtime: cannot map pages in arena address space")
   }
}

為了對(duì)比驗(yàn)證,用c寫一段調(diào)用mmap的代碼,在同一個(gè)docker中同時(shí)跑看下

#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#define BUF_SIZE 393216
void main() {
    char *addr;
    int i;
    for(i=0;i<1000000;i++) {
        addr = (char *)mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE,
                MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
        if (addr != MAP_FAILED) {
            addr[0] = 'a';
            addr[BUF_SIZE-1] = 'b';
            printf("i:%d, sz: %d, addr[0]: %c, addr[-1]: %c\n", i, BUF_SIZE, addr[0], addr[BUF_SIZE-1]);
            munmap(addr, BUF_SIZE);
        } else {
            printf("error no: %d\n", errno);
        }
        usleep(1000000);
    }
}

mmap沒有失敗,而且同樣會(huì)hang住,說明不是golang機(jī)制的問題,應(yīng)該是阻塞在了系統(tǒng)調(diào)用上。查看調(diào)用堆棧,發(fā)現(xiàn)是hang在了cgroup中

[<ffffffff81224d65>] mem_cgroup_oom_synchronize+0x275/0x340
[<ffffffff811a068f>] pagefault_out_of_memory+0x2f/0x74
[<ffffffff81066bed>] __do_page_fault+0x4bd/0x4f0
[<ffffffff81801605>] async_page_fault+0x45/0x50
[<ffffffffffffffff>] 0xffffffffffffffff

查看go程序,也有相同的調(diào)用堆棧

[<ffffffff81103681>] futex_wait_queue_me+0xc1/0x120
[<ffffffff81104086>] futex_wait+0xf6/0x250
[<ffffffff8110647b>] do_futex+0x2fb/0xb20
[<ffffffff81106d1a>] SyS_futex+0x7a/0x170
[<ffffffff81003948>] do_syscall_64+0x68/0x100
[<ffffffff81800081>] entry_SYSCALL_64_after_hwframe+0x3d/0xa2
[<ffffffffffffffff>] 0xffffffffffffffff
[<ffffffff810f3ffe>] hrtimer_nanosleep+0xce/0x1e0
[<ffffffff810f419b>] SyS_nanosleep+0x8b/0xa0
[<ffffffff81003948>] do_syscall_64+0x68/0x100
[<ffffffff81800081>] entry_SYSCALL_64_after_hwframe+0x3d/0xa2
[<ffffffffffffffff>] 0xffffffffffffffff
[<ffffffff81224c5a>] mem_cgroup_oom_synchronize+0x16a/0x340
[<ffffffff811a068f>] pagefault_out_of_memory+0x2f/0x74
[<ffffffff81066bed>] __do_page_fault+0x4bd/0x4f0
[<ffffffff81801605>] async_page_fault+0x45/0x50
[<ffffffffffffffff>] 0xffffffffffffffff
[<ffffffff81224c5a>] mem_cgroup_oom_synchronize+0x16a/0x340
[<ffffffff811a068f>] pagefault_out_of_memory+0x2f/0x74
[<ffffffff81066bed>] __do_page_fault+0x4bd/0x4f0
[<ffffffff81801605>] async_page_fault+0x45/0x50
[<ffffffffffffffff>] 0xffffffffffffffff
[<ffffffff81224c5a>] mem_cgroup_oom_synchronize+0x16a/0x340
[<ffffffff811a068f>] pagefault_out_of_memory+0x2f/0x74
[<ffffffff81066bed>] __do_page_fault+0x4bd/0x4f0
[<ffffffff81801605>] async_page_fault+0x45/0x50
[<ffffffffffffffff>] 0xffffffffffffffff

看了下cgroup內(nèi)存控制的代碼,策略是沒有可用內(nèi)存并且未配置oom kill的程序,會(huì)鎖在一個(gè)等待隊(duì)列里,當(dāng)有可用內(nèi)存時(shí)再?gòu)年?duì)首喚醒。這個(gè)邏輯沒辦法通過配置或者其他方式繞過去。

elixir.bootlin.com/linux/v4.14…

 /**
 * mem_cgroup_oom_synchronize - complete memcg OOM handling
 * @handle: actually kill/wait or just clean up the OOM state
 *
 * This has to be called at the end of a page fault if the memcg OOM
 * handler was enabled.
 *
 * Memcg supports userspace OOM handling where failed allocations must
 * sleep on a waitqueue until the userspace task resolves the
 * situation.  Sleeping directly in the charge context with all kinds
 * of locks held is not a good idea, instead we remember an OOM state
 * in the task and mem_cgroup_oom_synchronize() has to be called at
 * the end of the page fault to complete the OOM handling.
 *
 * Returns %true if an ongoing memcg OOM situation was detected and
 * completed, %false otherwise.
 */
bool mem_cgroup_oom_synchronize(bool handle)
{
        struct mem_cgroup *memcg = current->memcg_in_oom;
        struct oom_wait_info owait;
        bool locked;
        /* OOM is global, do not handle */
        if (!memcg)
                return false;
        if (!handle)
                goto cleanup;
        owait.memcg = memcg;
        owait.wait.flags = 0;
        owait.wait.func = memcg_oom_wake_function;
        owait.wait.private = current;
        INIT_LIST_HEAD(&owait.wait.entry);
        prepare_to_wait(&memcg_oom_waitq, &owait.wait, TASK_KILLABLE);
        mem_cgroup_mark_under_oom(memcg);
        locked = mem_cgroup_oom_trylock(memcg);
        if (locked)
                mem_cgroup_oom_notify(memcg);
        if (locked && !memcg->oom_kill_disable) {
                mem_cgroup_unmark_under_oom(memcg);
                finish_wait(&memcg_oom_waitq, &owait.wait);
                mem_cgroup_out_of_memory(memcg, current->memcg_oom_gfp_mask,
                                         current->memcg_oom_order);
        } else {
                schedule();
                mem_cgroup_unmark_under_oom(memcg);
                finish_wait(&memcg_oom_waitq, &owait.wait);
        }
        if (locked) {
                mem_cgroup_oom_unlock(memcg);
                /*
 * There is no guarantee that an OOM-lock contender
 * sees the wakeups triggered by the OOM kill
 * uncharges.  Wake any sleepers explicitly.
 */
                memcg_oom_recover(memcg);
        }
cleanup:
        current->memcg_in_oom = NULL;
        css_put(&memcg->css);
        return true;
}

結(jié)論:

docker內(nèi)存耗光后,golang在gc的mark階段,需要申請(qǐng)新的內(nèi)存記錄被標(biāo)記的對(duì)象時(shí),需要調(diào)用mmap,因?yàn)闆]有可用內(nèi)存,就會(huì)被hang在cgroup中,gc無法完成也就無法釋放內(nèi)存,就會(huì)導(dǎo)致golang程序一直在stw階段,無法對(duì)外服務(wù),即使壓力下降也無法恢復(fù)。最好還是不要關(guān)閉docker的oom-kill

以上就是golang進(jìn)程在docker中OOM后hang住問題解析的詳細(xì)內(nèi)容,更多關(guān)于golang進(jìn)程docker OOM hang的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go 1.18新特性之泛型的全面講解

    Go 1.18新特性之泛型的全面講解

    本文力求能讓未接觸過泛型編程的人也能較好理解Go的泛型,所以行文可能略顯啰嗦。但是請(qǐng)相信我,看完這篇文章你能獲得對(duì)Go泛型非常全面的了解
    2023-03-03
  • Golang對(duì)MongoDB數(shù)據(jù)庫(kù)的操作簡(jiǎn)單封裝教程

    Golang對(duì)MongoDB數(shù)據(jù)庫(kù)的操作簡(jiǎn)單封裝教程

    mongodb官方?jīng)]有關(guān)于go的mongodb的驅(qū)動(dòng),因此只能使用第三方驅(qū)動(dòng),mgo就是使用最多的一種。下面這篇文章主要給大家介紹了關(guān)于利用Golang對(duì)MongoDB數(shù)據(jù)庫(kù)的操作簡(jiǎn)單封裝的相關(guān)資料,需要的朋友可以參考下
    2018-07-07
  • Go中crypto/rsa庫(kù)的高效使用指南

    Go中crypto/rsa庫(kù)的高效使用指南

    本文主要介紹了Go中crypto/rsa庫(kù)的高效使用指南,從 RSA 的基本原理到 crypto/rsa 庫(kù)的實(shí)際應(yīng)用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • 使用Golang實(shí)現(xiàn)Sm2加解密的代碼詳解

    使用Golang實(shí)現(xiàn)Sm2加解密的代碼詳解

    本文主要介紹了Go語言實(shí)現(xiàn)Sm2加解密的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-03-03
  • go語言基礎(chǔ)語法示例

    go語言基礎(chǔ)語法示例

    這篇文章主要介紹了go語言基礎(chǔ)語法示例,介紹了go語言較為全面的基礎(chǔ)知識(shí),具有一定參考價(jià)值,需要的可以了解下。
    2017-11-11
  • go語言中的return語句

    go語言中的return語句

    這篇文章主要介紹了go語言中的return語句,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下,希望對(duì)你的學(xué)習(xí)有所幫助
    2022-05-05
  • Go語言中的數(shù)據(jù)格式(json、xml?、msgpack、protobuf)使用總結(jié)

    Go語言中的數(shù)據(jù)格式(json、xml?、msgpack、protobuf)使用總結(jié)

    在分布式的系統(tǒng)中,因?yàn)樯婕暗綌?shù)據(jù)的傳輸,所以一定會(huì)進(jìn)行數(shù)據(jù)的交換,此時(shí)就要定義數(shù)據(jù)交換的格式,例如二進(jìn)制、Json、Xml等等。本文總結(jié)了Go語言中的數(shù)據(jù)格式,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • Go語言pointer及switch?fallthrough實(shí)戰(zhàn)詳解

    Go語言pointer及switch?fallthrough實(shí)戰(zhàn)詳解

    這篇文章主要為大家介紹了Go語言pointer及switch?fallthrough實(shí)戰(zhàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 詳解Golang中interface接口的原理和使用技巧

    詳解Golang中interface接口的原理和使用技巧

    interface?接口在?Go?語言里面的地位非常重要,是一個(gè)非常重要的數(shù)據(jù)結(jié)構(gòu)。本文主要介紹了Golang中interface接口的原理和使用技巧,希望對(duì)大家有所幫助
    2022-11-11
  • go語言實(shí)現(xiàn)字符串base64編碼的方法

    go語言實(shí)現(xiàn)字符串base64編碼的方法

    這篇文章主要介紹了go語言實(shí)現(xiàn)字符串base64編碼的方法,實(shí)例分析了Go語言操作字符串的技巧及base64編碼的使用技巧,需要的朋友可以參考下
    2015-03-03

最新評(píng)論

伊宁县| 得荣县| 邓州市| 壶关县| 且末县| 牙克石市| 新巴尔虎右旗| 苍溪县| 鹿邑县| 梓潼县| 武鸣县| 正镶白旗| 吴忠市| 霸州市| 陵水| 江阴市| 安岳县| 科技| 江陵县| 武平县| 米泉市| 开封市| 富阳市| 仙居县| 乌拉特前旗| 昌都县| 阿巴嘎旗| 乐亭县| 龙海市| 叙永县| 石家庄市| 福海县| 周至县| 普安县| 嵊泗县| 虎林市| 贞丰县| 滦平县| 平乐县| 巴彦县| 罗定市|