ski*_*iwi 7 java jvm memory-leaks java-8 java.nio.file
我遇到过一个错误,其中一个服务器应用程序每秒使用越来越多的内存,我已经设法筛选出一个仍然显示该行为的简短示例:
public class TestGetLastModifiedTime {
private static final Path PATH = Paths.get("D:\\test.txt");
private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);
public static void main(String[] args) {
SCHEDULER.scheduleAtFixedRate(() -> getLastModifiedTime(), 0, 1, TimeUnit.SECONDS);
}
private static void getLastModifiedTime() {
try {
FileTime lastModifiedTime = Files.getLastModifiedTime(PATH);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
}
Run Code Online (Sandbox Code Playgroud)
在Windows 8.1和Java 8u20上运行.
通过VisualVM,我观察到最大堆大小不会增长,堆本身也会不断增加.同时我在Windows任务管理器中观察到生成的java.exe进程每秒都在使用(保留)更多内存.
有趣的是,当我从VisualVM中执行GC时,所有使用的堆内存都会重置为几乎为零,并且java.exe进程的已用内存不会像预期那样收缩,因为它被认为是保留的.
但是,GC完成后,内存使用量仍然每秒都在增加,而现在肯定有足够的可用堆空间.
Metaspace也不受影响.
对我来说这真的很有气味,看起来像JVM有内存泄漏.
任何人都可以帮助我解释这里发生了什么吗?
由于以下原因,我不认为它是泄漏:
gc,内存使用量会回落到默认值.这不是泄漏的工作原理.当存在泄漏时,这些对象是强可访问的,即使在重复的垃圾收集之后,堆大小也不会显着降低.java -Xmx20M TestGetLastModifiedTime在我杀死这个过程之前,完全没法运行10分钟.如果有泄漏,它最终会抛出OutOfMemoryError或有太多重复的gcFiles.getLastModifiedTime(PATH),ExecutorService内部会产生小工具对象在这里和那里.所以对我来说看起来很完美.我机器上的行为:

关键是,Windows管理器显示更高的堆使用率.这是很有可能.如果需要,JVM可以保留空间.增加堆利用率可能肯定比正在进行gc.这完全有道理(当你富有的时候,为什么要经历紧缩?).
这就是为什么像观察Windows Manager/free -m等工具没有正确地观察内部发生的事情的原因.要快速估算,您可能希望:
jstat -gccapacity 9043 | tail -n 1 | awk '{ print $4, $5, $6, $10 }' | python -c "import sys; nums = [x.split(' ') for x in sys.stdin.readlines()]; print(str(sum([float(x) for x in nums[0]]) / 1024.0) + ' mb');"
# change the pid from 9043 to your jvm process id.
#jvm process id can be known by running `jcmd` command (available as part of jdk)
Run Code Online (Sandbox Code Playgroud)
这仍然比free -m/ Windows Manager 更好地估计