调用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
相关产品推荐
相关产品推荐

