监控Java原生内存

6
我们正在监控JVM指标,如堆、元空间、线程和垃圾回收计数,并能将这些指标推送到监控服务器,如prometheus。同样地,我们想要跟踪Java本机内存指标(jcmd VM.sumary的输出)。我的问题是,是否可以通过调用任何JVM运行时类来获取这些指标?

监控VM.summary并不是很有用,因为NMT经常会报告比实际使用更少的内存,同时也可能会报告比进程的真实RSS更多的内存。我建议监控RSS而不是VM.summary。 - apangin
在Linux上,监控RSS非常容易-只需解析/proc/[pid]/stat输出即可。为了从应用程序内部监视JVM本机内存使用情况,请解析/proc/self/stat - apangin
感谢@apangin提供的链接。我的整个想法是减少进程内存,但我想监视本机内存使用情况,以便我们了解大部分内存去了哪里。 - Uday Shankar
1
@UdayShankar,如果你对解析/proc/...文件感兴趣,那么你也可以看一下/proc/<pid>/maps。 但正如Shipilev所说(https://shipilev.net/jvm/anatomy-quarks/12-native-memory-tracking/):“使用JVM的NMT比尝试通过解析例如/proc/$pid/maps中的不透明内存映射来弄清楚JVM正在做什么要容易得多,多得多。” - Juraj Martinka
4个回答

10

是的,可以直接从Java应用程序获取NativeMemoryTracking摘要:

import javax.management.JMException;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;

public class DiagnosticCommand {

    public static String execute(String command, String... args) throws JMException {
        return (String) ManagementFactory.getPlatformMBeanServer().invoke(
                new ObjectName("com.sun.management:type=DiagnosticCommand"),
                command,
                new Object[]{args},
                new String[]{"[Ljava.lang.String;"});
    }

    public static void main(String[] args) throws Exception {
        String summary = DiagnosticCommand.execute("vmNativeMemory", "summary");
        System.out.println(summary);
    }
}

你需要解析文本输出。

请注意,NMT报告的最重要部分可以使用指定的MBeans单独跟踪,包括

  • Java堆
  • 代码缓存
  • 元空间
  • 压缩类空间
  • 直接字节缓冲区和映射字节缓冲区

请参见MemoryPoolMXBeanBufferPoolMXBean

正如我在评论中所说,监视NMT输出在实践中并不总是有用的,因为它不能直接反映进程实际使用的物理内存。 NMT可能会报告比实际使用量的内存,或者它也可能会报告从操作系统角度来看进程消耗的内存更多

由于NMT可能会忽略Java进程消耗的大量操作系统内存,因此监视进程的常驻集大小(RSS)也是很有用的。在Linux上,可以通过解析/proc/[pid]/stat/proc/[pid]/status来完成此操作。


1

我认为没有Java API可以做到这一点。 你最好的选择可能是调用jcmd <PID> VM.native_memory命令并解析其输出。 当然,你需要先为进程启用本地内存跟踪。


1
有一个Java API可以做到这一点。尽管它仍然需要解析文本输出。 - apangin

1
你可以在JMX中找到许多你想要的东西。 "为什么使用JMX技术?" 的一个答案是“监视和管理Java VM”。在Oracle doc中:

Java虚拟机(Java VM)使用JMX技术进行高度仪表化。您可以启动JMX代理以访问内置的Java VM仪器,从而远程监视和管理Java VM。

//Metaspace
for (MemoryPoolMXBean memoryMXBean : ManagementFactory.getMemoryPoolMXBeans()) {
    if ("Metaspace".equals(memoryMXBean.getName())) {
            System.out.println(memoryMXBean.getUsage().getUsed());
            System.out.println(memoryMXBean.getUsage().getCommitted());
            System.out.println(memoryMXBean.getUsage().getMax());
            System.out.println(memoryMXBean.getUsage().getInit());
    }
}

//current number of live threads including both daemon and non-daemon threads
int threadCount = ManagementFactory.getThreadMXBean().getThreadCount();

//Returns the current memory usage of the heap and non-heap
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapMemory = memoryMXBean.getHeapMemoryUsage();
MemoryUsage nonHeapMemory = memoryMXBean.getNonHeapMemoryUsage();

//GarbageCollector total number of collections
List<GarbageCollectorMXBean> garbageCollectorMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
long totalCollectionCount = garbageCollectorMXBeans.stream().mapToLong(x -> x.getCollectionCount()).sum();

1
谢谢您的回答,但我更感兴趣的是找出其他JVM内存部分(如Code、GC、Compiler、Internal、Symbol和Unknown)随着进程时间增加的确切情况。 - Uday Shankar

0

虽然您没有提到Micrometer,但我想指出一个小型支持库,我编写了一个获取procfs信息(如Resident Set Size(RSS))的方法。也许您可以从中提取一些有用的代码。实际上,procfs代码与Micrometer无关(尽管不被视为公共API)。这是它的链接:https://github.com/mweirauch/micrometer-jvm-extras


感谢@mweirauch提供的Github项目,我可以用它来获取进程的总体内存。我还想了解其他JVM内存部分(如Code、GC、Compiler、Internal、Symbol和Unknown)随着进程时间的增加是如何增长的。 - Uday Shankar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接