一次操作系統報錯OutOfMemory Error的處理記錄


在啟動公司內嵌的tomcat容器時出現報錯, 如下:

# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (malloc) failed to allocate 160088 bytes for AllocateHeap
# An error report file with more information is saved as:
# /users/xxx/hs_err_pidxxxx.log

然后查看/users/xxx/hs_err_pidxxxx.log內容:

#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 357564416 bytes for committing reserved memory.
# Possible reasons:
# The system is out of physical RAM or swap space
# The process is running with CompressedOops enabled, and the Java Heap may be blocking the growth of the native heap
# Possible solutions:
# Reduce memory load on the system
# Increase physical memory or swap space
# Check if swap backing store is full
# Decrease Java heap size (-Xmx/-Xms)
# Decrease number of Java threads
# Decrease Java thread stack sizes (-Xss)
# Set larger code cache with -XX:ReservedCodeCacheSize=
# JVM is running with Unscaled Compressed Oops mode in which the Java heap is
# placed in the first 4GB address space. The Java Heap base address is the
# maximum limit for the native heap growth. Please use -XX:HeapBaseMinAddress
# to set the Java Heap base and to place the Java Heap above 4GB virtual address.
# This output file may be truncated or incomplete.
#
# Out of Memory Error (os_linux.cpp:2749), pid=4252, tid=0x00007f3f38bb5700
#
# JRE version: (8.0_201-b09) (build )
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.201-b09 mixed mode linux-amd64 compressed oops)
# Core dump written. Default location: /users/ems/core or core.4252 (max size 521000 kB). To ensure a full core dump, try "ulimit -c unlimited" before starting Java again
#
...

翻譯過來就是本地內存分配失敗, 可能的原因有兩種

  1. 系統物理內存或虛擬內存不足
  2. 程序在壓縮指針模式下運行, Java堆會阻塞本地堆的增長

然后使用free -m命令查詢, 發現內存足夠:

             total       used       free     shared    buffers     cached
Mem:          7983       5415       2568          0        170       1460
-/+ buffers/cache:       3784       4199
Swap:        15257         71      15186

那么嘗試按第二個問題進行解決, 可能的方案有兩種:

  1. 禁止使用壓縮指針模式
    1. 方法: 在catalina.sh中JAVA_OPTS的值后面添加-XX:-UseCompressedOops, 再重啟tomcat
  2. 將Java堆的起始地址設置成使Java堆大小+起始地址大於4G,
    1. 原因: 請參考 https://blogs.oracle.com/poonam/running-on-a-64bit-platform-and-still-running-out-of-memory,
    2. 方法: 在這里我將起始地址簡單直接的設為4G即4294967296

在嘗試過這兩種方法后發現依然報同樣的錯誤
這時我在想會不會是堆內存過大, 導致系統無法分配內存, 於是進行嘗試: 把堆內存減少一半, 看看效果.

  1. 方法: 在在catalina.sh中JAVA_OPTS的值中把原來的-Xms1024m -Xmx2048m改為-Xms512m -Xmx1024m, 再重啟tomcat

結果JVM啟動成功, 問題解決.

后續思考: 為什么在可用內存充足的情況下系統無法分配給JVM更多內存? 一直沒有想到完美的解釋, 如果有明白的兄弟可以指教一下.

嘗試對后續思考進行解答: 原因應該還是內存不足, 可能操作系統會預留一些內存, 而我的機器上默認的啟動參數是-Xms1024m -Xmx2048m, 可能已經超過了系統允許分配的最高值, 因此無法分配內存. 當我使用java -Xms10m -Xmx20m可以啟動成功, java -Xms500m -Xmx2000m會失敗, 因此, 應該還是內存不足的問題

對后續思考的最終解答及該問題的完美解決方案:

這個問題是由於/proc/meminfo下的vm.overcommit_memory被設置成不允許overcommit造成的

首先了解一下overcommit的意思: 用戶進程申請的是虛擬地址, 而這個虛擬地址是不允許任意申請的, 因為虛擬內存需要物理內存做支撐, 如果分配太多虛擬內存, 會對性能參數影響. overcommit就是對虛擬內存的過量分配

vm.overcommit_memory的用處: 控制過量分配的策略. 這個參數一共有3個可選值:

  1. 0: Heuristic overcommit handling. 就是由操作系統自己決定過量分配策略
  2. 1: Always overcommit. 一直允許過量分配
  3. 2: Don't overcommit. 不允許過量分配

在這個案例里面, 使用sysctl vm.overcommit_memory來查看, 發現vm.overcommit_memory = 2, 即采用的是不允許過量分配的設置. 而在錯誤日志中也證明了這一點:

CommitLimit:    15951192 kB
Committed_AS:   15837036 kB

Committed_AS: OS會預測啟動這個程序時, 所有的進程可能會用到多少的內存, 如果超過了CommitLimit, 就會報錯
解決方案是sudo sysctl vm.overcommit_memory=0, 即vm.overcommit_memory = 0, 允許系統自己決定過量分配策略

原文地址:https://www.jianshu.com/p/3f8692eb3660


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM