JVM调优

JVM垃圾回收的时候如何确定垃圾?

什么是垃圾?

垃圾:简单来说就是内存中已经不再被使用到的空间就是垃圾

如何判断一个对象是否可以被回收?

  1. 引用计数法(该算法已经过时了,解决不掉循环引用的问题)

    每当有一个地方引用它,计数器值加1;每当有一个引用失效,计数器值减1

    任何时刻计数器值为零的对象就是不可能再被使用的,那么这个对象就是可回收对象。

  2. 枚举根节点做可达性分析

    基本思路就是通过一系列名为GC Roots的对象作为起始点,从这个被称为GC Roots的对象开始向下搜索,如

    果一个对象到GC Roots没有任何引用链相连,则说明此对象不可用。

那些对象可以当做GC Roots?

  1. 虚拟机栈(栈帧中的局部变量表)中的引用对象
  2. 方法区中的类静态属性引用的对象(static)
  3. 方法区中常量引用的对象(例如字符串常量池里的引用)
  4. 本地方法栈中的Native方法的引用对象

代码说明

public class GCRootDemo {
    // private static GCRootDemo2 t2; //方法区中的类静态属性引用的对象

    // private static final GCRootDemo3 t3 = new GCRootDemo3(8); //方法区中的常量引用

    public static void m1() {
        GCRootDemo t1 = new GCRootDemo(); // 虚拟机栈中的引用对象
        System.gc();
        System.out.println("第一次GC完成");
    }

    public static void main(String[] args) {
        m1();
    }
}

如何盘点查看JVM系统默认值?

JVM的参数类型

标配参数

  • -version
  • -help
  • java -showversion

x参数(了解)

  • -Xint (解释执行)
  • -Xcomp (第一次使用就编译成本地代码)
  • -Xmixed (混合模式:先编译再执行)

1610988920180

xx参数

1、xx参数之布尔类型

公式:-XX:+ 或者 -XX:-,+表示开启,-表示关闭​​

案例:开启打印GC收集细节。-XX:+PrintGCDetails

2、xx参数之key-value类型

公式:-XX:属性key=属性value

案例:查看Java元空间大小默认:-XX:MetaspaceSize=21807104

如果想修改kv类型的值,把等号后面的值直接改了即可

如何查看运行的Java程序,JVM参数是否开启,具体值为多少?

首先我们运行一个HelloGC的java程序,我想查看PrintGCDetails参数是否开启了。

public class HelloGC {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("hello GC");
        Thread.sleep(Integer.MAX_VALUE);
    }
}

1、先通过jps -l,得到进程号。

2、然后使用 jinfo -flag 具体参数 进程号 然后查看是否开启PrintGCDetails这个参数

1610990081306

如果我们想开启这个参数,需要在运行程序的时候配置JVM参数。在IDEA中可以点Run——Edit Configurations

然后在VM Options中加入下面的代码,现在+号表示开启。

-XX:+PrintGCDetails

再次查看我们的配置,我们看到原来的-号变成了+号,说明我们通过 VM Options配置的JVM参数已经生效了!!

jinfo -flags 进程号。是打印出搜索到的全部参数,Command line代表自己配置的。

题外话(坑题)

两个经典参数:-Xms 和 -Xmx,这个如何解释?

  • -Xms:等价于-XX:InitialHeapSize(初始化堆内存)
  • -Xmx:等价于-XX:MaxHeapSize(最大堆内存)

因为比较常用,相当于起了个别名,类型属于xx参数。

JVM的初始参数盘点

  • -XX:+PrintFlagsInitial(主要是查看初始默认值)

  • -XX:+PrintFlagsFinal(查看修改以后,最终的值)
我们发现有一些参数有 :=,表示人为修改过或jvm自己修改过的, =表示没有修改过的

1611048400765

  • -XX:+PrintCommandLineFlags

1611050859056

这个最方便的是可以直接看到,现在默认的垃圾回收器用的是哪一个。

工作中JVM的常用配置有哪些?

查看堆内存

用java程序,查看JVM的初始化堆内存 -Xms 和最大堆内存 Xmx

public static void main(String[] args) throws InterruptedException {
    // 返回Java虚拟机中内存的总量
    long totalMemory = Runtime.getRuntime().totalMemory();

    // 返回Java虚拟机中试图使用的最大内存量
    long maxMemory = Runtime.getRuntime().maxMemory();
    
System.out.println("TOTAL_MEMORY(-Xms) = " + totalMemory + "(字节)、" + (totalMemory / (double)1024 / 1024) + "MB");
    
System.out.println("MAX_MEMORY(-Xmx) = " + maxMemory + "(字节)、" + (maxMemory / (double)1024 / 1024) + "MB");
    
}

打印结果为:

TOTAL_MEMORY(-Xms) = 128974848(字节)、123.0MB
MAX_MEMORY(-Xmx) = 1884815360(字节)、1797.5MB

常用参数

栈管运行,堆管存储
  • -Xms(-XX:InitialHeapSize)

