首页 > 数据库 >Redis 报”OutOfDirectMemoryError“(堆外内存溢出)

Redis 报”OutOfDirectMemoryError“(堆外内存溢出)

时间:2023-04-17 23:35:45浏览次数:46  
标签:netty OutOfDirectMemoryError 堆外 Redis DIRECT 内存 MEMORY maxDirectMemory

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(堆外内存溢出) 成功”

标签:netty,OutOfDirectMemoryError,堆外,Redis,DIRECT,内存,MEMORY,maxDirectMemory
From: https://www.cnblogs.com/jxzy/p/17327958.html

相关文章

  • redis介绍,redis linux安装,redis启动,redis经典场景,redis通用命令,数据结构和内部编码,red
    目录redis介绍,redislinux安装,redis启动,redis经典场景,redis通用命令,数据结构和内部编码,redis字符串类型昨日内容回顾今日内容1redis介绍2redislinux下安装3redis启动方式3.1最简启动3.2动态参数启动3.3配置文件启动3.4客户端连接命令4redis经典场景5redis通用命令6数据......
  • 【redis】 redis linux下安装 redis启动方式 redis典型场景 redis通用命令 数据结构
    目录上节回顾今日内容1redis介绍2redislinux下安装3redis启动方式3.1最简启动3.2动态参数启动3.3配置文件启动3.4客户端连接命令4redis典型场景5redis通用命令6数据结构和内部编码7redis字符串类型练习上节回顾#dockerfile命令 RUNCOPYADDENV......
  • 【Azure Redis 缓存】Azure Redis 遇见的连接不上问题和数据丢失的情况解答
    问题描述PHP应用再连接AzureRedis服务时,出现ConnectionTimedout。当通过升级提高AzureRedis的性能时候,发现之前的数据丢失了。 问题解答当Redis服务出现Timeout的情况时,可以从Redis服务的指标(Metrics)开始查看,如果出现负载(ServiceLoad)很高的情况,表明当前Redis服务的资源已......
  • 【Azure Redis 缓存】Azure Redis 遇见的连接不上问题和数据丢失的情况解答
    问题描述PHP应用再连接AzureRedis服务时,出现ConnectionTimedout。当通过升级提高AzureRedis的性能时候,发现之前的数据丢失了。 问题解答当Redis服务出现Timeout的情况时,可以从Redis服务的指标(Metrics)开始查看,如果出现负载(ServiceLoad)很高的情况,表明当前Redis服务的......
  • Redis---数据持久化之RDB与AOF
    一、数据持久化之RDB1、RDB介绍Redis数据库文件,全称RedisDataBase,数据持久化方式之一,数据持久化默认方式,按照指定时间间隔,将内存中的数据及快照写入硬盘定义RDB文件名 dbfilename"dump.rdb"RDB指dump.rdb文件;redis数据每次存盘,如果不指定持久化的方式,数据就会默认......
  • redis
    今日内容1redis介绍#特性Redis特性(8个)#速度快:10wops(每秒10w读写),数据存在内存中,c语言实现,单线程模型#持久化:rdb和aof#多种数据结构: 5大数据结构BitMaps位图:布隆过滤器本质是字符串HyperLogLog:超小内存唯一值计数,12kbHyperLogLog本质是字符串GEO:地......
  • Redis --- 数据类型之列表 数据类型之hash
    一、数据类型之列表列表简介Redis的list是一个字符队列,先进后出,一个key可以有多个值列表操作lpushkeyvalues[value...]将一个或多个值value插入到列表key的表头,Key不存在,则创建key127.0.0.1:6379>FLUSHALLOK#lpush命令,创建变量student,类型为列表类型,值为bob......
  • Redis中的Hash类型常用命令
    一、hset命令作用:设置hash类型值;格式:hsetkeyfieldvalue案例:192.168.0.111:0>hsetproductname苹果"1"192.168.0.111:0>hsetproductprice5"1"二、hget命令作用:获取hash类型某个key下的某个field的值格式:hgetkeyfield案例:192.168.0.111:0>hgetproductname"苹果&qu......
  • 记录 RedisTemplate.executePipelined 使用问题
    需求,向redis写入2000万个key@Slf4j@Component("job2")publicclassToRedis2implementsIJob{privateAtomicLongcount=newAtomicLong(0);privateLongoldCount=0L;privateList<String>userIdList=newArrayList<>();pri......
  • Centos安装Redis
    下载Rediswgethttps://github.com/redis/redis/archive/7.0.10.tar.gz解压tar-zxf7.0.10.tar.gz-C/usr/local/cd/usr/local/redis-7.0.10mv/usr/local/redis-7.0.10//usr/local/rediscd/usr/local/redis/安装一些依赖yum-yinstallncursesncurses-developens......