V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
gzk329
V2EX  ›  Java

[求助] Spring 相关,使用了 MultipartFile 进行文件上传,但是抛出了 OOM?

  •  
  •   gzk329 · 2023-08-15 11:42:18 +08:00 · 1895 次点击
    这是一个创建于 499 天前的主题,其中的信息可能已经有所发展或是发生改变。

    异常提示是直接缓冲区不足,堆外的,没有做最大大小限制,我的程序应该没有内存泄漏
    直接缓冲区的 memory 情况
    ------- used total max usage
    direct 183M 183M - 100.00%

    org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: Direct buffer memoryHandler dispatch failed; nested exception is java.lang.OutOfMemoryError: Direct buffer memory

    这种异常考虑是怎么产生的?我重启过程序进程,有些不好排查了

    我感觉是服务器当时内存不够了导致的?但是内存很大了 服务器内存使用情况
    free -h
    ------- total used free shared buff/cache available
    Mem: 251G 118G 54G 1.1G 79G 130G
    Swap: 4.0G 0B 4.0G

    目前这个情况也不能复现了,应该怎么继续排查?

    第 1 条附言  ·  2023-08-16 15:50:01 +08:00

    NIO中Bits类下面,这个预留内存的占用量一直没有降下去导致的,这个为啥没被清理啊?

    12 条回复    2023-09-05 15:39:57 +08:00
    shuson
        1
    shuson  
       2023-08-15 12:18:18 +08:00
    jvm vm arguments 加上个 -XX:MaxDirectMemorySize=512m
    gzk329
        2
    gzk329  
    OP
       2023-08-15 13:04:40 +08:00
    @shuson 但是我现在是 我没做限制的呀 理论上不应该是无限大到物理内存的上限吗?
    zed1018
        4
    zed1018  
       2023-08-15 13:28:03 +08:00   ❤️ 1
    感谢楼上,其实如果有 oss ,我更建议是做一次性 token 让 client 直接传到 oss 里
    Nooooobycat
        5
    Nooooobycat  
       2023-08-15 13:46:46 +08:00
    直接把上传文件的核心代码发上来吧? 说不定 GPT 能帮你 debug 。
    gzk329
        6
    gzk329  
    OP
       2023-08-15 13:54:47 +08:00
    @Nooooobycat 第一层上传直接用的 Spring 的 MultipartFile , 不是我写的,在这一层就 Direct buffer memory 不够了
    gzk329
        7
    gzk329  
    OP
       2023-08-15 18:00:12 +08:00
    这 Spring 的 MultipartFile 是不是有点离谱啊 各种 OOM 用了直接内存区 又不清理
    arloor
        8
    arloor  
       2023-08-15 18:59:40 +08:00
    看下代码
    xinshoushanglu
        9
    xinshoushanglu  
       2023-08-15 19:33:34 +08:00
    前端上传东西不限制大小的么?传大文件 也直接走 表单上传 那不是对后端压力山大,建议走前端上传 oss 的方式
    gzk329
        10
    gzk329  
    OP
       2023-08-16 08:26:49 +08:00
    我研究了下好像是因为使用方式的问题,multipartFile.getBytes()这种方式会导致直接内存区使用增加,还不会释放,
    multipartFile.transferTo(tempFile)这个方法就不会。

    multipartFile.getBytes() 这个方法好像是会拷贝一份 byte[]出来,但是这种不是应该在堆内吗,用完了也就 GC 了,咋会导致直接内存区不断增,一直到溢出啊?

    // try (OutputStream out = Files.newOutputStream(tempFile.toPath())) {
    // out.write(multipartFile.getBytes());
    // } catch (IOException e) {
    // e.printStackTrace();
    // }
    gzk329
        11
    gzk329  
    OP
       2023-08-17 08:42:07 +08:00
    定位出一个 jdk 的 bug 了,给 oracle 提了 bug ,多久会有邮件回复啊?
    gzk329
        12
    gzk329  
    OP
       2023-09-05 15:39:57 +08:00
    这个问题我后来定位出来了,使用 JDK 8
    try-with-resource 机制 自动关闭 Files.newOutputStream 生成的流,流正常关闭,堆外内存释放,但是 java.lang.nio.Bits 中的 reserveMemory ,这个内存占用没有被清理,多次操作之后会 OOM 。
    改写为 try-with-resource 机制 自动关闭 FileOutputStream 流,就不会有这个问题。
    所以推断是 JDK 的 bug
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3604 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 00:53 · PVG 08:53 · LAX 16:53 · JFK 19:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.