如何确定Java堆是否使用压缩指针,以及它是否驻留在内存地址0处?

5
我正在尝试跟进这篇博客中的一些提示https://www.elastic.co/blog/a-heap-of-trouble#ref5,其中讨论了调整Java堆大小的好处,以便(a)可以使用压缩指针(对于小于32GB的堆)和(b)使堆驻留在内存地址0处。该文章详细介绍了压缩指针如何更有效地利用堆空间,并解释说当堆位于地址零时,这减少了解析指针地址所需的算术量。最后,文章提到如果我使用JVM选项-XX:+UnlockDiagnosticVMOptions-XX:+PrintCompressedOopsMode,我将看到类似以下内容的日志输出:

heap address: 0x000000011be00000, size: 27648 MB, zero based Compressed Oops

这表示启用了基于零的压缩 oops,或者像这样的输出

heap address: 0x0000000118400000, size: 28672 MB, Compressed Oops with base: 0x00000001183ff000

这表明堆从非零地址开始,因此需要上述增加的算术处理。

然而,当我尝试使用这些选项并在我的应用程序(Elastic Search)的日志目录中进行grep搜索时,我找不到这样的消息。如果有人能告诉我如何强制记录基于零或非零压缩指针的详细信息,我将非常感激。

解决方案:

很好的答案!我接受了@apangin的回答,并将他提供的Java程序包装在一个shell脚本中,只要您有Java,它就可以运行..也就是说,如果您正在查看Java堆,则应该有Java! 这是脚本:https://github.com/buildlackey/scripts/blob/master/verify_compressed_pointers_from_zero_offset.sh


请注意,在这两种情况下,堆都不是从零开始的。但在第一种情况下,起始地址和堆大小的总和低于32GiB阈值,这允许基于零的寻址。但是为什么这个问题被标记为“垃圾回收”? - Holger
请提供您的操作系统和 ES 版本信息,如果我的答案没有帮助到您。 - Ivan Mamontov
哇..太棒了,非常感谢您的回答。我会在明天仔细审查并尝试这些建议。针对@Holger的回复-标记垃圾收集的原因是因为我们正在尝试优化堆大小的设置,除此之外还有其他与垃圾收集相关的调整..认为这个话题对于任何调整GC的人都是相关的。/谢谢 - Chris Bedford
4个回答

5
假设您正在使用Linux和ES,版本为5.x或更高版本。
跨整个群集收集信息的最有效方法是使用 节点信息API

curl -XGET "http://localhost:9200/_nodes/jvm?filter_path=nodes.*.jvm.using_compressed_ordinary_object_pointers"

{"nodes":{"-jYDCxbpT2SBKc4dTfOYsA":{"jvm":{"using_compressed_ordinary_object_pointers":"true"}}}}

相同的信息也会记录在主日志中 - /var/log/elasticsearch/elasticsearch.log

[2017-10-06T23:03:15,223][INFO ][o.e.e.NodeEnvironment ] [-jYDCxb] heap size [1.9gb], compressed ordinary object pointers [true]

如果您对真实的JVM输出感兴趣,那么您需要知道,默认情况下JVM将其消息写入标准输出,在Linux发行版上,默认情况下会配置此输出以重定向到journalctl。所以你有两个选择。第一个是阅读journalctl:
sudo journalctl -u elasticsearch.service Narrow klass base: 0x0000000000000000, Narrow klass shift: 3 Compressed class space size: 1073741824 Address: 0x0000000100000000 Req Addr: 0x0000000100000000
但有时journalctl默认被禁用,您必须在守护程序配置/usr/lib/systemd/system/elasticsearch.service中删除ES命令行参数中的--quite参数来更改此设置。第二种方法是最简单和跨平台的-将JVM消息重定向到特定的GC日志输出:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -Xloggc:/tmp/vm.log
现在,您可以在/tmp/vm.log中查看所有与GC相关的输出。

3

HotSpot Serviceability Agent可以在不需要额外命令行标志的情况下,在正在运行的JVM进程上显示这个内容。

使用目标Java进程ID作为参数运行以下程序。

import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.tools.Tool;

public class CompressedOopsInfo extends Tool {

    @Override
    public void run() {
        VM vm = VM.getVM();
        System.out.println("CompressedOops = " + vm.isCompressedOopsEnabled());
        System.out.println("CompressedClassPointers = " + vm.isCompressedKlassPointersEnabled());
        System.out.println("OOP base = 0x" + Long.toHexString(vm.getDebugger().getNarrowOopBase()));
        System.out.println("OOP shift = " + vm.getDebugger().getNarrowOopShift());
    }

    public static void main(String[] args) {
        new CompressedOopsInfo().execute(args);
    }
}

在JDK 9之前,需要在类路径中包含${JDK_HOME}/lib/sa-jdi.jar。程序需要在与运行目标进程使用的JVM版本相同的JVM版本下运行。

1
然而,当我尝试这些选项并grep我的应用程序(Elastic Search)的日志目录时,我找不到这样的消息。 可能是因为它只将应用程序消息记录到该目录。您还需要将JVM输出记录到文件中,或者检查stdout或stderr以获取这些消息。

0
boolean compressedOopsEnabled = Boolean.parseBoolean(java.lang.management.ManagementFactory
    .getPlatformMXBean(com.sun.management.HotSpotDiagnosticMXBean.class)
    .getVMOption("UseCompressedOops").getValue());

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