多線程內存溢出產生的實戰分析

<code class="hljs oxygene has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">Caused <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">by</span>: java.lang.OutOfMemoryError: unable <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">create</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> native thread
    at java.lang.Thread.start0(Native <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Method</span>)
    <span class="hljs-title" style="box-sizing: border-box;">at</span> <span class="hljs-title" style="box-sizing: border-box;">java</span>.<span class="hljs-title" style="box-sizing: border-box;">lang</span>.<span class="hljs-title" style="box-sizing: border-box;">Thread</span>.<span class="hljs-title" style="box-sizing: border-box;">start</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(Thread.java:597)</span>
    <span class="hljs-title" style="box-sizing: border-box;">at</span> <span class="hljs-title" style="box-sizing: border-box;">java</span>.<span class="hljs-title" style="box-sizing: border-box;">util</span>.<span class="hljs-title" style="box-sizing: border-box;">Timer</span>.<<span class="hljs-title" style="box-sizing: border-box;">init</span>><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(Timer.java:154)</span></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

看到這個錯誤,我的第一感覺是創建了大量的線程,並且資源沒有被回收,但是報錯的卻是其中一臺應用服務器,表象看不太像是程序的問題,而此時在凌晨併發量也不應該會有這麼大啊?同時我們不能因爲報錯暫停服務使用,而影響商戶,所以決定要先解決問題,於是採用必殺技重啓這臺服務器,觀察一小時內存溢出消失,問題暫時解決。

第二天白天這個問題並沒有復現,我認爲這是偶發事件,就沒有過於在意,於是當晚再次出現內存溢出,並且還是隨機某一臺服務器爆出,我緊急找到監控部和系統部要求拿到棧信息內容和dump文件,然而並沒有。。。。我們緊急開會做代碼走查,發現這個應用其實是一個非常簡單的應用,裏面沒有使用線程,也沒有很複雜的邏輯,只是簡單的增刪改操作,那會是什麼原因呢?

接下來我們的分析之路開始了。。。。。

一、現狀情況

  1. 無法找到OOM時的javacore和heapdump文件。
  2. 無法還原問題發生時候系統內存被各個進程使用的佔比,CPU的佔比。
  3. 日誌沒有異常堆棧信息。

二、解決思路

1、要能夠驗證Tomcat配置內存溢出時打印堆棧並驗證可行性,並保證在上線和重啓不被擦除。 
現狀:當前只配置-XX:+HeapDumpOnOutOfMemoryError”,沒有配置路徑,不知道是被重啓刪除還是沒有產生。

2、實現腳本,在出現OOM的時候,重啓前,打印jstack棧信息和dump文件。 
現狀:目前是人工使用jmap和jstack打印CPU和內存信息給應急人員看。

3、實現JVM內存監控,在JVM內存緊張的時候提前報警,人工干預。 
現狀:

4.、監控或者實現腳本收集Java進程OOM的時候,各個進程對內存的佔比等,以及監控cache, buffer等。 
現狀:根據凌晨OOM的情況,錯誤堆棧表明,start0是JVM申請系統內存時內存不夠,並非jvm堆內存不夠而導致,需要上面信息查看詳細的系統。

5、研究底層,尋找java.lang.OutOfMemoryError: unable to create new native thread錯誤的引發原因有哪些。

6、針對項目進行壓測發現問題點。

三、深層次測試研究

測試環境 
**操作系統:**centos 7 64bit 
**Linux內核:**Linux centos 3.10.0-327.10.1.el7.x86_64 
**配置:**1G內存 
**虛擬機工具:**virtual box 64bit 
JDK: 
openjdk version “1.8.0_71” 
OpenJDK Runtime Environment (build 1.8.0_71-b15) 
OpenJDK 64-Bit Server VM (build 25.71-b15, mixed mode)

代碼如下:

<code class="hljs java has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * PROJECT_NAME: test
 * CREATE BY:    chao.cheng
 **/</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">OOMTest</span> {</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">main</span>(String[] args) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> IOException {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (args.length < <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) {
            System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"please input thread numbers!"</span>);
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
        }

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> threadNumber = Integer.parseInt(args[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>]);

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; i < threadNumber; i++) {
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Thread() {
                    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() {
                        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>) {
                            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
                                Thread.sleep(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1000</span>);
                            } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                }.start();
                System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Thread "</span> + i);
            }
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Throwable e) {
            e.printStackTrace();
        }
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li></ul>

1、第一次測試過程: 
輸入命令如下:

