Redis 報錯“OutOfDirectMemoryError(堆外記憶體溢位) ”問題如下:
一、報錯資訊:
使用 Redis 的業務介面 ,產生 OutOfDirectMemoryError
(堆外記憶體溢位),如圖:
格式化后的報錯資訊:
{
"timestamp": "2023-04-17 22:46:36",
"status": 500,
"error": "Internal Server Error",
"message": "Java heap space",
"trace": "java.lang.OutOfMemoryError: Java heap
......
}
二、報錯原因:
原始碼分析:
public final class PlatformDependent {
// 直接記憶體大小,可通過 “-Dio.netty.maxDirectMemory”引數設定,
private static final long DIRECT_MEMORY_LIMIT;
// 默認的最大直接記憶體大小 ,方法略,大概意思還是:先獲取“Eclipse OpenJ9”的“sun.misc.VM”引數,如果沒有則獲取JVM的“-XX:MaxDirectMemorySize”作為默認值,
private static final long MAX_DIRECT_MEMORY = maxDirectMemory0();
static {
// 其他賦值操作,略
// 給 直接記憶體大小賦值,如果有設定 "-Dio.netty.maxDirectMemory" 引數,則使用用戶設定的,如果沒有則使用默認的
logger.debug("-Dio.netty.maxDirectMemory: {} bytes", maxDirectMemory);
DIRECT_MEMORY_LIMIT = maxDirectMemory >= 1 ? maxDirectMemory : MAX_DIRECT_MEMORY;
}
private static void incrementMemoryCounter(int capacity) {
if (DIRECT_MEMORY_COUNTER != null) {
long newUsedMemory = DIRECT_MEMORY_COUNTER.addAndGet(capacity);
//關鍵判斷:如果 netty內部使用的記憶體大小 大于 “直接記憶體大小”的話,就拋出 "OutOfDirectMemoryError"例外,
if (newUsedMemory > DIRECT_MEMORY_LIMIT) {
DIRECT_MEMORY_COUNTER.addAndGet(-capacity);
throw new OutOfDirectMemoryError("failed to allocate " + capacity
+ " byte(s) of direct memory (used: " + (newUsedMemory - capacity)
+ ", max: " + DIRECT_MEMORY_LIMIT + ')');
}
}
}
}
總結原因:
1)、Springboot 2.x 以后默認使用 Lettuce作為操作 redis 的客戶端,它是使用 netty 進行網路通信的,
2)、從spring-boot-starter-data-redis(2.2.3.RELEASE) 依賴可以看出內置使用的確實是 Lettuce 客戶端,分析原始碼得知,lettuce 使用的 netty 框架,參考的netty包netty-common-4.1.43.Final.jar里面有一個PlatformDependent.java類 ,底層有個-Dio.netty.maxDirectMemory 引數,會自己校驗堆外記憶體是否大于當前服務可使用的記憶體,如果大于則拋出 OutOfDirectMemoryError(堆外記憶體溢位),顯然,這是屬于 Netty(netty-common-4.1.43.Final.jar)的bug導致堆外記憶體溢位的,
三、解決方法:
不能使用-Dio.netty.maxDirectMemory
只調大堆外記憶體,這只能延遲bug出現的時機,不能完全解決該問題,想解決有如下兩個方案:
1、升級 Lettuce客戶端,期待新版本會解決該問題,
2、排除 Lettuce客戶端,切換使用沒有該問題的 Jedis 客戶端,
Netty 框架性能更好,吞吐量更大,但是Springboot默認使用的Lettuce 客戶端對Netty的支持不夠好;
Jedis客戶端雖然沒有Netty更快,但勝在穩定,沒有上述bug,
因此下面使用第二種解決方案:
切換使用Jedis 客戶端:
在data-redis中排除lettuce,在引入jedis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<!--排除 lettuce -->
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入 jedis -->
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
四、檢驗結果:
壓力測驗:
測驗結果:
解決 Redis 報“OutOfDirectMemoryError(堆外記憶體溢位) 成功”
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/550600.html
標籤:其它
上一篇:day02-2-商鋪查詢快取
下一篇:sql陳述句優化