精华内容
下载资源
问答
  • 怎么查看虚拟机使用哪种垃圾收集器?是G1,还是CMS? 启动java参数中没有配置-XX:+UseG1GC这种参数 $ ./java -version java version "1.7.0_45" Java(TM) SE Runtime Environment (build 1.7.0_45-b18) Java ...
  • 既然学习JVM,阅读GC日志是处理Java虚拟机内存问题基础技能,它只是一些人为确定规则,没有太多技术含量。 既然如此,那么在IDE控制台打印GC日志是必不可少了。现在就告诉你怎么打印。 (1)如果你用是...

    本文主要内容:

    • Trace跟踪参数
    • 堆的分配参数
    • 栈的分配参数

     

    零、在IDE的后台打印GC日志

    既然学习JVM,阅读GC日志是处理Java虚拟机内存问题的基础技能,它只是一些人为确定的规则,没有太多技术含量。

    既然如此,那么在IDE的控制台打印GC日志是必不可少的了。现在就告诉你怎么打印。

    (1)如果你用的是Eclipse,打印GC日志的操作如下:

    9b8c6877023e3e1f34c7fb36b5050c62.png

    c1031e1d00bb322787540924ff43743a.png

    在上图的箭头处加上-XX:+PrintGCDetails这句话。于是,运行程序后,GC日志就可以打印出来了:

    26c869a6f7a7209e482a57fa8ce833d5.png

    (2)如果你用的是IntelliJ IDEA,打印GC日志的操作如下:

    3fd7f45a1ad0db7c09ab1e152943c608.png

    44d6e7eba4e44d959bfff053d891058c.png

    在上图的箭头处加上-XX:+PrintGCDetails这句话。于是,运行程序后,GC日志就可以打印出来了:

    7f6d550aac3fd387dcd3105d5ba2b629.png

    当然了,光有-XX:+PrintGCDetails这一句参数肯定是不够的,下面我们详细介绍一下更多的参数配置。

     

    一、Trace跟踪参数:

    1、打印GC的简要信息:

    -verbose:gc
    -XX:+printGC

    解释:可以打印GC的简要信息。比如:

    [GC 4790K->374K(15872K), 0.0001606 secs]

    [GC 4790K->374K(15872K), 0.0001474 secs]

    [GC 4790K->374K(15872K), 0.0001563 secs]

    [GC 4790K->374K(15872K), 0.0001682 secs]

    上方日志的意思是说,GC之前,用了4M左右的内存,GC之后,用了374K内存,一共回收了将近4M。内存大小一共是16M左右。

     

    2、打印GC的详细信息:

    -XX:+PrintGCDetails

    解释:打印GC详细信息。

    -XX:+PrintGCTimeStamps

    解释:打印CG发生的时间戳。

     

    理解GC日志的含义:

    例如下面这段日志:

    [GC[DefNew: 4416K->0K(4928K), 0.0001897 secs] 4790K->374K(15872K), 0.0002232 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

    上方日志的意思是说:这是一个新生代的GC。方括号内部的“4416K->0K(4928K)”含义是:“GC前该内存区域已使用容量->GC后该内存区域已使用容量(该内存区域总容量)”。而在方括号之外的“4790K->374K(15872K)”表示“GC前Java堆已使用容量->GC后Java堆已使用容量(Java堆总容量)”。

    再往后看,“0.0001897 secs”表示该内存区域GC所占用的时间,单位是秒。

     

    再比如下面这段GC日志:

    f650cee76447a3e55b904294a7afaf0a.png

    上图中,我们先看一下用红框标注的“[0x27e80000, 0x28d80000, 0x28d80000)”的含义,它表示新生代在内存当中的位置:第一个参数是申请到的起始位置,第二个参数是申请到的终点位置,第三个参数表示最多能申请到的位置。上图中的例子表示新生代申请到了15M的控件,而这个15M是等于:(eden space的12288K)+(from space的1536K)+(to space的1536K)

    疑问:分配到的新生代有15M,但是可用的只有13824K,为什么会有这个差异呢?等我们在后面的文章中学习到了GC算法之后就明白了。

     

    3、指定GC log的位置:

    -Xloggc:log/gc.log

    解释:指定GC log的位置,以文件输出。帮助开发人员分析问题。

    3ba124f71bf636de9db58ba666109177.png

     

    -XX:+PrintHeapAtGC

    解释:每一次GC前和GC后,都打印堆信息。

    例如:

    ccc7bf07f9af9f50777f1e7137431342.png

    上图中,红框部分正好是一次GC,红框部分的前面是GC之前的日志,红框部分的后面是GC之后的日志。

     

    -XX:+TraceClassLoading

    解释:监控类的加载。

    例如:

    [Loaded java.lang.Object from shared objects file]

    [Loaded java.io.Serializable from shared objects file]

    [Loaded java.lang.Comparable from shared objects file]

    [Loaded java.lang.CharSequence from shared objects file]

    [Loaded java.lang.String from shared objects file]

    [Loaded java.lang.reflect.GenericDeclaration from shared objects file]

    [Loaded java.lang.reflect.Type from shared objects file]

     

    -XX:+PrintClassHistogram

     

    解释:按下Ctrl+Break后,打印类的信息。

    例如:

    b776f279dc38ae205801d26e66867205.png

     

    二、堆的分配参数:

    1、-Xmx –Xms:指定最大堆和最小堆

    举例、当参数设置为如下时:

    -Xmx20m -Xms5m

    然后我们在程序中运行如下代码:

    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");     //系统的最大空间
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");   //系统的空闲空间
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");   //当前可用的总空间

     运行效果:

    82d8f53f074ed4463e76b9ee6f2f0b6b.png

    保持参数不变,在程序中运行如下代码:(分配1M空间给数组)

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第12张

    byte[] b = new byte[1 * 1024 * 1024];
    System.out.println("分配了1M空间给数组");
    
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");   //系统的最大空间
    
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");   //系统的空闲空间
    
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");   

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第12张

    运行效果:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第13张

    注:Java会尽可能将total mem的值维持在最小堆。

    保持参数不变,在程序中运行如下代码:(分配10M空间给数组)

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第12张

    byte[] b = new byte[10 * 1024 * 1024];
    System.out.println("分配了10M空间给数组");
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");   //系统的最大空间
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");   //系统的空闲空间
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");   //当前可用的总空间

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第12张

    运行效果:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第16张

    如上图红框所示:此时,total mem 为7M时已经不能满足需求了,于是total mem涨成了16.5M。

     

    保持参数不变,在程序中运行如下代码:(进行一次GC的回收)

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第12张

    System.gc();
    
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");    //系统的最大空间
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");   //系统的空闲空间
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");   //当前可用的总空间 

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第12张

    运行效果:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第19张

    问题1: -Xmx(最大堆空间)和 –Xms(最小堆空间)应该保持一个什么关系,可以让系统的性能尽可能的好呢?

    问题2:如果你要做一个Java的桌面产品,需要绑定JRE,但是JRE又很大,你如何做一下JRE的瘦身呢?

     

    2、-Xmn、-XX:NewRatio、-XX:SurvivorRatio:

    • -Xmn

        设置新生代大小

    • -XX:NewRatio

        新生代(eden+2*s)和老年代(不包含永久区)的比值

            例如:4,表示新生代:老年代=1:4,即新生代占整个堆的1/5

    • -XX:SurvivorRatio(幸存代)

        设置两个Survivor区和eden的比值

            例如:8,表示两个Survivor:eden=2:8,即一个Survivor占年轻代的1/10

     

    现在运行如下这段代码:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第12张

    public class JavaTest {
        public static void main(String[] args) {
            byte[] b = null;
            for (int i = 0; i < 10; i++)
                b = new byte[1 * 1024 * 1024];
        }
    }

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第12张

    我们通过设置不同的jvm参数,来看一下GC日志的区别。

     

    (1)当参数设置为如下时:(设置新生代为1M,很小)

    -Xmx20m -Xms20m -Xmn1m -XX:+PrintGCDetails 

    运行效果:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第22张

    总结:

      没有触发GC

        由于新生代的内存比较小,所以全部分配在老年代。

     

    (2)当参数设置为如下时:(设置新生代为15M,足够大)

    -Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails

    运行效果:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第22张

    上图显示:

    没有触发GC

    全部分配在eden(蓝框所示)

    老年代没有使用(红框所示)

     

    (3)当参数设置为如下时:(设置新生代为7M,不大不小)

    -Xmx20m -Xms20m –Xmn7m -XX:+PrintGCDetails

    运行效果:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第22张

    总结:

      进行了2次新生代GC

      s0 s1 太小,需要老年代担保

     

    (4)当参数设置为如下时:(设置新生代为7M,不大不小;同时,增加幸存代大小)

    -Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails

    运行效果:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第22张

    总结:

        进行了至少3次新生代GC

        s0 s1 增大

     

    (5)当参数设置为如下时:

    -Xmx20m -Xms20m -XX:NewRatio=1
    -XX:SurvivorRatio=2 -XX:+PrintGCDetails 

    运行效果:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第22张

     

     

    (6)当参数设置为如下时: 和上面的(5)相比,适当减小幸存代大小,这样的话,能够减少GC的次数

    -Xmx20m -Xms20m -XX:NewRatio=1
    -XX:SurvivorRatio=3 -XX:+PrintGCDetails

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第22张

     

     

    3、-XX:+HeapDumpOnOutOfMemoryError、-XX:+HeapDumpPath

    • -XX:+HeapDumpOnOutOfMemoryError

        OOM时导出堆到文件

          根据这个文件,我们可以看到系统dump时发生了什么。

    • -XX:+HeapDumpPath

        导出OOM的路径

    例如我们设置如下的参数:

    -Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/a.dump

    上方意思是说,现在给堆内存最多分配20M的空间。如果发生了OOM异常,那就把dump信息导出到d:/a.dump文件中。

    然后,我们执行如下代码:

    Vector v = new Vector();
    for (int i = 0; i < 25; i++)
      v.add(new byte[1 * 1024 * 1024]);

    上方代码中,需要利用25M的空间,很显然会发生OOM异常。现在我们运行程序,控制台打印如下:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第22张

    现在我们去D盘看一下dump文件:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第22张

    上图显示,一般来说,这个文件的大小和最大堆的大小保持一致。

    我们可以用VisualVM打开这个dump文件。

    注:关于VisualVM的使用,可以参考下面这篇博客:

    使用 VisualVM 进行性能分析及调优:http://www.ibm.com/developerworks/cn/java/j-lo-visualvm/

    或者使用Java自带的Java VisualVM工具也行:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第22张

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第22张

    上图中就是dump出来的文件,文件中可以看到,一共有19个byte已经被分配了。 

     

    4、-XX:OnOutOfMemoryError:

    • -XX:OnOutOfMemoryError

        在OOM时,执行一个脚本。

          可以在OOM时,发送邮件,甚至是重启程序。

    例如我们设置如下的参数:

    -XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p //p代表的是当前进程的pid 

    上方参数的意思是说,执行printstack.bat脚本,而这个脚本做的事情是:D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt,即当程序OOM时,在D:/a.txt中将会生成线程的dump。

    5、堆的分配参数总结:

    • 根据实际事情调整新生代和幸存代的大小
    • 官方推荐新生代占堆的3/8
    • 幸存代占新生代的1/10
    • 在OOM时,记得Dump出堆,确保可以排查现场问题

     

    6、永久区分配参数:

    • -XX:PermSize  -XX:MaxPermSize

        设置永久区的初始空间和最大空间。也就是说,jvm启动时,永久区一开始就占用了PermSize大小的空间,如果空间还不够,可以继续扩展,但是不能超过MaxPermSize,否则会OOM。

        他们表示,一个系统可以容纳多少个类型

    代码举例:

    我们知道,使用CGLIB等库的时候,可能会产生大量的类,这些类,有可能撑爆永久区导致OOM。于是,我们运行下面这段代码:

    for(int i=0;i<100000;i++){
      CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap());
    }

    上面这段代码会在永久区不断地产生新的类。于是,运行效果如下:

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第22张

    总结:

      如果堆空间没有用完也抛出了OOM,有可能是永久区导致的

        堆空间实际占用非常少,但是永久区溢出 一样抛出OOM。

     

    三、栈的分配参数:

    1、Xss:

    设置栈空间的大小。通常只有几百K

      决定了函数调用的深度

      每个线程都有独立的栈空间

      局部变量、参数 分配在栈上

    注:栈空间是每个线程私有的区域。栈里面的主要内容是栈帧,而栈帧存放的是局部变量表,局部变量表的内容是:局部变量、参数。

    我们来看下面这段代码:(没有出口的递归调用)

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第12张

    public class TestStackDeep {
        private static int count = 0;
        public static void recursion(long a, long b, long c) {
            long e = 1, f = 2, g = 3, h = 4, i = 5, k = 6, q = 7, x = 8, y = 9, z = 10;
            count++;
            recursion(a, b, c);
        }
        public static void main(String args[]) {
            try {
                recursion(0L, 0L, 0L);
            } catch (Throwable e) {
                System.out.println("deep of calling = " + count);
                e.printStackTrace();
            }
        }
    }

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第12张

    上方这段代码是没有出口的递归调用,肯定会出现OOM的。

    如果设置栈大小为128k:

    -Xss128K 

    运行效果如下:(方法被调用了294次)

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第35张

    如果设置栈大小为256k:(方法被调用748次)

    Java虚拟机详解03—-常用JVM配置参数 idc资讯 第35张

    意味着函数调用的次数太深,像这种递归调用就是个典型的例子。

     

    总结:

    我们在本文中介绍了jvm的一些最基本的参数,还有很多参数(如GC参数等)将在后续的系列文章中进行介绍。我们将在接下来的文章中介绍GC算法。

    原文取自生命一号博客,MARK记录。原文地址:ava虚拟机详解03—-常用JVM配置参数

     

    转载请注明:云速博客www.ysidc.top» Java虚拟机详解03—-常用JVM配置参数

    https://www.ysidc.top 云速博客,数据库,云速,虚拟主机,域名注册,域名,云服务器,云主机,云建站,ysidc.top

    展开全文
  • 既然学习JVM,阅读GC日志是处理Java虚拟机内存问题基础技能,它只是一些人为确定规则,没有太多技术含量。 既然如此,那么在IDE控制台打印GC日志是必不可少了。现在就告诉你怎么打印。 (1)如果你用是...

    【声明】 

    欢迎转载,但请保留文章原始出处→_→ 

     

    本文主要内容:

    • Trace跟踪参数
    • 堆的分配参数
    • 栈的分配参数

     

    零、在IDE的后台打印GC日志:

    既然学习JVM,阅读GC日志是处理Java虚拟机内存问题的基础技能,它只是一些人为确定的规则,没有太多技术含量。

    既然如此,那么在IDE的控制台打印GC日志是必不可少的了。现在就告诉你怎么打印。

    (1)如果你用的是Eclipse,打印GC日志的操作如下:

    d32742cf-b002-4c55-a185-d4ccdc90a69c

    bc5b8afb-9d1f-438b-9225-ee7fbbbe2454

    在上图的箭头处加上-XX:+PrintGCDetails这句话。于是,运行程序后,GC日志就可以打印出来了:

    25d80649-69f0-47b2-a3bb-418ba4457849

    (2)如果你用的是IntelliJ IDEA,打印GC日志的操作如下:

    94726055-e81f-45b8-8978-d1277c5acb17

    f2c896da-404c-4415-98ef-5b582dec3528

    在上图的箭头处加上-XX:+PrintGCDetails这句话。于是,运行程序后,GC日志就可以打印出来了:

    6b1b4352-7172-4404-ac6c-b94c16036d73

    当然了,光有-XX:+PrintGCDetails这一句参数肯定是不够的,下面我们详细介绍一下更多的参数配置。

     

    一、Trace跟踪参数:

    1、打印GC的简要信息:

    -verbose:gc
    -XX:+printGC

    解释:可以打印GC的简要信息。比如:

    [GC 4790K->374K(15872K), 0.0001606 secs]

    [GC 4790K->374K(15872K), 0.0001474 secs]

    [GC 4790K->374K(15872K), 0.0001563 secs]

    [GC 4790K->374K(15872K), 0.0001682 secs]

    上方日志的意思是说,GC之前,用了4M左右的内存,GC之后,用了374K内存,一共回收了将近4M。内存大小一共是16M左右。

     

    2、打印GC的详细信息:

    -XX:+PrintGCDetails
    

    解释:打印GC详细信息。

    -XX:+PrintGCTimeStamps
    

    解释:打印CG发生的时间戳。

     

    理解GC日志的含义:

    例如下面这段日志:

    [GC[DefNew: 4416K->0K(4928K), 0.0001897 secs] 4790K->374K(15872K), 0.0002232 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

    上方日志的意思是说:这是一个新生代的GC。方括号内部的“4416K->0K(4928K)”含义是:“GC前该内存区域已使用容量->GC后该内存区域已使用容量(该内存区域总容量)”。而在方括号之外的“4790K->374K(15872K)”表示“GC前Java堆已使用容量->GC后Java堆已使用容量(Java堆总容量)”。

    再往后看,“0.0001897 secs”表示该内存区域GC所占用的时间,单位是秒。

     

    再比如下面这段GC日志:

    1fe41f36-cc6b-4a8b-b48e-8cbe2e3a04af

    上图中,我们先看一下用红框标注的“[0x27e80000, 0x28d80000, 0x28d80000)”的含义,它表示新生代在内存当中的位置:第一个参数是申请到的起始位置,第二个参数是申请到的终点位置,第三个参数表示最多能申请到的位置。上图中的例子表示新生代申请到了15M的控件,而这个15M是等于:(eden space的12288K)+(from space的1536K)+(to space的1536K)

    疑问:分配到的新生代有15M,但是可用的只有13824K,为什么会有这个差异呢?等我们在后面的文章中学习到了GC算法之后就明白了。

     

    3、指定GC log的位置:

    -Xloggc:log/gc.log
    

    解释:指定GC log的位置,以文件输出。帮助开发人员分析问题。

    805e8e33-1e3b-46c0-af9d-d68f4d38816f

      

    -XX:+PrintHeapAtGC
    

    解释:每一次GC前和GC后,都打印堆信息。

    例如:

    1c6f3837-4b31-4ac2-a639-e79c92f80df5

    上图中,红框部分正好是一次GC,红框部分的前面是GC之前的日志,红框部分的后面是GC之后的日志。

     

    -XX:+TraceClassLoading
    

    解释:监控类的加载。

    例如:

    [Loaded java.lang.Object from shared objects file]

    [Loaded java.io.Serializable from shared objects file]

    [Loaded java.lang.Comparable from shared objects file]

    [Loaded java.lang.CharSequence from shared objects file]

    [Loaded java.lang.String from shared objects file]

    [Loaded java.lang.reflect.GenericDeclaration from shared objects file]

    [Loaded java.lang.reflect.Type from shared objects file]

     

    -XX:+PrintClassHistogram

     

    解释:按下Ctrl+Break后,打印类的信息。

    例如:

    c8050739-0029-47cd-95bd-fbbd6289a5d1

     

    二、堆的分配参数:

    1、-Xmx –Xms:指定最大堆和最小堆

    举例、当参数设置为如下时:

    -Xmx20m -Xms5m
    

    然后我们在程序中运行如下代码:

    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");     //系统的最大空间
    
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");   //系统的空闲空间
    
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");   //当前可用的总空间

     运行效果:

    79c1029d-58fe-47d9-aa2e-1c5ee7e741cd

    保持参数不变,在程序中运行如下代码:(分配1M空间给数组)

    复制代码

    byte[] b = new byte[1 * 1024 * 1024];
    System.out.println("分配了1M空间给数组");
    
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");   //系统的最大空间
    
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");   //系统的空闲空间
    
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");   

    复制代码

    运行效果:

    14d260c9-28bf-4544-a36f-ee14a1d59623

    注:Java会尽可能将total mem的值维持在最小堆。

    保持参数不变,在程序中运行如下代码:(分配10M空间给数组)

    复制代码

    byte[] b = new byte[10 * 1024 * 1024];
    System.out.println("分配了10M空间给数组");
    
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");   //系统的最大空间
    
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");   //系统的空闲空间
    
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");   //当前可用的总空间

    复制代码

    运行效果:

    284e8036-8d70-46bc-aac1-99c9b3deb3ef

    如上图红框所示:此时,total mem 为7M时已经不能满足需求了,于是total mem涨成了16.5M。

     

    保持参数不变,在程序中运行如下代码:(进行一次GC的回收)

    复制代码

    System.gc();
    
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");    //系统的最大空间
    
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");   //系统的空闲空间
    
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");   //当前可用的总空间 

    复制代码

    运行效果:

    e419c020-0da3-4046-9b7f-f542ee14a780

    问题1: -Xmx(最大堆空间)和 –Xms(最小堆空间)应该保持一个什么关系,可以让系统的性能尽可能的好呢?

    问题2:如果你要做一个Java的桌面产品,需要绑定JRE,但是JRE又很大,你如何做一下JRE的瘦身呢?

     

    2、-Xmn、-XX:NewRatio、-XX:SurvivorRatio:

    • -Xmn

        设置新生代大小

    • -XX:NewRatio

        新生代(eden+2*s)和老年代(不包含永久区)的比值

            例如:4,表示新生代:老年代=1:4,即新生代占整个堆的1/5

    • -XX:SurvivorRatio(幸存代)

        设置两个Survivor区和eden的比值

            例如:8,表示两个Survivor:eden=2:8,即一个Survivor占年轻代的1/10

     

    现在运行如下这段代码:

    复制代码

    public class JavaTest {
        public static void main(String[] args) {
            byte[] b = null;
            for (int i = 0; i < 10; i++)
                b = new byte[1 * 1024 * 1024];
        }
    }

    复制代码

    我们通过设置不同的jvm参数,来看一下GC日志的区别。

     

    (1)当参数设置为如下时:(设置新生代为1M,很小)

    -Xmx20m -Xms20m -Xmn1m -XX:+PrintGCDetails 

    运行效果:

    4f0b24b4-cc74-4fd6-af15-b30a784d351b

    总结:

      没有触发GC

        由于新生代的内存比较小,所以全部分配在老年代。

     

    (2)当参数设置为如下时:(设置新生代为15M,足够大)

    -Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails
    

    运行效果:

    2cb6145f-8c1b-4269-bcfa-31912d2f0d41

    上图显示:

    没有触发GC

    全部分配在eden(蓝框所示)

    老年代没有使用(红框所示)

     

    (3)当参数设置为如下时:(设置新生代为7M,不大不小)

    -Xmx20m -Xms20m –Xmn7m -XX:+PrintGCDetails
    

    运行效果:

    0e0cc65d-e291-477a-ba7f-7d433f1085cc

    总结:

      进行了2次新生代GC

      s0 s1 太小,需要老年代担保

     

    (4)当参数设置为如下时:(设置新生代为7M,不大不小;同时,增加幸存代大小)

    -Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails
    

    运行效果:

    35eb96d6-9251-45e5-8120-05b82210df06

    总结:

        进行了至少3次新生代GC

        s0 s1 增大

     

    (5)当参数设置为如下时:

    -Xmx20m -Xms20m -XX:NewRatio=1
    
    -XX:SurvivorRatio=2 -XX:+PrintGCDetails 

    运行效果:

    c85f7057-1842-4d11-bc28-fc766e5681f8

     

     

    (6)当参数设置为如下时: 和上面的(5)相比,适当减小幸存代大小,这样的话,能够减少GC的次数

    -Xmx20m -Xms20m -XX:NewRatio=1
    
    -XX:SurvivorRatio=3 -XX:+PrintGCDetails
    

    fd3322ec-a853-49aa-86fa-81d8b3a02f8c

     

     

    3、-XX:+HeapDumpOnOutOfMemoryError、-XX:+HeapDumpPath

    • -XX:+HeapDumpOnOutOfMemoryError

        OOM时导出堆到文件

          根据这个文件,我们可以看到系统dump时发生了什么。

    • -XX:+HeapDumpPath

        导出OOM的路径

    例如我们设置如下的参数:

    -Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/a.dump
    

    上方意思是说,现在给堆内存最多分配20M的空间。如果发生了OOM异常,那就把dump信息导出到d:/a.dump文件中。

    然后,我们执行如下代码:

    Vector v = new Vector();
    for (int i = 0; i < 25; i++)
      v.add(new byte[1 * 1024 * 1024]);
    

    上方代码中,需要利用25M的空间,很显然会发生OOM异常。现在我们运行程序,控制台打印如下:

    3320aba5-2aa6-42bc-b656-57bbc5d8ec41

    现在我们去D盘看一下dump文件:

    8782a0ae-62fb-43a8-a5a6-1c5691e7fa59

    上图显示,一般来说,这个文件的大小和最大堆的大小保持一致。

    我们可以用VisualVM打开这个dump文件。

    注:关于VisualVM的使用,可以参考下面这篇博客:

    使用 VisualVM 进行性能分析及调优:http://www.ibm.com/developerworks/cn/java/j-lo-visualvm/

    或者使用Java自带的Java VisualVM工具也行:

    f9158d50-95d0-4732-942c-e872181fa530

    f69bd0d2-a355-4a93-81c1-c3e71bce7509

    上图中就是dump出来的文件,文件中可以看到,一共有19个byte已经被分配了。 

     

    4、-XX:OnOutOfMemoryError:

    • -XX:OnOutOfMemoryError

        在OOM时,执行一个脚本。

          可以在OOM时,发送邮件,甚至是重启程序。

    例如我们设置如下的参数:

    -XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p //p代表的是当前进程的pid 

    上方参数的意思是说,执行printstack.bat脚本,而这个脚本做的事情是:D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt,即当程序OOM时,在D:/a.txt中将会生成线程的dump。

    5、堆的分配参数总结:

    • 根据实际事情调整新生代和幸存代的大小
    • 官方推荐新生代占堆的3/8
    • 幸存代占新生代的1/10
    • 在OOM时,记得Dump出堆,确保可以排查现场问题

     

    6、永久区分配参数:

    • -XX:PermSize  -XX:MaxPermSize

        设置永久区的初始空间和最大空间。也就是说,jvm启动时,永久区一开始就占用了PermSize大小的空间,如果空间还不够,可以继续扩展,但是不能超过MaxPermSize,否则会OOM。

        他们表示,一个系统可以容纳多少个类型

    代码举例:

    我们知道,使用CGLIB等库的时候,可能会产生大量的类,这些类,有可能撑爆永久区导致OOM。于是,我们运行下面这段代码:

    for(int i=0;i<100000;i++){
      CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap());
    }

    上面这段代码会在永久区不断地产生新的类。于是,运行效果如下:

    fd7bcefb-d6d5-4fe0-8d77-9cddae2733fc

    总结:

      如果堆空间没有用完也抛出了OOM,有可能是永久区导致的

        堆空间实际占用非常少,但是永久区溢出 一样抛出OOM。

     

    三、栈的分配参数:

    1、Xss:

    设置栈空间的大小。通常只有几百K

      决定了函数调用的深度

      每个线程都有独立的栈空间

      局部变量、参数 分配在栈上

    注:栈空间是每个线程私有的区域。栈里面的主要内容是栈帧,而栈帧存放的是局部变量表,局部变量表的内容是:局部变量、参数。

    我们来看下面这段代码:(没有出口的递归调用)

    复制代码

    public class TestStackDeep {
        private static int count = 0;
    
        public static void recursion(long a, long b, long c) {
            long e = 1, f = 2, g = 3, h = 4, i = 5, k = 6, q = 7, x = 8, y = 9, z = 10;
            count++;
            recursion(a, b, c);
        }
    
        public static void main(String args[]) {
            try {
                recursion(0L, 0L, 0L);
            } catch (Throwable e) {
                System.out.println("deep of calling = " + count);
                e.printStackTrace();
            }
        }
    }

    复制代码

    上方这段代码是没有出口的递归调用,肯定会出现OOM的。

    如果设置栈大小为128k:

    -Xss128K 

    运行效果如下:(方法被调用了294次)

    5c2b2060-e54a-4e7c-9a30-81567204d55b

    如果设置栈大小为256k:(方法被调用748次)

    7d6be7d6-b646-42bf-9357-1a3bccbb7a49

    意味着函数调用的次数太深,像这种递归调用就是个典型的例子。

     

    总结:

    我们在本文中介绍了jvm的一些最基本的参数,还有很多参数(如GC参数等)将在后续的系列文章中进行介绍。我们将在接下来的文章中介绍GC算法。

     

    展开全文
  • 既然学习JVM,阅读GC日志是处理Java虚拟机内存问题基础技能,它只是一些人为确定规则,没有太多技术含量。 既然如此,那么在IDE控制台打印GC日志是必不可少了。现在就告诉你怎么打印。 (1)如果你用是...

    本文主要内容:

    • Trace跟踪参数
    • 堆的分配参数
    • 栈的分配参数

     

    零、在IDE的后台打印GC日志:

    既然学习JVM,阅读GC日志是处理Java虚拟机内存问题的基础技能,它只是一些人为确定的规则,没有太多技术含量。

    既然如此,那么在IDE的控制台打印GC日志是必不可少的了。现在就告诉你怎么打印。

    (1)如果你用的是Eclipse,打印GC日志的操作如下:

    在上图的箭头处加上-XX:+PrintGCDetails这句话。于是,运行程序后,GC日志就可以打印出来了:

    (2)如果你用的是IntelliJ IDEA,打印GC日志的操作如下:

    在上图的箭头处加上-XX:+PrintGCDetails这句话。于是,运行程序后,GC日志就可以打印出来了:

    当然了,光有-XX:+PrintGCDetails这一句参数肯定是不够的,下面我们详细介绍一下更多的参数配置。

     

    一、Trace跟踪参数:

    1、打印GC的简要信息:

    -verbose:gc
    -XX:+printGC

    解释:可以打印GC的简要信息。比如:

    [GC 4790K->374K(15872K), 0.0001606 secs]

    [GC 4790K->374K(15872K), 0.0001474 secs]

    [GC 4790K->374K(15872K), 0.0001563 secs]

    [GC 4790K->374K(15872K), 0.0001682 secs]

    上方日志的意思是说,GC之前,用了4M左右的内存,GC之后,用了374K内存,一共回收了将近4M。内存大小一共是16M左右。

     

    2、打印GC的详细信息:

    -XX:+PrintGCDetails

    解释:打印GC详细信息。

    -XX:+PrintGCTimeStamps

    解释:打印CG发生的时间戳。

     

    理解GC日志的含义:

    例如下面这段日志:

    [GC[DefNew: 4416K->0K(4928K), 0.0001897 secs] 4790K->374K(15872K), 0.0002232 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

    上方日志的意思是说:这是一个新生代的GC。方括号内部的“4416K->0K(4928K)”含义是:“GC前该内存区域已使用容量->GC后该内存区域已使用容量(该内存区域总容量)”。而在方括号之外的“4790K->374K(15872K)”表示“GC前Java堆已使用容量->GC后Java堆已使用容量(Java堆总容量)”。

    再往后看,“0.0001897 secs”表示该内存区域GC所占用的时间,单位是秒。

     

    再比如下面这段GC日志:

    上图中,我们先看一下用红框标注的“[0x27e80000, 0x28d80000, 0x28d80000)”的含义,它表示新生代在内存当中的位置:第一个参数是申请到的起始位置,第二个参数是申请到的终点位置,第三个参数表示最多能申请到的位置。上图中的例子表示新生代申请到了15M的控件,而这个15M是等于:(eden space的12288K)+(from space的1536K)+(to space的1536K)。

    疑问:分配到的新生代有15M,但是可用的只有13824K,为什么会有这个差异呢?等我们在后面的文章中学习到了GC算法之后就明白了。

     

    3、指定GC log的位置:

    -Xloggc:log/gc.log

    解释:指定GC log的位置,以文件输出。帮助开发人员分析问题。

      

    -XX:+PrintHeapAtGC

    解释:每一次GC前和GC后,都打印堆信息。

    例如:

     

    上图中,红框部分正好是一次GC,红框部分的前面是GC之前的日志,红框部分的后面是GC之后的日志。

     

    -XX:+TraceClassLoading

    解释:监控类的加载。

    例如:

    [Loaded java.lang.Object from shared objects file]

    [Loaded java.io.Serializable from shared objects file]

    [Loaded java.lang.Comparable from shared objects file]

    [Loaded java.lang.CharSequence from shared objects file]

    [Loaded java.lang.String from shared objects file]

    [Loaded java.lang.reflect.GenericDeclaration from shared objects file]

    [Loaded java.lang.reflect.Type from shared objects file]

     

    -XX:+PrintClassHistogram

     

    解释:按下Ctrl+Break后,打印类的信息。

    例如:

     

    二、堆的分配参数:

    1、-Xmx –Xms:指定最大堆和最小堆

    举例、当参数设置为如下时:

    -Xmx20m -Xms5m

    然后我们在程序中运行如下代码:

    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");     //系统的最大空间
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间

     运行效果:

    保持参数不变,在程序中运行如下代码:(分配1M空间给数组)

    复制代码
    byte[] b = new byte[1 * 1024 * 1024];
    System.out.println("分配了1M空间给数组");
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");
    复制代码

    运行效果:

    注:Java会尽可能将total mem的值维持在最小堆。

    保持参数不变,在程序中运行如下代码:(分配10M空间给数组)

    复制代码
    byte[] b = new byte[10 * 1024 * 1024];
    System.out.println("分配了10M空间给数组");
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间
    复制代码

    运行效果:

    如上图红框所示:此时,total mem 为7M时已经不能满足需求了,于是total mem涨成了16.5M。

     

    保持参数不变,在程序中运行如下代码:(进行一次GC的回收)

    复制代码
    System.gc();
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间 
    复制代码

    运行效果:

    问题1: -Xmx(最大堆空间)和 –Xms(最小堆空间)应该保持一个什么关系,可以让系统的性能尽可能的好呢?

    问题2:如果你要做一个Java的桌面产品,需要绑定JRE,但是JRE又很大,你如何做一下JRE的瘦身呢?

     

    2、-Xmn、-XX:NewRatio、-XX:SurvivorRatio:

    • -Xmn

        设置新生代大小

    • -XX:NewRatio

        新生代(eden+2*s)和老年代(不包含永久区)的比值

            例如:4,表示新生代:老年代=1:4,即新生代占整个堆的1/5

    • -XX:SurvivorRatio(幸存代)

        设置两个Survivor区和eden的比值

            例如:8,表示两个Survivor:eden=2:8,即一个Survivor占年轻代的1/10

     

    现在运行如下这段代码:

    复制代码
    public class JavaTest {
        public static void main(String[] args) {
            byte[] b = null;
            for (int i = 0; i < 10; i++)
                b = new byte[1 * 1024 * 1024];
        }
    }
    复制代码

    我们通过设置不同的jvm参数,来看一下GC日志的区别。

     

    (1)当参数设置为如下时:(设置新生代为1M,很小)

    -Xmx20m -Xms20m -Xmn1m -XX:+PrintGCDetails 

    运行效果:

    总结:

      没有触发GC

        由于新生代的内存比较小,所以全部分配在老年代。

     

    (2)当参数设置为如下时:(设置新生代为15M,足够大)

    -Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails

    运行效果:

    上图显示:

    没有触发GC

    全部分配在eden(蓝框所示)

    老年代没有使用(红框所示)

     

    (3)当参数设置为如下时:(设置新生代为7M,不大不小)

    -Xmx20m -Xms20m –Xmn7m -XX:+PrintGCDetails

    运行效果:

    总结:

      进行了2次新生代GC

      s0 s1 太小,需要老年代担保

     

    (4)当参数设置为如下时:(设置新生代为7M,不大不小;同时,增加幸存代大小)

    -Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails

    运行效果:

    总结:

        进行了至少3次新生代GC

        s0 s1 增大

     

    (5)当参数设置为如下时:

    -Xmx20m -Xms20m -XX:NewRatio=1
    
    -XX:SurvivorRatio=2 -XX:+PrintGCDetails 

    运行效果:

     

     

    (6)当参数设置为如下时: 和上面的(5)相比,适当减小幸存代大小,这样的话,能够减少GC的次数

    -Xmx20m -Xms20m -XX:NewRatio=1
    
    -XX:SurvivorRatio=3 -XX:+PrintGCDetails

     

     

    3、-XX:+HeapDumpOnOutOfMemoryError、-XX:+HeapDumpPath

    • -XX:+HeapDumpOnOutOfMemoryError

        OOM时导出堆到文件

          根据这个文件,我们可以看到系统dump时发生了什么。

    • -XX:+HeapDumpPath

        导出OOM的路径

    例如我们设置如下的参数:

    -Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/a.dump

    上方意思是说,现在给堆内存最多分配20M的空间。如果发生了OOM异常,那就把dump信息导出到d:/a.dump文件中。

    然后,我们执行如下代码:

    Vector v = new Vector();
    for (int i = 0; i < 25; i++)
      v.add(new byte[1 * 1024 * 1024]);

    上方代码中,需要利用25M的空间,很显然会发生OOM异常。现在我们运行程序,控制台打印如下:

    现在我们去D盘看一下dump文件:

    8782a0ae-62fb-43a8-a5a6-1c5691e7fa59

    上图显示,一般来说,这个文件的大小和最大堆的大小保持一致。

    我们可以用VisualVM打开这个dump文件。

    注:关于VisualVM的使用,可以参考下面这篇博客:

    使用 VisualVM 进行性能分析及调优:http://www.ibm.com/developerworks/cn/java/j-lo-visualvm/

    或者使用Java自带的Java VisualVM工具也行:

    f9158d50-95d0-4732-942c-e872181fa530

    f69bd0d2-a355-4a93-81c1-c3e71bce7509

    上图中就是dump出来的文件,文件中可以看到,一共有19个byte已经被分配了。 

     

    4、-XX:OnOutOfMemoryError:

    • -XX:OnOutOfMemoryError

        在OOM时,执行一个脚本。

          可以在OOM时,发送邮件,甚至是重启程序。

    例如我们设置如下的参数:

    -XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p //p代表的是当前进程的pid 

    上方参数的意思是说,执行printstack.bat脚本,而这个脚本做的事情是:D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt,即当程序OOM时,在D:/a.txt中将会生成线程的dump。

    5、堆的分配参数总结:

    • 根据实际事情调整新生代和幸存代的大小
    • 官方推荐新生代占堆的3/8
    • 幸存代占新生代的1/10
    • 在OOM时,记得Dump出堆,确保可以排查现场问题

     

    6、永久区分配参数:

    • -XX:PermSize  -XX:MaxPermSize

        设置永久区的初始空间和最大空间。也就是说,jvm启动时,永久区一开始就占用了PermSize大小的空间,如果空间还不够,可以继续扩展,但是不能超过MaxPermSize,否则会OOM。

        他们表示,一个系统可以容纳多少个类型

    代码举例:

    我们知道,使用CGLIB等库的时候,可能会产生大量的类,这些类,有可能撑爆永久区导致OOM。于是,我们运行下面这段代码:

    for(int i=0;i<100000;i++){
      CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap());
    }

    上面这段代码会在永久区不断地产生新的类。于是,运行效果如下:

    fd7bcefb-d6d5-4fe0-8d77-9cddae2733fc

    总结:

      如果堆空间没有用完也抛出了OOM,有可能是永久区导致的。

        堆空间实际占用非常少,但是永久区溢出 一样抛出OOM。

     

    三、栈的分配参数:

    1、Xss:

    设置栈空间的大小。通常只有几百K

      决定了函数调用的深度

      每个线程都有独立的栈空间

      局部变量、参数 分配在栈上

    注:栈空间是每个线程私有的区域。栈里面的主要内容是栈帧,而栈帧存放的是局部变量表,局部变量表的内容是:局部变量、参数。

    我们来看下面这段代码:(没有出口的递归调用)

    复制代码
    public class TestStackDeep {
        private static int count = 0;
    public static void recursion(long a, long b, long c) { long e = 1, f = 2, g = 3, h = 4, i = 5, k = 6, q = 7, x = 8, y = 9, z = 10; count++; recursion(a, b, c); }
    public static void main(String args[]) { try { recursion(0L, 0L, 0L); } catch (Throwable e) { System.out.println("deep of calling = " + count); e.printStackTrace(); } } }
    复制代码

    上方这段代码是没有出口的递归调用,肯定会出现OOM的。

    如果设置栈大小为128k:

    -Xss128K 

    运行效果如下:(方法被调用了294次)

    5c2b2060-e54a-4e7c-9a30-81567204d55b

    如果设置栈大小为256k:(方法被调用748次)

    7d6be7d6-b646-42bf-9357-1a3bccbb7a49

    意味着函数调用的次数太深,例如递归调用。

     

     

    转载于:https://www.cnblogs.com/wxw7blog/p/7293935.html

    展开全文
  • 既然学习JVM,阅读GC日志是处理Java虚拟机内存问题基础技能,它只是一些人为确定规则,没有太多技术含量。 既然如此,那么在IDE控制台打印GC日志是必不可少了。现在就告诉你怎么打印。 (1)如果你用是...

    本文主要内容:

    • Trace跟踪参数
    • 堆的分配参数
    • 栈的分配参数

     

    零、在IDE的后台打印GC日志:

    既然学习JVM,阅读GC日志是处理Java虚拟机内存问题的基础技能,它只是一些人为确定的规则,没有太多技术含量。

    既然如此,那么在IDE的控制台打印GC日志是必不可少的了。现在就告诉你怎么打印。

    (1)如果你用的是Eclipse,打印GC日志的操作如下:

    d32742cf-b002-4c55-a185-d4ccdc90a69c

    bc5b8afb-9d1f-438b-9225-ee7fbbbe2454

    在上图的箭头处加上-XX:+PrintGCDetails这句话。于是,运行程序后,GC日志就可以打印出来了:

    25d80649-69f0-47b2-a3bb-418ba4457849

    (2)如果你用的是IntelliJ IDEA,打印GC日志的操作如下:

    94726055-e81f-45b8-8978-d1277c5acb17

    f2c896da-404c-4415-98ef-5b582dec3528

    在上图的箭头处加上-XX:+PrintGCDetails这句话。于是,运行程序后,GC日志就可以打印出来了:

    6b1b4352-7172-4404-ac6c-b94c16036d73

    当然了,光有-XX:+PrintGCDetails这一句参数肯定是不够的,下面我们详细介绍一下更多的参数配置。

     

    一、Trace跟踪参数:

    1、打印GC的简要信息:

    -verbose:gc
    -XX:+printGC

    解释:可以打印GC的简要信息。比如:

    [GC 4790K->374K(15872K), 0.0001606 secs]

    [GC 4790K->374K(15872K), 0.0001474 secs]

    [GC 4790K->374K(15872K), 0.0001563 secs]

    [GC 4790K->374K(15872K), 0.0001682 secs]

    上方日志的意思是说,GC之前,用了4M左右的内存,GC之后,用了374K内存,一共回收了将近4M。内存大小一共是16M左右。

     

    2、打印GC的详细信息:

    -XX:+PrintGCDetails

    解释:打印GC详细信息。

    -XX:+PrintGCTimeStamps

    解释:打印CG发生的时间戳。

     

    理解GC日志的含义:

    例如下面这段日志:

    [GC[DefNew: 4416K->0K(4928K), 0.0001897 secs] 4790K->374K(15872K), 0.0002232 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

    上方日志的意思是说:这是一个新生代的GC。方括号内部的“4416K->0K(4928K)”含义是:“GC前该内存区域已使用容量->GC后该内存区域已使用容量(该内存区域总容量)”。而在方括号之外的“4790K->374K(15872K)”表示“GC前Java堆已使用容量->GC后Java堆已使用容量(Java堆总容量)”。

    再往后看,“0.0001897 secs”表示该内存区域GC所占用的时间,单位是秒。

     

    再比如下面这段GC日志:

    1fe41f36-cc6b-4a8b-b48e-8cbe2e3a04af

    上图中,我们先看一下用红框标注的“[0x27e80000, 0x28d80000, 0x28d80000)”的含义,它表示新生代在内存当中的位置:第一个参数是申请到的起始位置,第二个参数是申请到的终点位置,第三个参数表示最多能申请到的位置。上图中的例子表示新生代申请到了15M的控件,而这个15M是等于:(eden space的12288K)+(from space的1536K)+(to space的1536K)。

    疑问:分配到的新生代有15M,但是可用的只有13824K,为什么会有这个差异呢?等我们在后面的文章中学习到了GC算法之后就明白了。

     

    3、指定GC log的位置:

    -Xloggc:log/gc.log

    解释:指定GC log的位置,以文件输出。帮助开发人员分析问题。

    805e8e33-1e3b-46c0-af9d-d68f4d38816f

      

    -XX:+PrintHeapAtGC

    解释:每一次GC前和GC后,都打印堆信息。

    例如:

    1c6f3837-4b31-4ac2-a639-e79c92f80df5

    上图中,红框部分正好是一次GC,红框部分的前面是GC之前的日志,红框部分的后面是GC之后的日志。

     

    -XX:+TraceClassLoading

    解释:监控类的加载。

    例如:

    [Loaded java.lang.Object from shared objects file]

    [Loaded java.io.Serializable from shared objects file]

    [Loaded java.lang.Comparable from shared objects file]

    [Loaded java.lang.CharSequence from shared objects file]

    [Loaded java.lang.String from shared objects file]

    [Loaded java.lang.reflect.GenericDeclaration from shared objects file]

    [Loaded java.lang.reflect.Type from shared objects file]

     

    -XX:+PrintClassHistogram

     

    解释:按下Ctrl+Break后,打印类的信息。

    例如:

    c8050739-0029-47cd-95bd-fbbd6289a5d1

     

    二、堆的分配参数:

    1、-Xmx –Xms:指定最大堆和最小堆

    举例、当参数设置为如下时:

    -Xmx20m -Xms5m

    然后我们在程序中运行如下代码:

    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");     //系统的最大空间
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间

     运行效果:

    79c1029d-58fe-47d9-aa2e-1c5ee7e741cd

    保持参数不变,在程序中运行如下代码:(分配1M空间给数组)

    复制代码
    byte[] b = new byte[1 * 1024 * 1024];
    System.out.println("分配了1M空间给数组");
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");
    复制代码

    运行效果:

    14d260c9-28bf-4544-a36f-ee14a1d59623

    注:Java会尽可能将total mem的值维持在最小堆。

    保持参数不变,在程序中运行如下代码:(分配10M空间给数组)

    复制代码
    byte[] b = new byte[10 * 1024 * 1024];
    System.out.println("分配了10M空间给数组");
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间
    复制代码

    运行效果:

    284e8036-8d70-46bc-aac1-99c9b3deb3ef

    如上图红框所示:此时,total mem 为7M时已经不能满足需求了,于是total mem涨成了16.5M。

     

    保持参数不变,在程序中运行如下代码:(进行一次GC的回收)

    复制代码
    System.gc();
    System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间
    System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
    System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间 
    复制代码

    运行效果:

    e419c020-0da3-4046-9b7f-f542ee14a780

    问题1: -Xmx(最大堆空间)和 –Xms(最小堆空间)应该保持一个什么关系,可以让系统的性能尽可能的好呢?

    问题2:如果你要做一个Java的桌面产品,需要绑定JRE,但是JRE又很大,你如何做一下JRE的瘦身呢?

     

    2、-Xmn、-XX:NewRatio、-XX:SurvivorRatio:

    • -Xmn

        设置新生代大小

    • -XX:NewRatio

        新生代(eden+2*s)和老年代(不包含永久区)的比值

            例如:4,表示新生代:老年代=1:4,即新生代占整个堆的1/5

    • -XX:SurvivorRatio(幸存代)

        设置两个Survivor区和eden的比值

            例如:8,表示两个Survivor:eden=2:8,即一个Survivor占年轻代的1/10

     

    现在运行如下这段代码:

    复制代码
    public class JavaTest {
        public static void main(String[] args) {
            byte[] b = null;
            for (int i = 0; i < 10; i++)
                b = new byte[1 * 1024 * 1024];
        }
    }
    复制代码

    我们通过设置不同的jvm参数,来看一下GC日志的区别。

     

    (1)当参数设置为如下时:(设置新生代为1M,很小)

    -Xmx20m -Xms20m -Xmn1m -XX:+PrintGCDetails 

    运行效果:

    4f0b24b4-cc74-4fd6-af15-b30a784d351b

    总结:

      没有触发GC

        由于新生代的内存比较小,所以全部分配在老年代。

     

    (2)当参数设置为如下时:(设置新生代为15M,足够大)

    -Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails

    运行效果:

    2cb6145f-8c1b-4269-bcfa-31912d2f0d41

    上图显示:

    没有触发GC

    全部分配在eden(蓝框所示)

    老年代没有使用(红框所示)

     

    (3)当参数设置为如下时:(设置新生代为7M,不大不小)

    -Xmx20m -Xms20m –Xmn7m -XX:+PrintGCDetails

    运行效果:

    0e0cc65d-e291-477a-ba7f-7d433f1085cc

    总结:

      进行了2次新生代GC

      s0 s1 太小,需要老年代担保

     

    (4)当参数设置为如下时:(设置新生代为7M,不大不小;同时,增加幸存代大小)

    -Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails

    运行效果:

    35eb96d6-9251-45e5-8120-05b82210df06

    总结:

        进行了至少3次新生代GC

        s0 s1 增大

     

    (5)当参数设置为如下时:

    -Xmx20m -Xms20m -XX:NewRatio=1
    
    -XX:SurvivorRatio=2 -XX:+PrintGCDetails 

    运行效果:

    c85f7057-1842-4d11-bc28-fc766e5681f8

     

     

    (6)当参数设置为如下时: 和上面的(5)相比,适当减小幸存代大小,这样的话,能够减少GC的次数

    -Xmx20m -Xms20m -XX:NewRatio=1
    
    -XX:SurvivorRatio=3 -XX:+PrintGCDetails

    fd3322ec-a853-49aa-86fa-81d8b3a02f8c

     

     

    3、-XX:+HeapDumpOnOutOfMemoryError、-XX:+HeapDumpPath

    • -XX:+HeapDumpOnOutOfMemoryError

        OOM时导出堆到文件

          根据这个文件,我们可以看到系统dump时发生了什么。

    • -XX:+HeapDumpPath

        导出OOM的路径

    例如我们设置如下的参数:

    -Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/a.dump

    上方意思是说,现在给堆内存最多分配20M的空间。如果发生了OOM异常,那就把dump信息导出到d:/a.dump文件中。

    然后,我们执行如下代码:

    Vector v = new Vector();
    for (int i = 0; i < 25; i++)
      v.add(new byte[1 * 1024 * 1024]);

    上方代码中,需要利用25M的空间,很显然会发生OOM异常。现在我们运行程序,控制台打印如下:

    3320aba5-2aa6-42bc-b656-57bbc5d8ec41

    现在我们去D盘看一下dump文件:

    8782a0ae-62fb-43a8-a5a6-1c5691e7fa59

    上图显示,一般来说,这个文件的大小和最大堆的大小保持一致。

    我们可以用VisualVM打开这个dump文件。

    注:关于VisualVM的使用,可以参考下面这篇博客:

    使用 VisualVM 进行性能分析及调优:http://www.ibm.com/developerworks/cn/java/j-lo-visualvm/

    或者使用Java自带的Java VisualVM工具也行:

    f9158d50-95d0-4732-942c-e872181fa530

    f69bd0d2-a355-4a93-81c1-c3e71bce7509

    上图中就是dump出来的文件,文件中可以看到,一共有19个byte已经被分配了。 

     

    4、-XX:OnOutOfMemoryError:

    • -XX:OnOutOfMemoryError

        在OOM时,执行一个脚本。

          可以在OOM时,发送邮件,甚至是重启程序。

    例如我们设置如下的参数:

    -XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p //p代表的是当前进程的pid 

    上方参数的意思是说,执行printstack.bat脚本,而这个脚本做的事情是:D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt,即当程序OOM时,在D:/a.txt中将会生成线程的dump。

    5、堆的分配参数总结:

    • 根据实际事情调整新生代和幸存代的大小
    • 官方推荐新生代占堆的3/8
    • 幸存代占新生代的1/10
    • 在OOM时,记得Dump出堆,确保可以排查现场问题

     

    6、永久区分配参数:

    • -XX:PermSize  -XX:MaxPermSize

        设置永久区的初始空间和最大空间。也就是说,jvm启动时,永久区一开始就占用了PermSize大小的空间,如果空间还不够,可以继续扩展,但是不能超过MaxPermSize,否则会OOM。

        他们表示,一个系统可以容纳多少个类型

    代码举例:

    我们知道,使用CGLIB等库的时候,可能会产生大量的类,这些类,有可能撑爆永久区导致OOM。于是,我们运行下面这段代码:

    for(int i=0;i<100000;i++){
      CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap());
    }

    上面这段代码会在永久区不断地产生新的类。于是,运行效果如下:

    fd7bcefb-d6d5-4fe0-8d77-9cddae2733fc

    总结:

      如果堆空间没有用完也抛出了OOM,有可能是永久区导致的。

        堆空间实际占用非常少,但是永久区溢出 一样抛出OOM。

     

    三、栈的分配参数:

    1、Xss:

    设置栈空间的大小。通常只有几百K

      决定了函数调用的深度

      每个线程都有独立的栈空间

      局部变量、参数 分配在栈上

    注:栈空间是每个线程私有的区域。栈里面的主要内容是栈帧,而栈帧存放的是局部变量表,局部变量表的内容是:局部变量、参数。

    我们来看下面这段代码:(没有出口的递归调用)

    复制代码
    public class TestStackDeep {
        private static int count = 0;
    public static void recursion(long a, long b, long c) { long e = 1, f = 2, g = 3, h = 4, i = 5, k = 6, q = 7, x = 8, y = 9, z = 10; count++; recursion(a, b, c); }
    public static void main(String args[]) { try { recursion(0L, 0L, 0L); } catch (Throwable e) { System.out.println("deep of calling = " + count); e.printStackTrace(); } } }
    复制代码

    上方这段代码是没有出口的递归调用,肯定会出现OOM的。

    如果设置栈大小为128k:

    -Xss128K 

    运行效果如下:(方法被调用了294次)

    5c2b2060-e54a-4e7c-9a30-81567204d55b

    如果设置栈大小为256k:(方法被调用748次)

    7d6be7d6-b646-42bf-9357-1a3bccbb7a49

    意味着函数调用的次数太深,像这种递归调用就是个典型的例子。

     

    总结:

    我们在本文中介绍了jvm的一些最基本的参数,还有很多参数(如GC参数等)将在后续的系列文章中进行介绍。我们将在接下来的文章中介绍GC算法。

     

    from: https://yq.aliyun.com/articles/37181?spm=5176.100239.blogcont37195.25.DtxXZF

    转载于:https://www.cnblogs.com/KingIceMou/p/6967371.html

    展开全文
  • 既然学习JVM,阅读GC日志是处理Java虚拟机内存问题基础技能,它只是一些人为确定规则,没有太多技术含量。 既然如此,那么在IDE控制台打印GC日志是必不可少了。现在就告诉你怎么打印。 (1)如果你用...
  • 既然学习JVM,阅读GC日志是处理Java虚拟机内存问题基础技能,它只是一些人为确定规则,没有太多技术含量。 既然如此,那么在IDE控制台打印GC日志是必不可少了。现在就告诉你怎么打印。 (1)如果你用是...
  • java虚拟机内存管理

    2017-05-21 00:09:00
    java heamp是java虚拟机配置管理最大内存区;唯一目的用于存储实例对象,所有线程共享; GC堆,回收技术主要采用分代收集算法;java Heap细分为:新生代、老生代;划分本身与存放内容无关;无论怎么划分,都...
  • 常用JVM配置参数

    2020-07-11 21:00:32
    既然学习JVM,阅读GC日志是处理Java虚拟机内存问题基础技能,它只是一些人为确定规则,没有太多技术含量。 既然如此,那么在IDE控制台打印GC日志是必不可少了。现在就告诉你怎么打印。 (1)如果你用...
  • JVM常用参数配置

    2019-10-08 03:13:07
    既然学习JVM,阅读GC日志是处理Java虚拟机内存问题基础技能,它只是一些人为确定规则,没有太多技术含量。 既然如此,那么在IDE控制台打印GC日志是必不可少了。现在就告诉你怎么打印。 (1)如果你用是...
  • 既然学习JVM,阅读GC日志是处理Java虚拟机内存问题基础技能,它只是一些人为确定规则,没有太多技术含量。 既然如此,那么在IDE控制台打印GC日志是必不可少了。现在就告诉你怎么打印。 (1)如果你用是...
  • 狂伸说-JVM学习总结

    2021-03-07 17:18:46
    JVM学习总结JVM探究1、JVM位置2、JVM体系结构3、类加载器4、双亲委派机制5、方法区与元空间解释与区别6、GC6.1、复制算法:6.2、标记压缩:6.3、总结7、JMM8代码和IDEA虚拟机参数配置8.1、这是IDEAjava...
  • android151 笔记 3

    2016-01-28 00:56:00
    34. 对android虚拟机的理解,包括内存管理机制垃圾回收机制。 虚拟机很小,空间很小,谈谈移动设备的虚拟机的大小限制 16M , 谈谈加载图片的时候怎么处理大图片的,压缩。 垃圾回收,没有引用的对象,在某个时刻会被...
  • JVM参数详解

    千次阅读 2018-10-03 09:56:18
    既然学习JVM,阅读GC日志是处理Java虚拟机内存问题基础技能,它只是一些人为确定规则,没有太多技术含量。 既然如此,那么在IDE控制台打印GC日志是必不可少了。现在就告诉你怎么打印。 (1)如果你用是...
  • 电话面试一

    2016-05-22 13:34:30
    这个要看实现JVM规范的虚拟机怎么设置,hotspot是把永久代也放在堆中,但是默认不GC需要配置。 2Servlet 生命周期INIT SERVICE DESTROY 再具体点有一个无参有一个有参INIT 有参会调用无参,所以建议重写无参...
  • OOM,native meory exhausted

    2015-04-28 14:32:01
    在前置平台测大报文的时候报出来的OOM,出core的dump文件4~5m很小,虚拟机配置的内存为512~1024M,从core文件中能看到出core的时候堆内存还有很大。现在测试的思路是修改MaxNewSize,但是根据测试情况完全不明白这个...
  • 我问了下这台机器的配置是啥?虚拟机:1核2Gjvm配置: xms:1g xmx:1g我就开始着手查看监控指标怎么样?   监控系统这两天在升级,暂时看不了;  开发反馈,load在1左右,cpu高峰也就80%,我先信了。...
  • 05_NoGc: 展示怎么去避免值类型的GC。 06_Coroutine: 展示lua协程怎么和Unity协程相配合。 07_AsyncTest: 展示怎么用lua协程来把异步逻辑同步化。 08_Hotfix: 热补丁的示例(需要开启热补丁特性,如何开启请看...

空空如也

空空如也

1 2 3
收藏数 56
精华内容 22
关键字:

虚拟机的配置的gc怎么配置