You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用Memory.close()后Java进程内存仍上涨的原因排查

问题分析与解答

问题描述

运行下述Java程序后,进程占用内存从83M升至99M。监测发现内存多次增长约1.4M(与bytes数组大小近似),怀疑Memory.close()偶尔未生效?理论上进程占用内存不应为程序自身内存加分配的2M吗?

环境:JDK 11,JNA 5.13

代码示例:

public static void main(String[] args) throws IOException {
    File imageFile = new File("test.jpeg");
    BufferedImage image = ImageIO.read(imageFile);
    byte[] bytes = bufferedImageToByteArray(image);
    for (int i = 0; i < 100; i++) {
        Pointer data = new Memory(2 * 1024 * 1024);
        data.write(0, bytes, 0, bytes.length);
        if(data instanceof Memory){
            ((Memory)data).close();
        }
    }
}

private static byte[] bufferedImageToByteArray(BufferedImage image) {
    int width = image.getWidth();
    int height = image.getHeight();
    byte[] byteArray = new byte[width * height * 3];
    Raster raster = image.getRaster();
    int numBands = raster.getNumBands();
    int index = 0;
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            int[] pixel = new int[numBands];
            raster.getPixel(x, y, pixel);
            // Blue
            byteArray[index++] = (byte) (pixel[2] & 0xFF);
            // Green
            byteArray[index++] = (byte) (pixel[1] & 0xFF);
            // Red
            byteArray[index++] = (byte) (pixel[0] & 0xFF);
        }
    }
    return byteArray;
}

实际场景内存利用率曲线:
实际场景内存利用率曲线

核心原因与解释

  • JNA Memory的内存管理逻辑
    JNA的Memory对象分配的是本地内存(非JVM堆内存),调用close()确实会释放这部分本地内存,但操作系统不会立即将释放的内存归还给系统,而是会将其作为进程的缓存保留,以便后续快速分配,因此进程的常驻内存(RSS)不会立刻回落至初始水平。

  • 内存增长与bytes数组的关联
    你观察到的1.4M增长和bytes数组大小近似,并非Memory未释放导致:

    • bufferedImageToByteArray生成的bytes数组存储在JVM堆中,堆内存的回收依赖GC触发,循环过程中GC不会立即执行,堆内存的临时占用会反映到进程整体内存中。
    • 本地内存释放后的系统缓存机制,叠加堆内存的延迟回收,共同造成了进程内存的阶段性增长。
  • 理论值与实际偏差的原因
    理想状态的“自身内存+2M”不会出现,因为:

    • 操作系统对已释放本地内存的缓存策略,会让进程占用内存高于理论值。
    • JVM堆内存的分配、GC的延迟执行,会导致堆内存暂时累积,进一步推高进程内存占用。

验证与解决方案

  • 验证Memory是否正确释放:使用JNA的Native.getNativeMemorySize()统计当前已分配的本地内存总量,在循环每次close()后打印该值,确认本地内存是否被正常释放。
  • 测试性触发GC:在循环末尾添加System.gc()(仅用于测试,生产环境不建议),观察堆内存是否被回收、进程内存是否下降。
  • 改用try-with-resources语法:JNA 5.x的Memory实现了AutoCloseable接口,用该语法可确保close()被可靠调用,避免手动判断的疏漏:
for (int i = 0; i < 100; i++) {
    try (Memory data = new Memory(2 * 1024 * 1024)) {
        data.write(0, bytes, 0, bytes.length);
    }
}

内容的提问来源于stack exchange,提问作者rocco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 10:56:17