我遇到了一个错误,其中我们的一个服务器应用程序几乎每秒都在使用越来越多的内存,并且我设法过滤出一个简短的示例,该示例仍然显示了该行为:
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);
}
}
}
在Windows 8.1和Java 8u20上运行。
通过VisualVM,我观察到最大堆大小并没有增加,并且堆本身一直在增加。同时,我在Windows任务管理器中观察到,产生的java.exe进程每秒保持使用(保留)更多内存。
有趣的是,当我从VisualVM中执行GC时,所有使用的堆内存几乎都重置为零,并且java.exe进程的使用内存不会像预期的那样减少,因为它被认为是保留的。
但是,在完成GC之后,内存使用率仍然每秒增加一次,而现在肯定有足够的可用堆空间。
元空间也不受影响。
对我来说,这真的很香,看起来JVM有内存泄漏。
谁能帮我一下,解释一下这里发生了什么事?
最佳答案
由于以下原因,我不认为它是泄漏:
gc
时,内存使用率将恢复为默认值。这不是泄漏的工作方式。发生泄漏时,这些对象很容易到达,即使经过重复的垃圾回收,堆大小也不会显着减少。 java -Xmx20M TestGetLastModifiedTime
运行了10分钟完全正常。如果发生泄漏,它将最终抛出OutOfMemoryError
或重复的gc的Files.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)
与
free -m
/Windows Manager相比,这仍然可以为您提供更好的估计