摘要
本文将会带您走进JDK1.8的JVM调优的方法。
本文将会带您走进JDK1.8的JVM调优的方法。
在数据量和并发量极高的应用中,程序的健壮性和服务的稳定性一直是性能优化的一块重点区域,而GC则永远是程序中需要重点考虑的不稳定因素之一。
JVM调优大部分是调GC参数, GC参数主要关注三点:最大堆和最小堆大小;GC算法;新生代(年轻代)大小。
在JDK8及更早的版本, GC算法通常会在默认的Parallel和CMS中根据不同场合做选择, 新生代也要根据实际需求和自身经验手动调节大小才能达到性能和STW停顿的平衡.
而在JDK9及更新的版本中, 由于更为"智能"的G1成为默认,GC算法和新生代(年轻代)大小不用手动调节就能在大多数场合下达到几乎最优的性能, 通常只需配置最大堆容量和期望的STW暂停时间即可(不配置也可获得比较均衡合理的性能), 对技术能力和经验的要求大大降低了.
当然,我们需要明白的是:在没有全面监控、收集性能数据之前,调优就是瞎调。
本文为JDK 1.8下JVM调优系列文章的初识篇,主要普及基本的JVM组成和基本编译、运行原理。
JVM是Java Virtual Machine(Java虚拟机)的缩写,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
一个java类从.java到编译为.class再到通过JVM运行在windows和Linux机器上的流程如下:
大概解释下上面图中的流程:
我们在学Java的时候就知道一个基本常识:那就是Java语言是一次编译,多种平台上运行,也就是所说的跨平台。那么为什么Java能跨平台呢?专业的解释是Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行;通俗的解释是我们知道,一个文件在windows和Linux中会被解析成不同的二进制文件,那么其实我们的java文件在linux和windows上编译成class文件(字节码文件),它的机器码是不一样的,那么java是如何实现跨平台的呢?主要原因就是JVM内部针对不同的环境(windows、LinUx…)封装了不同的机器码处理程序(这就是为什么我们下载jdk时不同环境需要选择不同的jdk的最主要原因),当java被编译成class文件后,在不同的操作系统上,不同的JVM会将这个class文件转换成该操作系统所能处理的二进制,于是就实现了java所谓的一次编译、多地运行的目标。
class字节码文件是如何在JVM中运行的呢?
如下图,在运行class文件时候,JVM虚拟机首先会通过“类装在子系统”将.class文件加载到“运行时的数据区(也就是所说的内存模型)”中,最终由“字节码执行引擎”来一行行的运行“内存模型”中的代码。
我们知道JAVA虚拟机中最重要的就是运行时内存(内存模型),接下来我们就详细讲解“运行时数据区”的运行机制。
我们对“堆”的概念都不生疏,刚学JAVA时我们就应该知道New出来的对象都是放到“堆”里面。而且我们应该知道,JVM调优主要调的就是这块,堆放到后面讲。首先我们要讲下栈(线程栈)的概念
“栈”,官方称之为“虚拟机栈(Java Virtual Machine Stacks)”,咱们可以通俗的把它理解为“线程栈”(只要有一个线程在运行,java就会从内存模型中的栈中给它分配一个内存空间,用来放线程内部的局部变量)。“线程栈”的主要作用是为了承载线程运行过程中的局部变量。
例如如下代码:
public class Math { public static final int initData = 666; public static User user = new User(); public int initData1 = 666; 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"); } }
当运行main方法时,JVM就会像下图一样给这个main线程分配一个栈:
当然,如果此时有第二个线程(线程2),那么JVM同样会给线程2创建一个栈。
当然,栈的内部其实很复杂,最直观的是栈内部有“栈帧”的概念。
所谓“栈帧”,指的就是当在当前线程中如果调用了其他方法(例如math.conpute()),此时就会在main线程的栈中再分出一块空间,用于存放该方法的局部变量(可以无限的创建内存统建),该被调用的方法结束,这这个内存空间消失,这就是栈帧。
如下图所示:
我们需要知道的是,栈的数据结构就是使用的数据结构中的栈(先进后出),之所以这样说,咱们可以参照上面的代码讲下:
首先在执行main方法时,JVM给main创建了一个内存空间(栈帧),然后在main方法中调用compute方法,此时,JVM给compute方法创建了一个内存空间(栈帧),当compute方法运行结束(局部变量销毁,栈帧销毁),继续执行main方法的sysout方法,然后main方法所在的栈帧销毁,遵循了先进后出的原则。