死磕內存篇2 --JAVA進程是怎麼忽然掛掉的

JVM內存不足致使進程死掉. Native memory allocation (mmap) failed to maphtml

一臺服務器上部署不少JAVA進程已是微服務的常態,但也有些坑。java

 

 

背景,測試服務器上的一些JAVA進程忽然掛掉,查看call back的日誌發現以下:linux

 

1spring

2ubuntu

3服務器

4微服務

5oop

6測試

7ui

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

# There is insufficient memory for the Java Runtime Environment to continue.

# Native memory allocation (mmap) failed to map 1786867712 bytes for committing reserved memory.

# Possible reasons:

# The system is out of physical RAM or swap space

# In 32 bit mode, the process size limit was hit

# Possible solutions:

# Reduce memory load on the system

# Increase physical memory or swap space

# Check if swap backing store is full

# Use 64 bit Java on a 64 bit OS

# Decrease Java heap size (-Xmx/-Xms)

# Decrease number of Java threads

# Decrease Java thread stack sizes (-Xss)

# Set larger code cache with -XX:ReservedCodeCacheSize=

# This output file may be truncated or incomplete.

#

# Out of Memory Error (os_linux.cpp:2673), pid=28610, tid=139813184919296

#

# JRE version: Java(TM) SE Runtime Environment (8.0_40-b26) (build 1.8.0_40-b26)

# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.40-b25 mixed mode linux-amd64 compressed oops)

 

#

  

很明顯是因爲服務器的內存不足形成

 

內存不夠用分兩種狀況

 

 推測:內存不夠用分兩種狀況

 

推測 1, JAVA程序使用的內存申請超出了JVM分配的, 此類只會拋出 

java.lang.OutOfMemoryError: Java heap space,JAVA進程是不會有影響,下次也能夠繼續接受請求提供服務

 

 

推測 2, JVM嘗試去像操做系統申請一塊內存超出系統的可以使用(RSS)內存時, 此時linux會結束JAVA進程,並拋出如上錯誤。

這種場景也常見: 如在一臺16G內存的服務器上跑着兩個服務,兩個服務同時啓動各自分配10G內存,由於JVM的垃圾回收機制通常狀況下是不會有問題,能正常服務,當兩個服務同時須要用到的內存超出16G時,將會被linux幹掉一個服務.

(注: JVM啓動時,經過-Xmx等參數分配的內存只會影響到VIRT ,詳見上一篇博客《JAVA進程和linux內存間的大小關係

 

 

 

證明:

去證明是最重要的, 上面的都是個人推測

一臺內存是1G的linux服務器

total used free shared buffers cached 
Mem:      984 119 864 0 2 20 
-/+         buffers/cache: 97 886 
Swap:      2044 45 1999

物理內存+swap內存 在 3G左右  (886+1999)

 

加入JAVA程序,由於是要觀察JAVA的內存超出是否會終止JAVA的進程,故思路以下設計

1,用一個線程去申請分配內存

2,主線程不停報告存活狀態

 程序以下

複製代碼

package org.hejinbin.memory.test;

import java.io.IOException;

/**
 * 用於演示內存不足致使JAVA進程退出, 注意:輸入的size勿致使超出int (1024 * 1024 * size)
 * 
 * @author 何錦彬 QQ 277803242(包子) 2016.12.29
 */
public class Test {
    public static void main(String[] args) throws IOException, InterruptedException {

        new Thread(new Runnable() {

            public void run() {

                try {
                    //接收測試內存的大小,單位m
                    byte[] byteSize = new byte[50];
                    System.out.print("input want to allocation memory:");
                    int temp = System.in.read(byteSize);
                    String sizeStr = new String(byteSize, 0, temp);
                    int size = Integer.parseInt(sizeStr.trim());
                    System.out.println(1024 * 1024 * size);
                    byte[] bt = new byte[1024 * 1024 * size];
                    System.out.println("succ..allocation: " + size + "m");
                    //阻30分,防止垃圾回收
                    Thread.sleep(1000 * 60 * 30);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }).start();

        while (true) {
            //1分鐘後報告
            Thread.sleep(1000 * 60);
            System.out.println(Thread.currentThread().getId() + ": i'm alive");
        }
    }
}

複製代碼

 

證明推測1:

運行程序,輸入1000 (這裏只要超過了年齡代內存其實就會拋出異常)

 

輸出 alive, 證實了推測1,使用內存超出了並不會致使JAVA進程死掉

 

 

證明推測2:

打開窗口1 , 運行,輸入

root@ubuntu:/home/hejinbin# java -Xmx3048m -Xms3048m org.hejinbin.memory.test.Test 

input want to allocation memory:

 

打開窗口2 , 運行,輸入

root@ubuntu:/home/hejinbin# java -Xmx3048m -Xms3048m org.hejinbin.memory.test.Test 

input want to allocation memory:

 

而後在窗口1輸入1500

運行以下:

input want to allocation memory:1500 
succ..allocation: 1500m

 

而後在窗口2,輸入1500

input want to allocation memory:1500 
succ..allocation: 1500m

 

此時發現窗口1的JAVA進程被殺掉了,完整以下

 

root@ubuntu:/home/hejinbin# java -Xmx3048m -Xms3048m org.hejinbin.memory.test.Test 
input want to allocation memory:1500 
succ..allocation: 1500m 
Killed

 

證明了推測2

結論: 現在微服務流行(趁點熱度)。不少進程同時在一臺服務器上跑, 必須注意,分配給JAVA的內存只和必定在服務器的可用內存以內。否則頗有可能忽然被linux幹掉一個進程

 

最後還有個覺得,爲啥個人進程被killed以後,並無產生hs_err_pid*.log的日誌呢? 

分類: JVM,JDK source analyse

相關文章
相關標籤/搜索