物理內存充足,可是爲何用代碼總申請不到內存呢?

點擊上方藍色字體,關注我 ——
java

一個在阿里雲打工的清華學渣!linux


圖 by:我是嚴肅的於海童@清華c++

本文做者:楊牧原(花名牧原),阿里雲技術專家,多年操做系統和應用調試經驗,理論功底深厚,實踐經驗豐富。目前專一Linux性能調優,容器集羣和系統網絡。程序員

本文經原做者受權發於公衆號【程序猿石頭】,原文基礎上稍做措辭改動。web

背景

某次遇到一個客戶嘗試用 Java (其實跟具體用什麼語言不要緊)申請使用 4G 的內存申請,機器(ECS總內存是 8G,free 的內存也超過 4G,按道理是 OK 的,但老是直接 OOM。面試

因而便找上門來講,「大家這玩意有問題啊?編程

心裏 :「bug 是不可能有的,必定是你的打開姿式不對」,恩,不行,本着「客戶第一」的原則,仍是要來幫客戶解鎖姿式的。
後端

本文就詳細記錄了這個 case 的排查過程。微信

具體過程

申請4g內存失敗

如上圖所示,記錄顯示爲申請 4G 內存失敗(4294967296 B / 1024 / 1024 = 4096 M)。cookie

是不是 min_free_kbytes & nr_hugepage 配置錯誤?

  1. 第一反應是想起來以前的  vm.min_free_kbytes & nr_hugepage 致使的free大於available案例有關

memfree 統計的是全部內存的 free 內存,而 memavailable 統計的是能夠拿來給程序用的內存,而客戶設置了 vm.min_free_kbytes(2.5G),這個內存在 free 統計,可是不在 memavailable 統計,nr_hugepage 也會有這個問題。

兩者的統計方式不同, 具體參考 Documentation/filesystems/proc.txt

  • MemFree: The sum of LowFree+HighFree
  • MemAvailable: An estimate of how much memory is available for starting new applications, without swapping. Calculated from MemFree, SReclaimable, the size of the file LRU lists, and the low watermarks in each zone. The estimate takes into account that the system needs some page cache to function well, and that not all reclaimable slab will be reclaimable, due to items being in use. The impact of those factors will vary from system to system.
  1. 跟客戶要  free -m && sysctl -p && /proc/meminfo 等信息分析問題。
  • HugePages_Total 爲0,說明沒有設置  nr_hugepage
  • MemAvailable: 7418172 kB, 說明這麼多內存可用。
# sysctl -p
net.ipv4.ip_forward = 0
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 1
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
...
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_max_syn_backlog=4096
net.core.netdev_max_backlog=10000
vm.overcommit_memory=2
...
#cat /proc/meminfo
MemTotal:        8009416 kB
MemFree:         7347684 kB
MemAvailable:    7418172 kB
Buffers:           18924 kB
Cached:           262836 kB
SwapCached:            0 kB
Active:           315188 kB
Inactive:         222364 kB
Active(anon):     256120 kB
Inactive(anon):      552 kB
Active(file):      59068 kB
Inactive(file):   221812 kB
....
HugePages_Total:       0  
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:      114560 kB
DirectMap2M:     4079616 kB
DirectMap1G:     6291456 kB

嘗試重現

  1. 嘗試自行測試使用 java命令,去申請超出個人測試機物理內存,拿到報錯。

實際上面的meminfo已經說明了問題,可是因爲經驗不足,一時沒有看明白怎麼回事。

下面測試證實正常申請內存不會有問題,超額的內存纔會 OOM。

[root@test ~]# java -Xms4096M -version
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (build 1.8.0_242-b08)
OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)
[root@test ~]# java -Xms5000M -version
OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000687800000, 3495428096, 0) failed; error='Cannot allocate memory' (errno=12)
......

系統信息以下:

---------------  S Y S T E M  ---------------
OS:CentOS Linux release 7.4.1708 (Core)
uname:Linux 3.10.0-693.2.2.el7.x86_64 #1 SMP Tue Sep 12 22:26:13 UTC 2017 x86_64
libc:glibc 2.17 NPTL 2.17
rlimit: STACK 8192k, CORE 0k, NPROC 15088, NOFILE 65535, AS infinity
load average:0.05 0.05 0.05
/proc/meminfo:
MemTotal:        3881692 kB
MemFree:         2567724 kB
MemAvailable:    2968640 kB
Buffers:           69016 kB
Cached:           536116 kB
SwapCached:            0 kB
Active:           355280 kB
Inactive:         326020 kB
...
VmallocTotal:   34359738367 kB
VmallocUsed:       14280 kB
VmallocChunk:   34359715580 kB
HardwareCorrupted:     0 kB
AnonHugePages:     30720 kB
HugePages_Total:     256
HugePages_Free:      256
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:       57216 kB
DirectMap2M:     3088384 kB
DirectMap1G:     3145728 kB
....
Memory: 4k page, physical 3881692k(2567600k free), swap 0k(0k free)
vm_info: OpenJDK 64-Bit Server VM (25.242-b08) for linux-amd64 JRE (1.8.0_242-b08), built on Jan 28 2020 14:28:22 by "mockbuild" with gcc 4.8.5 20150623 (Red Hat 4.8.5-39)
time: Thu Feb 20 15:13:30 2020
timezone: CST
elapsed time: 0 seconds (0d 0h 0m 0s)

重現失敗,繼續分析

  1. Java 測試證實正常申請內存不會有問題,超額的內存纔會 OOM,那麼爲何超額呢,視線迴歸到  sysctl -p 有所發現。

vm.overcommit_memory=2

關於 overcommit_memory 設置項:

overcommit_memory=0

默認設置,當應用進程嘗試申請內存時,內核會作一個檢測。內核將檢查是否有足夠的可用內存供應用進程使用;

若是有足夠的可用內存,內存申請容許;不然,內存申請失敗,並把錯誤返回給應用進程。

舉個例子,好比1G的機器,A進程已經使用了500M,當有另外進程嘗試malloc 500M的內存時,內核就會進行check,發現超出剩餘可用內存,就會提示失敗。

overcommit_memory=1

對於內存的申請請求,內核不會作任何check,直到物理內存用完,觸發 OOM 殺用戶態進程。

一樣是上面的例子,1G 的機器,A進程500M,B進程嘗試 malloc 500M,會成功,可是一旦kernel發現內存使用率接近1個G(內核有策略),就觸發OOM,殺掉一些用戶態的進程(有策略的殺)。

overcommit_memory=2

當請求申請的內存 >= SWAP內存大小 + 物理內存 * N,則拒絕這次內存申請。解釋下這個N:N是一個百分比,根據overcommit_ratio/100來肯定,好比overcommit_ratio=50(個人測試機默認50%),那麼N就是50%。 vm.overcommit_ratio 只有當 vm.overcommit_memory = 2 的時候纔會生效,內存可申請內存爲 SWAP內存大小 + 物理內存 * overcommit_ratio/100

看看上面日誌的 overcommit 信息:

  • CommitLimit: 4004708 kB (小於客戶申請的4096M)
  • Committed_AS: 2061568 kB

具體而言:

  • CommitLimit:最大能分配的內存(測試下來在 vm.overcommit_memory=2時候生效),具體的值是:SWAP內存大小(ecs均未開啓) + 物理內存 * overcommit_ratio / 100;
  • Committed_AS:當前已經分配的內存大小;

5,兩相對照,說明客戶設置的 vm.overcommit_memory在生效,建議改回 0 再試試。

  • 用  vm.overcommit_memory = 2 測試,分配內存失敗;
[root@test ~]# grep -i commit /proc/meminfo
CommitLimit:     1940844 kB
Committed_AS:     480352 kB
# java -Xms2048M -version 失敗了
OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000080000000, 1431830528, 0) failed; error='Cannot allocate memory' (errno=12)
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 1431830528 bytes for committing reserved memory.
# An error report file with more information is saved as:
# /root/hs_err_pid1267.log
  • 用以下配置,便可恢復:  vm.overcommit_memory = 0, vm.overcommit_ratio = 50
#vm.overcommit_memory = 0
#vm.overcommit_ratio = 50
[root@test ~]# java -Xms2048M -version
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (build 1.8.0_242-b08)
OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)

最後

能夠看出,這其實跟具體的編程語言沒有關係,用 Java 申請不到,用 c++/c 也同樣。一個容易忽略的小知識點,你 get 到了嗎?


ECS運維指南之Linux系統診斷

本文節選自《ECS運維指南之Linux系統診斷》,《ECS運維指南之Linux系統診斷》是牧原嘔心瀝血之做,不只內容精益求精,代碼的編排做者也花了很多心思。你能夠直接登陸阿里雲開發者社區下載本書——《ECS運維指南之Linux系統診斷》,或者直接在公衆號後臺回覆關鍵字「ecs」獲取本合集。

阿里雲開發者社區有很多高質量技術文章,你們能夠去觀摩學習,有不少書籍都是能夠直接免費下載的。

後記

以爲本號分享的文章有價值,記得添加星標哦。周更很累,不要白 piao,須要來點正反饋,安排個 「一鍵三連」(點贊、在看、分享)如何?😝 這將是我持續輸出優質文章的最強動力。


推 薦 閱 讀



程序猿石頭 


程序猿石頭(ID: tangleithu),現任阿里巴巴技術專家,清華學渣,前大疆後端 Leader。用不一樣的視角分享高質量技術文章,以每篇文章都讓人有收穫爲目的,歡迎關注,交流和指導!掃碼回覆關鍵字 「1024」 獲取程序員大廠面試指南


本文分享自微信公衆號 - 程序猿石頭(tangleithu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索