前面看了jdk的几个查看jvm信息的命令,然后只看不动手,不用回头就忘记的干干净净了。还是实际使用一下,然后顺便结合着jvm的内存分区,再深层次的记录一下几个分区的名称、大小、gc器、等等。

首先,查看一个进程,筛选一个进程,就可以使用jps -mvl ,m让他带上方法参数,v带上设置的jvm参数,l显示完整的运行main方法名称。

完了之后,就可以看到这个pid的好多信息了。但是这个看到的不全,还是有些虚拟机默认的参数是看不到的,这个时候已经拿到了pid,再使用jinfo -flags pid,就会显示这个pid的手动在脚本里面显示的设置的各种参数,再加上虚拟机自动设置的默认值的参数,看一个pid的jvm设置就更完全了。

然后,还打算看看这个pid的gc情况,就可以使用jstat -gc 1s 100 意思就是1s执行一次,执行100次,连续的看内存的gc才能发现问题。

下面是 jinfo -flags pid的输出

     * 命令:jinfo -flags 64363
     * 输出:
     * Attaching to process ID 64363, please wait...
     * Debugger attached successfully.
     * Server compiler detected.
     * JVM version is 25.131-b11
     *
     * Non-default VM flags: -XX:+AlwaysPreTouch -XX:CICompilerCount=4 -XX:CMSInitiatingOccupancyFraction=75
     * -XX:+HeapDumpOnOutOfMemoryError
     * -XX:InitialHeapSize=4294967296 -XX:MaxHeapSize=4294967296
     * -XX:MaxNewSize=697892864
     * -XX:MaxTenuringThreshold=6 -XX:MinHeapDeltaBytes=196608
     * -XX:NewSize=697892864
     * -XX:OldPLABSize=16
     * -XX:OldSize=3597074432
     * -XX:ThreadStackSize=1024
     * -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCompressedClassPointers
     * -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseFastUnorderedTimeStamps
     * -XX:+UseParNewGC
     *
     * Command line:  -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly
     * -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true
     * -Djdk.io.permissionsUseCanonicalPath=true -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true
     * -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true
     * -Dlog4j.skipJansi=true -XX:+HeapDumpOnOutOfMemoryError
     * -Xms4g
     * -Xmx4g
     * -Des.path.home=/home/lxk/elasticsearch
     *

从 command line里面可以看到,通过-Xms最小-Xmx最大都是4g,不让堆自动扩展,是不是以为这个heap的大小就是完整的4个g呢?我之前一直以为是这样的,直到下面的因为好奇而测试了一下,好像不是的。

下面是一个jstat -gc pid 的执行结果

     * 命令:jstat -gc 64363
     * 输出:
     *  S0C    S1C      S0U    S1U       EC       EU        OC         OU           MC     MU    CCSC   CCSU    YGC     YGCT    FGC    FGCT     GCT
     * 68096.0 68096.0  0.0   16160.3   545344.0  397868.8 3512768.0  2109826.5  71216.0 66507.1 9304.0 8046.9  22389  942.637  270    15.966  958.603
     *

这个jstat -gc其实已经把堆区的信息表达的差不多了,要是觉得这个还不够的话。

还有个jdk提供的命令,jmap -heap pid。他打印的也差不多是这个吧。

他可就不能批次执行,然后很直观的看每个分区的内存变化了。

要是打算看看内存活着live的对象具体是啥,就需要使用

jmap -histo:live pid,这个也比较重要。不过我这次是好奇大小问题。

然后,我就好奇,s区+e区+o区最终会不会等于堆heap的大小。

然后就有了下面的测试代码

package com.lxk.jdk.jvm.gc;

import java.text.DecimalFormat;

/**
 * 在 jdk1.8 前提下测试堆里面的各区间的大小
 * 参考 jps和jstat2个jdk提供的内存监控工具。
 *
 * @author LiXuekai on 2020/6/5
 */
public class HeapSizeTest {


    public static void main(String[] args) {

        // jinfo 出来的jvm参数,这三个的单位是 byte 字节
        long maxHeapSize = 4294967296L,
                newSize = 697892864L,
                oldSize = 3597074432L;

        System.out.println("InitialHeapSize = MaxHeapSize = " + showNumberBetter(maxHeapSize));

        long all = newSize + oldSize;
        System.out.println("newSize + oldSize = " + showNumberBetter(all));

        System.out.println("newSize + oldSize == maxHeapSize is true.");
        System.out.println("NewSize is " + showNumberBetter(newSize));
        System.out.println("OldSize is " + showNumberBetter(oldSize));
        // jstat 出来的jvm参数 下面的这些个的但是 k bytes KB
        long s0 = 68096L,
                s1 = 68096L,
                eden = 545344L,
                old = 3512768L,
                mc = 71216,
                ccs = 9304;
        long young = s0 + s1 + eden;
        System.out.println("s0 + s1 + eden = " + showNumberBetter(young));

        long heap = young + old;
        System.out.println("young + old = " + showNumberBetter(heap));

        long total = heap + mc + ccs;
        System.out.println("heap + mc + ccs = " + showNumberBetter(total));


    }

    private static String showNumberBetter(long number) {
        DecimalFormat df = new DecimalFormat("#,###");
        return df.format(number);
    }
}

代码运行结果:

初始化的堆的大小是4.29个g,new区和old区的大小加起来和这个初始化堆的大小是一致的。

经常说新生代被分成2个s区和一个eden区,但是从上面的代码计算来看 s0 + s1 +Eden != newSize,稍微小了一丢丢。

jstat -gc 看到的old 的capacity的大小也比jinfo -flags看到的oldsize小一丢丢。

那最后的young + old =heap 也就少了

后面的heap + m + ccs 这个值就有点四不像了。

怎么这么加呢!

jdk1.8之后,metasapce区变成了本地内存,在堆之外,跟堆没关系。所以强行把他们加在一起,没道理。

ccs,是类压缩空间。说是针对64bit的系统优化的。

我就是看gc出来那么几个区,想都加一起,看看能否等于上面的初始化堆的大小。

当然是不可能的了,因为写代码的时候,我还没去了解啥是metaspace,啥是ccsc和ccscu呢。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