安顺市网站建设_网站建设公司_Django_seo优化
2026/1/16 9:15:15 网站建设 项目流程
  • SpringBoot 在一次 http 请求中耗费了多少内存?
    • 先说说为啥会关心这个问题
    • 先搞懂:一次 HTTP 请求,SpringBoot 到底在干啥?
    • 动手测:最简单的接口,到底耗多少内存?
      • 第一步:准备测试代码
      • 第二步:测试结果
      • 第三步:加一点业务逻辑,再测
    • 影响内存消耗的几个关键因素
    • 我是怎么优化的?
    • 最后聊聊我的看法
      • 总结

SpringBoot 在一次 http 请求中耗费了多少内存?

最近一直在琢磨一个问题:我们平时写的 SpringBoot 接口,处理一次普通的 HTTP 请求到底要耗多少内存?之前只关注接口快不快、会不会报错,从没细算过内存账,这两天抽时间测了下,聊聊我的发现,不一定全对,就是自己的一点心得。

先说说为啥会关心这个问题

其实一开始我也没当回事,直到有次压测小接口,明明 CPU 使用率才 30% 多,服务却开始频繁卡顿,看监控发现堆内存涨得飞快,才意识到“单次请求内存消耗”这事儿不能忽略。在我看来,哪怕单次耗得少,高并发下积少成多,也可能让 JVM 频繁 GC,甚至 OOM,所以还是有必要搞清楚的。

先搞懂:一次 HTTP 请求,SpringBoot 到底在干啥?

要算内存账,得先知道请求进来后,SpringBoot 都创建了哪些东西。我简单梳理了下,一个普通的 GET 请求过来,至少会有这些开销:

  1. Tomcat 接请求,创建 Request/Response 对象;
  2. SpringMVC 解析请求参数,创建 HandlerMethod、参数绑定相关的对象;
  3. 我们自己写的 Service、Mapper 层对象,还有查询出来的业务数据对象;
  4. 日志对象、上下文对象(比如 RequestContextHolder);
  5. 请求处理完,这些对象大多会变成垃圾,等 GC 回收。

这些对象加起来,就是一次请求的内存消耗核心部分。

动手测:最简单的接口,到底耗多少内存?

光说不练假把式,我写了个最基础的 SpringBoot 接口,啥业务逻辑都没有,就返回个字符串,来测测内存消耗。

第一步:准备测试代码

先建个普通的 SpringBoot 项目,引入 web 依赖,然后写个接口:

