【JVM】OOMError之Unable to create new native thread

       最近線上出現了一個OOM的問題,使得服務異常以至於不可用。

一、現象

       從現象來看就是請求服務全部失敗,線程數激增,cpu和內存顯示相對正常。查看error.log,都是"error.log:unable to create new native thread"錯誤:

[ERROR] [org.springframework.boot.web.servlet.support.ErrorPageFilter:190] -- Forwarding to error page from request [/a/b/c/d/e/f] due to exception [unable to create new native thread]
java.lang.OutOfMemoryError: unable to create new native thread
        at java.lang.Thread.start0(Native Method)
        at java.lang.Thread.start(Thread.java:714)
        at sun.net.www.http.KeepAliveCache$1.run(KeepAliveCache.java:112)
        at sun.net.www.http.KeepAliveCache$1.run(KeepAliveCache.java:96)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.net.www.http.KeepAliveCache.put(KeepAliveCache.java:95)
        at sun.net.www.http.HttpClient.putInKeepAliveCache(HttpClient.java:407)
        at sun.net.www.http.HttpClient.finished(HttpClient.java:364)
        at sun.net.www.http.ChunkedInputStream.closeUnderlying(ChunkedInputStream.java:219)
        at sun.net.www.http.ChunkedInputStream.processRaw(ChunkedInputStream.java:455)
        at sun.net.www.http.ChunkedInputStream.readAheadNonBlocking(ChunkedInputStream.java:520)
        at sun.net.www.http.ChunkedInputStream.readAhead(ChunkedInputStream.java:611)
        at sun.net.www.http.ChunkedInputStream.available(ChunkedInputStream.java:725)
        at java.io.FilterInputStream.available(FilterInputStream.java:168)

很明顯是說無法創建本地線程了。

 

二、分析

通過jstack來進行分析:

EndpointManagerSD update schedule : #4077 daemon prio=5 os_prio=5 tid=0x000007exxxx nid=xxxxx waiting on condition (xxxxxx)
java.lang.Thread.State: TIMED_WAITING(parking)
    at sun.misc.Unsafe.park(Native method)
    - parking to wait for <0x000000000xafa> (a java.util.concurrent.AbstractQueuedSynchronizer.java:xxxx)
    at java.util.concurrent.locks.LockSupport.parkNanos
    at 

發現大量線程處於:TIME_WAITING階段,根據關鍵字可以定位到代碼中的問題。

原因就是在生產的過程中一直在創建消息隊列的producer客戶端,producer.start()之後沒有close。

public void sendMsg(String msg) {
    Producer producer = new Procuer();
    producer.send(msg);
}

producer是我們發送消費的客戶端,包含一定的資源,如線程,網絡連接等,使用前進行初始化,使用完後,shutdown釋放資源。在我們的使用中,每一條消息創建一個producer,但是使用完後,沒有shutdown釋放資源,那麼隨着不斷new Producer,就會不斷創建新的線程而不釋放推薦使用方式是,服務啓動初始化producer,服務關閉時,釋放資源,中間保持長連接複用

該問題發生的常見過程主要包括以下幾步:

1.JVM 內部的應用程序請求創建一個新的 Java 線程;

2.JVM native方法代理了該次請求,並向操作系統請求創建一個 native 線程;

3.操作系統嘗試創建一個新的native 線程,併爲其分配內存;

4.如果操作系統的虛擬內存已耗盡,或是受到 32 位進程的地址空間限制,操作系統就會拒絕本次native 內存分配;

5.JVM將拋出 java.lang.OutOfMemoryError: Unable to create new native thread 錯誤。

能創建的線程數的具體計算公式如下:

(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads

MaxProcessMemory:指的是一個進程的最大內存

JVMMemory:JVM內存

ReservedOsMemory :保留的操作系統內存

ThreadStackSize:線程棧的大小

 

 

三、解決方案

1. 調用完之後將producer.close()。

public void sendMsg(String msg) {
    Producer producer = new Procuer();
    producer.send(msg);
    producer.close();
}

2.維持一個維持producer複用對象,使用ConcurrentHashMap來保存topic和producer引用。

3.通用方案:

a.如果程序中有bug,導致創建大量不需要的線程或者線程沒有及時回收,那麼必須解決這個bug,修改參數是不能解決問題的。

b.如果程序確實需要大量的線程,現有的設置不能達到要求,那麼可以通過修改MaxProcessMemory,JVMMemory,ThreadStackSize這三個因素,來增加能創建的線程數.

c.適當犧牲性能,優化代碼裏面的線程池的線程配置。

d.升級配置,爲機器提供更多的內存;

e.降低Java Heap Space大小;

f.修復應用程序的線程泄漏問題;

g.限制線程池大小;

h.使用 -Xss 參數減少線程棧的大小;

i.調高 OS 層面的線程最大數:執行 ulimia -a 查看最大線程數限制,使用 ulimit -u xxx 調整最大線程數限制。

 

Author:憶之獨秀

Email:[email protected]

轉載註明出處:https://blog.csdn.net/lavorange/article/details/103448504

 

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