1.什么是JVM?
- Java虚拟机

-
栈:线程私有,存放着每个方法的局部变量,执行Java方法
-
堆:被所有线程共享的一块内存区域,存放着对象
-
方法区(元空间):存放着静态变量、常量、类元信息(加载的类信息)
-
程序计数器:线程私有,当前线程所执行的Java字节码的行号指示器
-
本地方法栈:执行Native方法(通过JNI直接调用本地C/C++库)
-
类的加载机制:将.class字节码文件加载到内存中,并将这些静态数据转化成方法区中可运行的数据结构(加载、校验、准备、解析、初始化、使用、卸载)
-
运行代码
public class Math { public int compute() { int a = 1; int b = 2; int c = (a + b) * 10; return c; } public static void main(String[] args) { Math math = new Math(); math.compute(); System.out.println("test"); } } -
字节码反汇编
Compiled from "Math.java" public class test.Math { public test.Math(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public int compute(); Code: 0: iconst_1 1: istore_1 2: iconst_2 3: istore_2 4: iload_1 5: iload_2 6: iadd 7: bipush 10 9: imul 10: istore_3 11: iload_3 12: ireturn public static void main(java.lang.String[]); Code: 0: new #2 // class test/Math 3: dup 4: invokespecial #3 // Method "<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #4 // Method compute:()I 12: pop 13: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 16: ldc #6 // String test 18: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 21: return } -
GC Root对象
Java虚拟机栈(局部变量表)中的引用的对象 方法区中静态引用指向的对象 仍处于存活中的线程对象 Native方法中JNI引用的对象 -
可达性分析算法

- GC(Garbage Collection)算法

2.为什么要JVM调优?
-
前提:已无代码再优化问题
为了提升系统性能,JVM调优并不是我们的首选方案,因为“优化代码”所需要的成本更低,JVM层面的调优不光是对服务器性能有要求,不同的项目也需要制定相应的调优方案。
- 设计高性能算法、使用相匹配的设计模式
- 合理运用Java四大引用
引用 GC回收时机 使用示例 强引用 如果一个对象具有强引用,那垃圾回收器绝不会回收它 Object obj = new Object(); 软引用 在内存实在不足时(即将发生OOM),会对软引用进行回收 SoftReference softObj = new SoftReference(); 弱引用 第一次GC回收时,如果垃圾回收器遍历到此弱引用,则将其回收 WeakReference weakObj = new WeakReference(); 虚引用 一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象的实例 不会使用 -
原因:减少Stop the World事件发生
顾名思义“系统卡顿”,这种现象让用户体验变差,但它的出现也是为了保证系统JVM能按照一定机制正常运行。
-
JVM调优的目的:减少GC的频率和Full GC的次数(减少Stop the World事件)
3.怎样做JVM调优?
-
参数说明
命令参数 功能描述 -verbose:gc 显示GC的操作内容 -Xms20M 初始化堆大小为20M(如果不指定默认为物理内存的1/64) -Xmx20M 设置堆的最大分配内存20M -Xmn10M 设置新生代的内存大小为10M -XX:+PrintGCDetails 打印GC的详细log日志 -XX:SurvivorRatio=8 新生代中Eden区域与Survivor区域的大小比值为8:1:1 -XX:PretenureSizeThreshold 设置从Eden直接升入老年代的对象大小 - 一般情况下-Xms和-Xmx所设置的大小都是一样的,因为这样能够防止避免在GC后调整堆大小带来的压力。
- -Xms 、-Xmx 、-Xmn 后接设置的内存大小,默认单位为Byte,也可以手动设置成M或者是K,表示MB或KB
-
GC log分析(IDEA)
-
设置VM options
(-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8)
-

-
运行代码
public class MinorGCTest { public static final int _1MB = 1024 * 1024; //1KB = 1024B(字节) //1MB = 1024KB public static void testAllocation() { byte[] a1, a2, a3, a4; a1 = new byte[2 * _1MB]; a2 = new byte[2 * _1MB]; a3 = new byte[2 * _1MB]; a4 = new byte[2 * _1MB]; } public static void main(String[] args) { testAllocation(); } } -
运行你的程序得到GC log
[GC (Allocation Failure) [PSYoungGen: 8160K->824K(9216K)] 8160K->6976K(19456K), 0.0028212 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Ergonomics) [PSYoungGen: 824K->0K(9216K)] [ParOldGen: 6152K->6802K(10240K)] 6976K->6802K(19456K), [Metaspace: 3157K->3157K(1056768K)], 0.0034982 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap PSYoungGen total 9216K, used 2130K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) eden space 8192K, 26% used [0x00000000ff600000,0x00000000ff814930,0x00000000ffe00000) from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000) to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000) ParOldGen total 10240K, used 6802K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) object space 10240K, 66% used [0x00000000fec00000,0x00000000ff2a4998,0x00000000ff600000) Metaspace used 3164K, capacity 4496K, committed 4864K, reserved 1056768K class space used 345K, capacity 388K, committed 512K, reserved 1048576K -
打包成jar包输入带参数进行运行程序(类中没有一个对象的创建)

> 但问题来了,为什么没有类的创建为什么Eden区的使用率不是0,而是16%
-
利用Java VisualVMJVM性能分析工具,查看堆中年轻代和老年代的变化
打开dos窗口,输入jvisualvm、jconsole命令,即可打开Java VisualVm工具
能够自动检测到当前电脑正在运行的Java程序,Visual GC窗口需要更新Java VisualVM插件中心URL,再进行下载。

-
运行代码
public class HeapTest { byte[] a = new byte[1024 * 100]; public static void main(String[] args) throws InterruptedException { ArrayList<HeapTest> heapTests = new ArrayList<>(); while (true) { heapTests.add(new HeapTest()); Thread.sleep(10); } } } -
对于不同场景选择合适的垃圾收集器
