前面看了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呢。


评论关闭
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!