<code class="hljs lasso has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">   ulimit <span class="hljs-attribute" style="box-sizing: border-box;">-u</span>
   顯示:<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3584</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

注: ulimit -u是顯示用戶最多可開啓的程序數目。

程序JVM參數設置如下:

<code class="hljs ruby has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">java <span class="hljs-constant" style="box-sizing: border-box;">OOMTest</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4000</span> -<span class="hljs-constant" style="box-sizing: border-box;">Xmx500m</span> -<span class="hljs-constant" style="box-sizing: border-box;">Xss2m</span> -<span class="hljs-constant" style="box-sizing: border-box;">XX</span><span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">:+HeapDumpOnOutOfMemoryError</span> -<span class="hljs-constant" style="box-sizing: border-box;">XX</span><span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">:HeapDumpPath=/tmp</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

運行結果如下:

這裏寫圖片描述 
查看系統內存如下: 
這裏寫圖片描述

結論分析: 
a、在tmp目錄下沒有生成dump文件。 
我們需要注意,使用-XX:+HeapDumpOnOutOfMemoryError參數的時候,並不一定在任何溢出場景下都會產生dump文件

b、系統內存還有很多,卻無法創建線程了。感覺是系統中存在的進程/線程已經達到系統配置的極限。

2、第二次測試過程:

使用ulimit -u 65535命令或者直接修改limits.conf文件,將max user process參數修改爲65535。

jvm參數配置如下:

<code class="hljs ruby has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">java <span class="hljs-constant" style="box-sizing: border-box;">OOMTest</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4000</span> -<span class="hljs-constant" style="box-sizing: border-box;">Xmx500m</span> -<span class="hljs-constant" style="box-sizing: border-box;">Xss2m</span> -<span class="hljs-constant" style="box-sizing: border-box;">XX</span><span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">:+HeapDumpOnOutOfMemoryError</span> -<span class="hljs-constant" style="box-sizing: border-box;">XX</span><span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">:HeapDumpPath=/tmp</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

程序運行結果如下: 
並沒有報任何錯誤

這裏寫圖片描述

修改jvm參數爲:

<code class="hljs ruby has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">java <span class="hljs-constant" style="box-sizing: border-box;">OOMTest</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">40000</span> -<span class="hljs-constant" style="box-sizing: border-box;">Xmx500m</span> -<span class="hljs-constant" style="box-sizing: border-box;">Xss2m</span> -<span class="hljs-constant" style="box-sizing: border-box;">XX</span><span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">:+HeapDumpOnOutOfMemoryError</span> -<span class="hljs-constant" style="box-sizing: border-box;">XX</span><span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">:HeapDumpPath=/tmp</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

配置只是修改了創建線程數爲40000,比上一次4000多了10倍。

程序運行結果如下:

這裏寫圖片描述 
查看內存如下:

這裏寫圖片描述 
結論分析: 
此時已經把系統的內存全部耗盡,無法使用free、top命令,此時已經無法執行任何命令

3、綜合分析 
a、當max user processers 設置的較小的時候,影響系統線程數目的是max user processers的設置 
b、當max user processers設置爲65535的時候,影響系統線程數目的是系統的內存。 
c、對外的異常信息均爲:OOM:unable to create native thread。

四、多線程內存溢出的理論支撐

通過上面的分析,我們看到其實多線程內存溢出有很大原因是因爲系統設置和內存大小造成的,那麼我們如何來分析當前系統配置能夠支持多少線程呢?

對於java中的線程,我之前的理解一直是在java中new新線程的時候是直接使用jvm的內存,可實際情況卻不是這樣的。在java中每個線程需要分配線程內存,用來存儲自身的線程變量,在jdk1.4中每個線程是256K的內存,在jdk1.5中每個線程是1M的內存,jdk1.6以上版本不太清楚。在java中每new一個線程,jvm都是向操作系統請求new一個本地線程,此時操作系統會使用剩餘的內存空間來爲線程分配內存,而不是使用jvm的內存。這樣,當操作系統的可用內存越少,則jvm可用創建的新線程也就越少,我們舉一個例子如下:

總內存 -Xmx -Xms -Xss 剩餘內存 線程數
1024M 256M 256M 1M 768M 768

根據上面的知識,於是衍生出了下面這樣的通用公式: 
(MaxProcessMemory – JVMMemory – ReservedOsMemory) / (ThreadStackSize) = Number of threads 
注: 
MaxProcessMemory:進程最大尋址空間。 
JVMMMEMORY:jvm的內存空間(堆+永久區)-Xmx大小 (應該是實際分配大小) 
ReservedOsMemory:操作系統預留內存 
ThreadStackSize:-Xss大小

