【问题标题】:Is Files.getLastModifiedTime() leaking memory?Files.getLastModifiedTime() 是否泄漏内存?
【发布时间】:2015-02-07 12:45:13
【问题描述】:

我遇到了一个错误,我们的一个服务器应用程序几乎每秒使用越来越多的内存,我设法过滤掉了一个仍然显示该行为的简短示例:

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 中 Perform GC 时,所有使用的堆内存都会重置为零,并且 java.exe 进程的使用内存不会像预期的那样缩小,因为它被认为是保留的。

但是,GC完成后,内存使用量仍然每秒增加,而现在肯定有足够的空闲堆空间。

元空间也不受影响。

对我来说,这真的很难闻,看起来 JVM 存在内存泄漏。

谁能帮我解释一下这里发生了什么?

【问题讨论】:

  • 我认为没有抛出异常?
  • 这就是垃圾回收的工作原理。该程序不断分配内存,最终变成垃圾。当它无法从空闲池中分配更多内存时,它会收集垃圾。只有在 GC 之后堆没有减少(无论是由 JVM 自动触发还是手动触发)时才会出现问题。
  • @fge 不抛出异常
  • @kdgregory 如果堆中有足够的可用空间,我认为不需要分配额外的内存
  • 请将您的 lambda 与手动创建 File 对象进行比较,使用 File.lastModified()。

标签: java memory-leaks jvm java-8 java.nio.file


【解决方案1】:

我不认为它是泄漏,原因如下:

  1. 您提到当您触发gc 时,内存使用量会回落到默认值。这不是泄漏的工作方式。当发生泄漏时,这些对象是可访问的,即使在重复垃圾回收之后,堆大小也不会显着减小。
  2. 不断增长的堆并不意味着泄漏。它也可能真的意味着创建了太多的对象。这是正常的,完全没问题。在您的情况下,因为它在循环中被调用。是的
  3. 在我的机器上,java -Xmx20M TestGetLastModifiedTime 在我终止进程之前完美运行了 10 分钟。如果有泄漏,最终会抛出 OutOfMemoryError 或有太多重复的 gc
  4. 如果您在 visualvm 中观察,内存消耗在 2mb 和 2.8mb 之间跳跃。这几乎没有任何泄漏的嫌疑。此外,预计会有这么多内存消耗,因为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 为您提供更好的估计

【讨论】:

  • 我更多地考虑的是本机资源泄漏,JVM中的底层C代码在调用Files.getLastModifiedTime()时会泄漏内存,也许我的问题不够清楚。
  • 当使用JVisuaVM 进行监控时,我猜JMX 不断发送有关内存使用情况的更新的开销远大于每秒调用一次Files.getLastModifiedTime 的内存消耗......
  • @skiwi 我在 Ubuntu 上。我的看起来还可以。这可能是 Windows 上的问题。这样的事情在不同的平台上可以有不同的表现。所以你可能仍然想通过在 windows 的情况下应用类似的逻辑来确认自己
  • @Holger 在没有通过 JVisualVM 监控的情况下也会发生这种情况。
  • @skiwi:我知道没有JVisualVM 也存在问题。我想强调的是,JVisualVM 生成的图表在尝试分析这个特定问题时可能会导致错误的方向。 JMX 的副作用甚至可能在尝试分析问题时隐藏问题……
猜你喜欢
  • 2012-09-13
  • 2020-07-22
  • 2010-10-13
  • 2019-01-28
  • 2011-01-29
  • 2011-05-08
  • 1970-01-01
  • 1970-01-01
  • 2011-06-26
相关资源
最近更新 更多