VisualVM介绍VisualVM是一款免费的,集成了多个JDK命令行工具的可视化工具,它能为您提供强大的分析能力,对Java应用程序做性能分析和调优。这些功能包括生成和分析海量数据、跟踪内存泄漏、监控垃圾回收器、执行内存和CPU分析,同时它还支持在MBeans上进行浏览和操作。本文主要介绍如何使用VisualVM进行性能分析及调优。VisualVM位于{JAVA_HOME}/bin目录中。点击运行,效果如下:下面我们来看visualvm的各种功能查看jvm配置信息第一步:点击左边窗口显示正在运行的java进程第二步:点击右侧窗口“概述”,可以查看各种配置信息通过jdk提供的jinfo命令工
jconsole介绍JConsole(javamonitoringandmanagementconsole)是一款基于JMX的可视化监视和管理工具。启动JConsole点击JDK/bin目录下面的“jconsole.exe”即可启动然后会自动自动搜索本机运行的所有虚拟机进程选择其中一个进程可开始进行监控JConsole基本介绍JConsole基本包括以下基本功能:概述、内存、线程、类、VM概要、MBean运行下面的程序、然后使用JConsole进行监控;注意设置虚拟机参数packagecom.jvm.jconsole;importjava.util.ArrayList;importjava.u
jstack介绍jstack(stacktraceforjava)是java虚拟机自带的一种堆栈跟踪工具。jstack用于打印出给定的java进程ID或corefile或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jstack使用方式只支持以下的这种方式:jstack[-l]pid主要分为两个功能:针对活着的进程做本地的或远程的线程dump针对core文件做线程dumpjstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程
jhatjhat也是jdk内置的工具之一。主要是用来分析java堆的命令,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言。使用jmap等方法生成java的堆文件后,使用其进行分析。示例:运行代码packagecom.jvm.test8;importlombok.*;importjava.util.ArrayList;importjava.util.List;importjava.util.UUID;importjava.util.concurrent.TimeUnit;publicclassTest8{@Getter@Setter@ToString@Bu
jmapjdk安装后会自带一些小工具,jmap命令(MemoryMapforJava)是其中之一。主要用于打印指定Java进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节。jmap命令可以获得运行中的jvm的堆的快照,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的大对象的创建,检查系统中什么对象最多,各种对象所占内存的大小等等。可以使用jmap生成HeapDump。如果不想使用jmap命令,要想获取Java堆转储快照还有一些比较“暴力”的手段:譬如在前面用过的-XX:+HeapDumpOnOutOfMemoryError参数,可以让虚拟机在OOM异常出现之后自动生成
jinfojinfo(ConfigurationInfoforJava)的作用是实时地查看和调整虚拟机的各项参数。使用jps-v可以查看虚拟机启动时显示指定的参数列表,但是如果想知道未被显示指定的参数的系统默认值,除了去找资料外,就只能使用jinfo的-flag选项进行查询了。[root@localhost~]#jinfoUsage:jinfo[option]<pid>(toconnecttorunningprocess)jinfo[option]<executable<core>(toconnecttoacorefile)jinfo[option][server
jstat(JVMStatisticsMonitorningTool)用于监控虚拟机各种运行状态信息的命令行工具。它可以显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据,它是运行期定位虚拟机性能问题的首选工具。语法:[root@localhost~]#jstat-helpUsage:jstat-help|-optionsjstat-<option>[-t][-h<lines>]<vmid>[<interval>[<count>]]Definitions:<option>Anoptionreporte
jpsjps(JVMProcessStatusTool):虚拟机进程状态工具,可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(MainClass,main()函数所在的类)的名称,以及这些进程的本地虚拟机的唯一ID(LVMID,LocalVitualMachineIdentifier),它是使用频率最高的JDK命令行工具,因为其他JDK工具大多需要输入它查询到的LVMID来确定要监控的是哪一个虚拟机进程。对于本地虚拟机进程来说,LVMID与操作系统进程ID(PID,ProcessIdentifier)是一致的。如果同时启动了多个虚拟机进程,无法根据进程名称定位时,那就只能依靠jps命令显示
一、简介本章,我们将介绍一个因为处理消息队列中的数据不当而引起的内存溢出问题,先来看下系统的背景。1.1系统背景这是一个线上的数据同步系统,专门从Kafka消费其它系统送进去的数据,处理后存储到自己的数据库中:就这么简单的一个系统,生产上却时不时报出OOM异常,然后就得重启系统。重启系统后,Java堆内存使用率越来越高,直到下次OOM异常。这种情况要么是因为系统并发太高,对象来不及回收,要么就是发生了内存泄漏,很多对象赖在内存里,无论如何就是GC不掉。根据监控系统的显示,系统的访问量并不高,所以很可能就是因为某种对象”赖在了内存里“,然后不断触发GC,但就是无法回收掉:二、问题分析我们通过js
一、简介本章,我们将讲解一个已经稳定运行的系统的内存溢出问题,该内存溢出问题的元凶是类加载器,我们先来看下系统的背景。1.1系统背景这个系统已经在线上稳定运行了一段时间,部署在Tomcat中启动。突然有一天收到告警,显示许多访问该系统的调用请求出现假死现象,但是过了一会儿又可以了。经过排查发现,每隔一段时间,系统就会出现假死。一般来说,系统出现假死,接口无法调用,就是系统的资源不足以处理新的请求,所以我们先通过top命令排查下机器的CPU和内存使用情况。如果这个服务大量使用了内存,导致频繁FullGC(这个问题我们之前的章节介绍过),从而引发STW,接口调用就会出现假死现象。如果机器的CPU负
一、简介本章,我们将讲解一个使用Jetty作为Web容器的应用的内存溢出问题,该内存溢出问题发生的区域是堆外内存,主要原因是JVM内存区域划分不合理,我们先来看下系统的背景。1.1系统背景生产环境的一个系统发生告警,拿到生产日志后出现如下字样:niohandlefailedjava.lang.OutOfMemoryError:Directbuffermemoryatorg.eclipse.jetty.io.nio.xxxxatorg.eclipse.jetty.io.nio.xxxxatorg.eclipse.jetty.io.nio.xxxx通过日志,我们可以知道是Directbufferm
一、简介本章,我们将通过实际案例讲解一个Web应用的内存溢出问题,该内存溢出问题的排查涉及Tomcat的一些底层原理,最终排查发现是由于请求超时问题导致,我们先来看下系统的背景。1.1系统背景生产环境的一个系统发生告警,拿到生产日志后出现如下字样:Exceptioninthread"http-nio-8080-exec-1089"java.lang.OutOfMemoryError:Javaheapspace。很明显,Java堆内存区域发生了内存溢出异常。特别要注意的是http-nio-8080-exec-1089,由于当时系统部署在tomcat中(8080端口),所以上面
一、简介本章,我们将通过示例代码演示Java堆内存区域是如何发生内存溢出的,并根据内存快照进行分析。我们回顾下堆内存溢出的一个场景:系统负载很高,不停的在Eden区创建新对象,直到触发YoungGC,但是由于并发太高,YoungGC发现Eden区存活对象非常多,Survivor无法容纳,只能把大批存活对象转移到老年代。经过几次这种YoungGC之后,老年代也满了,于是触发FullGC,但是FullGC之后老年代里还是塞满了对象,导致YoungGC过后的存活对象无处可安放,最终引发堆内存溢出。二、示例程序2.1程序源码packagecom.ressmix.jvm;importjava.util.
一、简介本章,我们将通过示例代码演示Java虚拟机栈区域是如何发生内存溢出的,并根据内存快照进行分析。我们回顾下栈内存溢出的一个场景:每个线程的栈内存是固定的,如果某个线程不停的无限制调用方法,每次方法调用都会有一个栈帧入栈,此时就会导致线程的栈内存被耗尽。二、示例程序2.1程序源码packagecom.ressmix.jvm;publicclassDemo2{publicstaticlongcounter=0L;publicstaticvoidmain(String[]args){work();}privatestaticvoidwork(){System.out.println("
一、简介本章,我们将通过示例代码演示Metaspace区域是如何发生内存溢出的,并根据内存快照进行分析。我们回顾下Metaspace区发生内存溢出的一个场景:程序不停的动态生成类,然后不停的加载类到Metaspace区域,而且这些动态生成的类必须得是不能被回收的,一旦Metaspace区满了,就会触发FullGC,而由于Metaspace区中的对象无法被回收,此时就触发了Metaspace内存溢出。二、示例程序我们的示例程序采用CGLIB来动态生成类。2.1程序源码publicclassDemo1{publicstaticvoidmain(String[]args){longcount=0L;
一、简介从本章开始,我们将介绍JVM中的内存溢出异常——OutofMemory。我们运行Java程序时,本质是创建了一个JVM进程,然后在里面执行Java字节码。既然是进程,就一定有内存限制,当Java程序使用的内存空间超过限制时,就可能发生内存溢出异常。在JVM中,一共有三种可能出现OOM的地方:方法区(元数据区)、Java栈内存、Java堆内存。本章,我们就来一一看一下各个区域内存溢出的情况。二、方法区溢出在JVM内存模型中,我们介绍过JVM内存模型,JVM中有一块区域叫“方法区”,里面主要保存着从”.class“文件里加载进来的类,包括类的名称、方法信息、字段信息、静态变量、常量以及编译
一、案例背景本章将介绍一个因为程序员同学不懂JVM的GC机制而导致的系统卡死的案例。首先,这个系统上线之后,平时都还算正常,结果有一次大促活动的时候,系统直接卡死不动了。这个时候,我们首先想到的是可不可能是因为频繁FullGC导致StoptheWorld,正常工作线程无法执行。1.1存在问题我们通过jstat进行分析,发现JVM中各个区域的内存使用都正常,新生代对象增长也不快,老年代也才使用了10%左右的空间,永久代也就使用了20%。但是,jstat日志显示,JVM竟然每秒执行一次FullGC,每次都耗时几百毫秒。既然Java堆内存都正常,为啥会频繁FullGC?这是我们立马想到,是不是有开发
一、案例背景本章介绍的案例比较特殊,是由于人为设置JVM参数错误,而导致的JVM性能问题。首先,生产环境有一个新上线的系统,频繁触发FullGC告警。通过GC日志,我们发现日志中有大量以下字样:【FullGC(MetadataGCThreshold)xxxxx,xxxxx】从这里就知道,频繁的FullGC是因为Metadata区域(JDK1.8+)被占满。1.1存在问题Metadata区域,也就是元数据区,一般存放着类信息,为什么会被频繁占满,进而触发FullGC呢?我们通过工具分析元数据区的内存占用情况,发现元数据区域的内存使用波动曲线类似于下面这样:也就是说,元数据区的内存占用不断增加,当
一、案例背景本章将介绍一个因为大对象而导致的频繁GC问题,其本质也是开发童鞋在写程序代码时存在bug,进而导致出现JVM性能问题。首先,这个系统上线之后发现一天的FullGC次数多达几十次,通常来说,我们建议的一个比较良好的JVM性能,应该是FullGC几天才发生一次,或者最多一天发生几次而已。生产环境这个系统部署在2核4G的机器上,JVM参数如下:-Xms=1536M-Xmx=1536M-Xmn=512M-Xss=256K-XX:SurvivorRatio=5-XX:+UseParNewGC-XX:+UseConcMarkSweepGC-XX:CMSInitiatingOccupancyFr
一、案例背景本案例的背景是一个高峰期每秒十万QPS的社交APP,这类APP流量最大的模块就是个人主页模块,会有大量的用户在一个集中的时间段内(比如晚上)频繁访问各种个人主页数据,而且个人主页的数据量通常比较大,在几MB左右:1.1存在问题上述案例,由于高峰期的每秒并发量太高,所以新生代的Eden区会被迅速占满,频发触发YoungGC。而在YoungGC的时候,下一秒的请求又来了,导致很多请求是还没来得及处理完的,导致每次YoungGC的时候很多对象需要存活下来,因此在高峰期经常会出现存活对象太多,导致Survivor区放不下的问题:所以,此时就会有大量对象频繁进入老年代,从而频繁触发老年代的G
一、简介我们通过jstat进行分析,发现FullGC非常频繁,基本上每隔两分钟就会执行一次,而且每次FullGC的时间长达10秒。1.1案例背景系统的JVM内存模型如下,当时给Java堆内存分配了20G,其中年轻代10G,老年代10G:事实上,虽然分配了那么大的内存空间给年轻代和老年代,但是通过jstat分析发现,Eden区大概1分钟就会被占满,然后触发一次YoungGC,而且YoungGC过后有几个G的对象都会存活并进入老年代:这说明系统代码运行时会产生大量对象,经常在1分钟过后就塞满Eden,然后会触发YoungGC,但是由于程序处理极慢,导致大量存活对象Survivor区无法容纳,从而进
一、简介上一章,我们通过jstat命令分析了BI系统中新生代对象的GC情况,也就是YoungGC。本章,我们再来通过jstat命令分析下FullGC的情况。1.1案例背景假设现在生产环境有一套“数据计算系统”,不停地从MySQL等各类数据源提取数据到内存中进行计算,系统是分布式的。每个节点(机器)每分钟执行100次操作(提取数据并计算,每次操作耗时10s),每次操作1万条数据,每条数据大小为1KB左右,那么每次操作的数据大小就是10MB:每台机器的配置是4核8G,JVM分配4G内存,其中新生代1.5G,老年代1.5G。1.2内存使用模型估算每次操作会在Eden区分配10MB对象,以1分钟100
一、简介本章和下一章,我们将通过之前讲过的两个案例,看看如何在生产环境下使用jstat对JVM运行情况进行分析。我们先来回顾下商户BI系统。1.1案例背景假设生产环境有一个商户BI系统,用于商户日常经营数据的分析和报表输出,其大致运行逻辑如下:商户会在业务平台上进行运营,产生各种各样的业务数据;Hadoop、Spark等会对这些业务数据进行计算,然后放入MySQL、HBase之类的存储中;最后,我们的BI系统会把各种存储好的数据暴露给前端,允许前端基于各种搜索条件筛选和展示。系统刚上线时,商户数量只有几万家,生产机器配置是4核8G,新生代分配1.5G,Eden区有1G:1.2内存使用模型估算每
一、简介前面章节,我们已经介绍了如何通过GC日志去分析系统的运行情况。本章,我们将带领大家运行一些JVM调优/检测工具来分析运行中的系统。我们常用的调优/检测工具有三种:jstat、jmap、jhat,我们来一一看下。jstat(JVMstatisticsMonitoring):用于监视JVM运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。jmap(JVMMemoryMap):用于生成heapdump文件,jmap可以查询当前Java堆内存的详细信息,比如当前各个区域使用率(总容量、已使用、未使用)、当前使用的是哪种收集器等。jhat(JVMHea
一、简介上一章,我们已经进行了一次对象晋升的模拟,本章我们将继续结合代码示例做实验,来看看老年代的GC是如何触发的。1.1JVM内存参数我们的示例程序基于JDK1.8,JVM参数如下:-XX:NewSize=10485760-XX:MaxNewSize=10485760-XX:InitialHeapSize=20971520-XX:MaxHeapSize=20971520-XX:SurvivorRatio=8-XX:MaxTenuringThreshold=15-XX:PretenureSizeThreshold=3145728-XX:+UseParNewGC-XX:+UseConcMarkS
一、简介上一章,我们已经进行了一次YoungGC日志的分析,本章我们继续结合代码示例做实验,来看看对象是如何从新生代进入老年代的。我们之前讲过新生代对象晋升到老年代的几种场景:躲过15次GC符合动态年龄判断规则YoungGC后存活对象放不进Survivor大对象直接进入老年代本章,我们通过示例代码模拟最常见的一种场景——YoungGC后存活对象放不进Survivor。1.1JVM内存参数我们的示例程序基于JDK1.8,JVM参数如下:-XX:NewSize=10485760-XX:MaxNewSize=10485760-XX:InitialHeapSize=20971520-XX:MaxHea
一、简介本章,我们将通过示例代码演示YoungGC是如何发生的。同时,我们会讲解如何通过JVM参数去配置打印GC日日,然后通过GC日志分析JVM中的GC到底是如何运行的。1.1JVM内存参数我们的示例程序基于JDK1.8,JVM参数如下:-XX:NewSize=5242880-XX:MaxNewSize=5242880-XX:InitialHeapSize=10485760-XX:MaxHeapSize=10485760-XX:SurvivorRatio=8-XX:PretenureSizeThreshold=10485760-XX:+UseParNewGC-XX:+UseConcMarkSw
一、简介本章将会讲解一个频繁FullGC的案例,示例来自于JVM基础篇中的亿量级计算系统。我们先来回顾下案例。1.1案例背景假设现在生产环境有一套“数据计算系统”,不停地从MySQL等各类数据源提取数据到内存中进行计算,系统是分布式的。每个节点(机器)每分钟执行100次操作(提取数据并计算,每次操作耗时10s),每次操作1万条数据,每条数据大小为1KB左右,每次操作的数据大小就是10MB:每台机器的配置是4核8G,JVM分配4G内存,其中新生代1.5G,老年代1.5G。1.2内存使用模型估算每次操作会在Eden区分配10MB对象,以1分钟100次操作来算,那么Eden区1分钟内就会被占满:二、
一、简介本章,我们还是以案例驱动的方式讲解YoungGC调优,之前在基础篇中,我们曾通过一个亿级访问量的的电商系统讲解过新生代调优。新生代调优最简单的思路就是扩Survivor区,本章示例的调优思路也是一脉相承。1.1案例背景假设生产环境有一个商户BI系统,用于商户日常经营数据的分析和报表输出,其大致运行逻辑如下:商户会在业务平台上进行运营,产生各种各样的业务数据;Hadoop、Spark等会对这些业务数据进行计算,然后放入MySQL、HBase之类的存储中;最后,我们的BI系统会把各种存储好的数据暴露给前端,允许前端基于各种搜索条件筛选和展示。系统刚上线时,商户数量只有几万家,生产机器配置是
一、简介本章,我们先来对系统运行过程中可能会遇到的各种JVM性能问题作个概述,以此为引子,作为后续实战篇的铺垫。JVM性能优化其实就是针对JVM内存分配、参数设置进行优化,目的是减少GC次数,避免对象频繁进入老年代。所以,我们来先来回顾下新生代和老年代的垃圾回收过程,并看下可能会引发的各种JVM性能问题。在正式开始之前,我先给出一份JVM调优模板,这份模板基本上涵盖了JVM调优所需的所有核心参数,后续我们所有的调优也会围绕它展开:-Xms4096M-Xmx4096M-Xmn3072M-Xss1M-XX:MetaspaceSize=256M-XX:MaxMetaspaceSize=256M-XX