五、信息文件的導出

文章開始的時候說過,在內存溢出的時候,因爲服務器重啓導致jstack內容消失了,雖然配置了jvm參數HeapDumpOnOutOfMemoryError,但並沒有產生相應的dump文件,於是我們採用腳本導出的方式,內容如下:

<code class="hljs bash has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-shebang" style="color: rgb(0, 102, 102); box-sizing: border-box;">#!/bin/bash</span>
ps -Leo pid,lwp,user,pcpu,pmem,cmd > /tmp/ps.log
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">echo</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>
pid=`ps aux|grep tomcat|grep xxx|awk -F <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">' '</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'{print $2}'</span>`
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">echo</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>
pstack <span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">$pid</span> >/tmp/pstack.log
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">echo</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3</span>
lsof > /tmp/sys-o-files.log
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">echo</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span>
lsof -p <span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">$pid</span> > /tmp/service-o-files.log
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">echo</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5</span>
jstack <span class="hljs-operator" style="box-sizing: border-box;">-l</span> <span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">$pid</span>  > /tmp/js.log
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">echo</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">6</span> 
free -m >/tmp/free.log
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">echo</span> end

vmstat <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>

在系統異常的時候,監控系統能夠自動調用腳本產生信息文件,有了這些文件分析問題才能夠得心應手,不然出了問題根本無從查起,只能是沒頭蒼蠅亂撞。

六、壓測分析

在壓測環境中配置與生產環境一樣的硬件環境和配置環境進行壓測,可以看到如下的測試圖:

這裏寫圖片描述

通過壓測分析,在程序併發線程達到1010個的時候,就報出unable to create new native thread異常,查看上面這張圖其實不難看出,應用程序中並沒有使用線程,但是在Log4j中卻大量的使用了synchronized這個關鍵字,在併發非常高的時候會產生非常多的阻塞,最終內存資源耗盡報出內存溢出錯誤。

七、問題解決方案

問題解決方案

1、優化程序,減少沒用的log4j日誌輸出,將log4j日誌改爲異步+buffer的模式。 
2、單臺服務器本身性能有限,通過增加服務器的方式提高擴展性。 
3、將系統的一些限制屬性增大,如:ulimit -a。

通用解決方案

上面的四點是針對我們的解決方案,但通過以上的這些分析,我們不難發現所有的unable to create new native thread的錯誤異常都有其共性的地方,那接下來我再總結一些相對通用一點的方法幫助大家在以後遇到類似的問題的時候,能夠有據可查知道如何進行逐步的排查。

1、當發現這個錯誤的時候,第一時間要排查程序是否有bug,是否大量的創建了線程,或者沒有正確使用線程池,比如:是否使用了Executors.newCachedThreadPool()方法,該方法能創建Integer最大值個線程,創建到一定程度的時候系統資源耗盡就會報錯。

2、如果發現程序中並沒有使用線程卻依然報這個錯,那麼觀察一下這個時刻的併發情況如何,要是溢出的這一時刻比其他時候併發量都要大,這時先查看一下系統資源的情況,使用ulimit –a查看max user processes和open files這二個屬性的值越大,能創建的線程數也就越大。 
3、如果以上二個屬性調大依然報錯的話,說明此時受限於系統內存資源了,要是服務器本身內存就比較小的話,建議增加內存。要是服務器內存比較大,就需要通過調整jvm參數來增加線程使用的內存,比如減小-Xss值,這個值越小能創建的線程數也就越多,也可以適當減少-Xmx和-Xms的值,增加堆外內存的容量。

八、回顧總結

在我們排查整個內存溢出問題的過程中,其實耗費了挺長時間,而且報錯的時間基本都是在晚上,分析交易量看到這個時間段的併發量確實比白天要高,給我們最大的啓示是發生問題的時候,不能很快的定位問題原因,沒有最重要的報錯日誌可供分析。基於此我們開發了JAVA探針功能,可以實時採集當前服務器的內存使用情況、JVM堆使用情況,棧使用情況等等,並且能夠提前預警,界面類似下面這樣:

這裏寫圖片描述

這裏寫圖片描述 
注:第一張圖顯示的內存使用的百分比,第二張圖可以查看一段時間jvm內存使用情況,當高峯期來臨時可以提前預警。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章