@RestController@RequestMapping("/test")publicclassTestController{// 记录初始内存(单位:字节)privatelonginitialMemory=0;@GetMapping("/empty")publicStringemptyRequest(){// 获取当前线程的内存使用(堆内存)Runtimeruntime=Runtime.getRuntime();if(initialMemory==0){// 第一次请求,记录初始内存(避免初始化影响)initialMemory=runtime.totalMemory()-runtime.freeMemory();return"初始化完成";}// 处理请求时的内存longcurrentMemory=runtime.totalMemory()-runtime.freeMemory();// 单次请求消耗 = 当前内存 - 初始内存longconsume=currentMemory-initialMemory;// 打印消耗(转成 KB,方便看)System.out.println("单次空请求内存消耗:"+consume/1024+" KB");return"ok";}}

第二步:测试结果

启动项目后,先访问一次/test/empty做初始化,然后再访问几次,得到的结果大概是:
第一次(初始化后):约 10~20 KB
后续多次访问:稳定在 2~5 KB 左右

这个结果还挺意外的,纯空请求居然也要耗这么点内存?后来我查了下,主要是 Tomcat 和 SpringMVC 底层的对象开销,哪怕没业务逻辑,这些基础对象还是要创建的。

第三步:加一点业务逻辑,再测

光测空接口没意义,我再加个简单的业务逻辑,比如查询个列表(模拟真实场景):

// 模拟业务对象classUser{privateLongid;privateStringname;privateIntegerage;// 省略 getter/setter}@GetMapping("/with-data")publicList<User>withDataRequest(){Runtimeruntime=Runtime.getRuntime();longstartMemory=runtime.totalMemory()-runtime.freeMemory();// 模拟查询10个用户数据List<User>userList=newArrayList<>();for(inti=0;i<10;i++){Useruser=newUser();user.setId((long)i);user.setName("测试用户"+i);user.setAge(20+i);userList.add(user);}// 计算消耗longendMemory=runtime.totalMemory()-runtime.freeMemory();longconsume=endMemory-startMemory;System.out.println("带10个User对象的请求内存消耗:"+consume/1024+" KB");returnuserList;}

这次测试结果大概是:单次消耗约 30~50 KB。
能看出来,业务数据对象是内存消耗的大头,对象越多、字段越复杂,耗得就越多。

影响内存消耗的几个关键因素

测完之后,我总结了下,影响单次请求内存消耗的因素主要有这些:

  1. 请求参数和返回数据:参数越多、返回的 JSON 数据越大(比如列表里的对象多),内存耗得越多,这是最直观的;
  2. 中间件和框架开销:比如用了 RedisTemplate、MyBatis,这些框架会创建连接对象、Mapper 代理对象,都会占内存;
  3. 日志和监控:如果日志级别设为 DEBUG,会创建大量日志字符串对象;加了 SkyWalking、Pinpoint 这类监控,也会多一层对象开销;
  4. 线程本地变量(ThreadLocal):如果代码里用了 ThreadLocal 没及时清理,对象会一直占着内存,直到线程销毁;
  5. 临时对象:比如循环里创建字符串、List 没复用,会产生大量临时对象,虽然 GC 能回收,但高并发下还是会增加内存压力。

我是怎么优化的?

知道了消耗点,我们的经验是从这几个地方入手优化:

  1. 复用对象:比如频繁创建的 List、Map,用 ThreadLocal 缓存起来复用,避免每次请求都 new;
  2. 控制返回数据量:分页查询一定要做,别一次性返回几千条数据,不仅耗内存,网络传输也慢;
  3. 减少不必要的对象创建:比如字符串拼接用 StringBuilder,别用 “+” 号;循环里别创建匿名内部类;
  4. 及时清理 ThreadLocal:在请求结束时(比如用 @AfterReturning 切面)清理 ThreadLocal,避免内存泄漏;
  5. 调整 JVM 参数:比如设置合适的堆内存大小(-Xms、-Xmx),让 GC 更高效,减少内存碎片。

举个简单的优化例子,复用 List 对象:

// 优化前:每次请求都new ArrayList@GetMapping("/bad")publicList<User>badRequest(){List<User>list=newArrayList<>();// 每次都创建新对象// 业务逻辑...returnlist;}// 优化后:复用ThreadLocal里的ListprivateThreadLocal<List<User>>userListLocal=ThreadLocal.withInitial(ArrayList::new);@GetMapping("/good")publicList<User>goodRequest(){List<User>list=userListLocal.get();list.clear();// 清空复用// 业务逻辑...returnlist;}// 请求结束后清理@AfterReturning("/good")publicvoidclean(){userListLocal.remove();}

最后聊聊我的看法

其实问“一次请求耗多少内存”,没有固定答案,我认为核心不是纠结具体的数字,而是要有“内存意识”——知道哪些代码会耗内存,高并发下该怎么优化。

普通的业务接口,单次消耗几十 KB 其实很正常,只要不是几百 KB 甚至几 MB,一般都没问题。但如果是每秒几千、几万的高并发接口,哪怕单次只耗 10 KB,一秒钟也会耗掉几十 MB 内存,这时候就得精打细算了。

总的来说,SpringBoot 本身的基础开销是固定的,我们能优化的主要是业务代码里的对象创建和数据传输量。写代码时多留意一下,别随手 new 对象,别返回多余的数据,就能少耗不少内存。

总结

  1. SpringBoot 处理一次 HTTP 请求的内存消耗分两部分:框架基础开销(空请求约 2~5 KB)+ 业务对象开销(随数据量增加);
  2. 影响内存消耗的核心是返回数据量、临时对象创建、中间件开销这几点;
  3. 优化的关键是复用对象、控制数据量、及时清理 ThreadLocal,不用追求极致的内存消耗,重点是避免不必要的浪费。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询