初始堆大小内存,默认为物理内存的1/64

  • -Xmx(-XX:MaxHeapSize)

最大分配内存,默认为物理内存的1/4

  • -Xss(-XX:ThreadStackSize)

设置单个线程栈的大小,一般默认为512k~1024k。

使用jinfo -flag ThreadStackSize 会发现 -XX:ThreadStackSize = 0为什么呢?

查看官方发现,0代表默认值,这个默认值的大小取决于平台。例如Linux/x64、OS X是1024KB,Windows取决于虚拟内存的大小。

  • -Xmn

设置年轻代大小,一般用默认值就行。

  • -XX:MetaspaceSize

设置元空间大小。(元空间的本质和永久代类似,都是对JVM规范中方法区的实现。他们最大的区别在于:元空间并不在虚拟机中,而是使用本地内存,因此元空间的大小仅受本地内存限制)。

默认的大小为21807104,相当于21M。为了防止在频繁的实例化对象的时候,让元空间出现OOM。要把内存调大些,可以调成1024m!

  • 典型设置案例
-Xms128m -Xmx4096m -Xss1024k -XX:MetaspaceSize=512m -XX:+PrintCommandLineFlags
-XX:+PrintGCDetails -XX:+UseParallelGC
  • -XX:PrintGCDetails
    • 输出详细GC收集日志信息
    • 首先我们用一段代码,制造出垃圾回收的过程

      //首先我们设置一下程序的启动配置: 设置初始堆内存为10M,最大堆内存为10M    
      
      //然后用下列代码,创建一个 非常大空间的byte类型数组
      byte [] byteArray = new byte[50 * 1024 * 1024];

      运行后,发现会出现下列错误,这就是OOM:java内存溢出,也就是堆空间不足

      Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
          at com.moxi.interview.study.GC.HelloGC.main(HelloGC.java:22)

      同时打印出了GC垃圾回收时候的详情......

      [GC (Allocation Failure) [PSYoungGen: 1972K->504K(2560K)] 1972K->740K(9728K), 0.0156109 secs] [Times: user=0.00 sys=0.00, real=0.03 secs] 
      [GC (Allocation Failure) [PSYoungGen: 504K->480K(2560K)] 740K->772K(9728K), 0.0007815 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
      [Full GC (Allocation Failure) [PSYoungGen: 480K->0K(2560K)] [ParOldGen: 292K->648K(7168K)] 772K->648K(9728K), [Metaspace: 3467K->3467K(1056768K)], 0.0080505 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
      [GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 648K->648K(9728K), 0.0003035 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
      [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 648K->630K(7168K)] 648K->630K(9728K), [Metaspace: 3467K->3467K(1056768K)], 0.0058502 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
      Heap
       PSYoungGen      total 2560K, used 80K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
        eden space 2048K, 3% used [0x00000000ffd00000,0x00000000ffd143d8,0x00000000fff00000)
        from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
        to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
       ParOldGen       total 7168K, used 630K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
        object space 7168K, 8% used [0x00000000ff600000,0x00000000ff69dbd0,0x00000000ffd00000)
       Metaspace       used 3510K, capacity 4500K, committed 4864K, reserved 1056768K
        class space    used 389K, capacity 392K, committed 512K, reserved 1048576K

      这tm看不懂啊,我们通过这张图来解析里面的参数:

      YoungGC:

      JVM调优_第1张图片

      Full GC:(大部分发生在养老区)

      1611118233584

      规律:[名称: GC前内存占用 -> GC后内存占用 (该区内存总大小)]

  • -XX:SurvivorRatio
    • java堆从GC的角度可以细分为: 新生代(Eden区、FromSurvivor区、ToSurvivor区)和 老年代

      1611120758565

    • 设置新生代中eden和S0/S1空间的比例。默认值为:-XX:SurvivorRatio=8,即Eden:S0:S1=8:1:1
    • 假如-XX:SurvivorRatio=4,则Eden:S0:S1=4:1:1。Survivor值就是设置Eden区的比例占多少,S0/S1是相同的。
  • -XX:NewRatio
    • 设置年轻代与老年代在堆中的比例。默认值为:-XX:NewRatio=2,即新生代占1,老年代占2,年轻代占整个堆的1/3。
    • 假如-XX:NewRatio=4,则新生代占1,老年代占4,年轻代占整个堆的1/5。NewRatio值就是设置老年代的占比,剩下的1给新生代。
    如果新生代特别小,会造成频繁的进行GC收集
  • -XX:MaxTenuringThreshold
    • 设置垃圾最大年龄,SurvivorTo和SurvivorFrom互换,原SurvivorTo成为下一次GC时的SurvivorFrom区,部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,这个参数默认为15),最终如果还是存活,就存入老年代。
    • 默认是15,并且设置的值 在 0~15之间(因为就四个字节,最大1111=15)
    • 如果设置为0的话,则年轻对象不经过Survivor区,直接进入老年代;对于老年代比较多的应用,可以提高效率。如果将此值设置较大,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存活空间。

你可能感兴趣的:(javaJVM)