Linux环境下Java运行时获取已加载本地库内存占用的方法
问题描述
我在Linux系统上通过JVM运行Java应用,希望获取应用初始化阶段加载的特定本地库Tested_Lib的内存消耗。当前加载该库的代码如下:
public class Test { static { System.loadLibrary("Tested_Lib"); } public int register () { ......} public int unregister () { ......} }
我有两个疑问:
- 这种加载方式是否会将该库的全部大小加载至JVM地址空间?
- 如何在运行时测量
Tested_Lib的内存占用?
我曾尝试以下方法,但得到的结果与预期的库大小相差甚远:
System.gc(); usedMemoryBefore = runtime.totalMemory() - runtime.freeMemory(); System.loadLibrary("Tested_Lib"); usedMemoryAfter = runtime.totalMemory() - runtime.freeMemory(); libSizeInRam = (usedMemoryAfter - usedMemoryBefore)/KILOBYTE;
解答
疑问1:加载方式是否会把库的全部大小加载到JVM地址空间?
不会。Linux系统采用内存分页和**按需加载(懒加载)**机制处理动态库:
- 动态库的代码段(
.text)会被多个进程共享,且只有当实际执行到对应代码页时才会加载到物理内存; - 数据段(
.data、.bss)属于进程私有,加载时仅分配虚拟内存空间,物理内存同样是在实际写入时才真正分配; System.loadLibrary底层调用Linux的dlopen接口,它只会把库映射到进程的虚拟地址空间,不会立刻将整个库的所有内容加载到物理内存。
疑问2:如何运行时测量Tested_Lib的内存占用?
你之前用JVM堆内存统计的方法无效,因为totalMemory()和freeMemory()仅能统计JVM堆内存的使用情况,而本地库的内存属于进程堆外内存,不在JVM堆的统计范围内。推荐以下几种Linux下的可行方法:
方法1:使用pmap工具
直接查看进程的内存映射,定位Tested_Lib的内存占用:
- 获取Java进程PID:执行
jps或ps aux | grep java; - 执行
pmap -x <PID>,在输出中找到Tested_Lib对应的行:RSS列是该库实际占用的物理内存大小;SIZE列是该库映射的虚拟地址空间大小。
方法2:解析/proc/<PID>/smaps文件
这是更精确的统计方式,/proc文件系统提供了进程的详细内存映射信息:
- 执行
cat /proc/<PID>/smaps | grep -A 10 "Tested_Lib",输出包含该库各段的内存统计:Rss:物理内存占用;Pss:考虑共享后的实际占用(代码段为多进程共享,Pss会按共享进程数分摊);Private_Dirty:进程私有的脏内存(比如修改过的数据段)。
方法3:在代码中调用Linux系统API
如果需要在Java程序内获取,可以通过JNI调用Linux系统函数:
- 调用
dlopen打开库后,使用dlinfo获取库的加载地址和大小; - 结合
/proc/self/smaps解析对应地址范围的内存占用。
内容的提问来源于stack exchange,提问作者Islam Gamal Abd El-Naser
相关产品推荐
相关产品推荐

