在jvm调优之前,我们必须先了解jvm的内存模型与GC回收机制,这些在我前面的文章里面有介绍!接下来我们通过一个案例来调整jvm性能。

一测试案例:

  1.1 编写demo

import java.text.DecimalFormat;
/**
-XX:+PrintGC 打印GC日志
-XX:+PrintGCDetails 打印详细的GC日志
file.encoding 文件编码
-XX:MaxTenuringThreshold 对象年龄,默认15次之后较大几率放进入老年代,为0则不进入s0 s1区,直接进老年代 -XX:+UseSerialGC 串行收集器
-XX:+UseParallelOldGC 并行收集器
-XX:+UseParallelGC 合并回收
-XX:ParallelGCThreads 并行收集器线程数量 -Xms 堆初始值
-Xmx 堆最大可用值
-XX:SurvivorRatio 新生代中eden空间和from(s0),to(s1)空间的比例
-XX:NewRatio 新生代与老年代的比例
-Xss 栈的大小(栈的深度) -XX:MetaspaceSize 元空间初始值
-XX:MaxMetaspaceSize 元空间最大值
一般生产环境都肯定会指定这两个参数
-XX:+HeapDumpOnOutOfMemoryError:内存溢出生成快照文件
-XX:HeapDumpPath= 存放快照路径
*/
public class JVMDemo {
public static void main(String[] args) throws InterruptedException {
// 最大内存
long maxMemory = Runtime.getRuntime().maxMemory();
System.out.println("最大堆内存为: "+format(maxMemory)+"MB");
// 已经使用内存
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("已使用堆内存: "+format(totalMemory)+"MB");
// 当前剩余内存
long freeMemory = Runtime.getRuntime().freeMemory();
System.out.println("剩余堆内存为: "+format(freeMemory)+"MB"); byte[] b1 = new byte[4 * 1024 * 1024];
System.out.println("-----分配了4m堆内存-----"); // 当前剩余内存
long freeMemory2 = Runtime.getRuntime().freeMemory();
System.out.println("剩余堆内存为: "+format(freeMemory2)+"MB");
}
/**
* 将堆内存单位格式化成 MB
*/
static private String format(long maxMemory) {
float num = (float) maxMemory / (1024 * 1024);
DecimalFormat df = new DecimalFormat("0.00");// 格式化小数
String s = df.format(num);// 返回的是String类型
return s;
}
}

  1.2 配置参数,打印jvm信息:  右键 --> Run As --> Run Configurations... --> Arguments --> VM arguments 输入配置信息  -XX:+PrintGCDetails -XX:+UseSerialGC

  

  1.3 运行java代码, 查看jvm信息

// 这些信息与电脑配置参数有关,我们的具体数据可能不一样,但是内存模型数据比例是一样的
最大堆内存为: 1890.81MB // 大约1900MB
已使用堆内存: 119.88MB // 已用120MB
剩余堆内存为: 117.89MB // 剩余117MB
-----分配了4m堆内存-----
剩余堆内存为: 113.89MB // 用了4mb后还剩113MB
Heap
def new generation total 38080K, used 6805K [0x0000000085c00000, 0x0000000088550000, 0x00000000ae800000)
// 可以看出新生代中默认 eden区 from区 to区比例为 33856:4224:4224 即 8:1:1
eden space 33856K, 20% used [0x0000000085c00000, 0x00000000862a57a8, 0x0000000087d10000)
from space 4224K, 0% used [0x0000000087d10000, 0x0000000087d10000, 0x0000000088130000)
to space 4224K, 0% used [0x0000000088130000, 0x0000000088130000, 0x0000000088550000)
// 老年代和新生代默认比例为 84672:(33856+4224+4224) 即2:1
tenured generation total 84672K, used 0K [0x00000000ae800000, 0x00000000b3ab0000, 0x0000000100000000)
the space 84672K, 0% used [0x00000000ae800000, 0x00000000ae800000, 0x00000000ae800200, 0x00000000b3ab0000)
// 元空间
Metaspace used 3652K, capacity 4600K, committed 4864K, reserved 1056768K
class space used 410K, capacity 428K, committed 512K, reserved 1048576K

二性能调优:

这个时候我们对jvm堆内存有了一个大致的认识,我们开始正式的性能调优。
案例:通过jmeter压力测试工具来访问一个网站,测试系统的吞吐量。
 2.1.1:双击启动 jmeter/bin/jmeter.bat 后台不要关闭
 2.1.2: 添加线程组 (用50个线程去发起5w个请求)

   

 2.1.3: 添加http请求 指定服务器  ip   port   url。

   

 2.1.4: 添加聚合报告

  

 2.1.5:查看测试结果 (只需要关注总请求量  和  吞吐量)

  

2.2:串行收集器吞吐量调优

【在tomcat里设置如下参数配置】
打印详细日志 最大堆和初始堆为32mb
内存溢出时生成快照文件
串行回收
非堆区初始大小32mb -XX:+PrintGCDetails -Xmx32M -Xms32M
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=D:\app\jvmlog\app.dump
-XX:+UseSerialGC
-XX:PermSize=32M (1.8以前的永久区写法)

2.3:并行收集器吞吐量调优

【在tomcat设置如下参数】
打印详细日志 最大堆和初始堆为1000mb
内存溢出时生成快照文件
并行合并回收
并行收集器线程数量为8个
非堆区初始大小500mb -XX:+PrintGCDetails -Xmx1000M -Xms1000M
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=D:\app\jvmlog\app.dump
-XX:+UseParallelGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=8 -XX:PermSize=500M (1.8以前的永久区写法)

对比可以看出:第一次调优由于堆内存太小而经常触发GC,且由于是串行清理,吞吐量仅仅只有131;而第二次加大了堆内存几乎不触发GC清理,且由于是并行清理所以吞吐量提升到了2482。

调优总结:  

  1.将初始的堆大小与最大堆大小相等,来垃圾回收次数从而提高效率。 
  2.根据自身服务器性能,将堆内存最大化,以此提高吞吐量。
  3.将新生代或老年代的比例设置为1/2 或者 1/3,让GC尽量去新生代去回收。
  4.合理的使用并行收集器。数量和CPU核数相同,例如8核的CPU就设置为8,既提高了工作效率又避免了上下的频繁切换。

在windows中设置JVM参数

修改 tomcat/bin/catalina.bat 文件
set JAVA_OPTS=-Dfile.encoding=UTF-8 -server -XX:+PrintGCDetails -Xms900m -Xmx900m -XX:SurvivorRatio=8 -XX:NewRatio=2 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\app\jvmlog\app.dump -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=8 -XX:MaxMetaspaceSize=128m

在linux中设置JVM参数

修改 tomcat/bin/catalina.sh 文件
JAVA_OPTS="-Dfile.encoding=UTF-8 -server
-XX:+PrintGCDetails
-Xms1000m -Xmx1000m
-XX:SurvivorRatio=8 -XX:NewRatio=2
-XX:MaxMetaspaceSize=200m
-XX:+HeapDumpOnOutOfMemoryError
       -XX:HeapDumpPath=/app/jvmlog/app.dump
-XX:+UseParallelGC -XX:+UseParallelOldGC
-XX:ParallelGCThreads=8"

 

 生产环境定位问题:

  1. 我们启动项目的时候,可以设置jvm参数使内存溢出时生成dump快照文件(事先创建好输出的目录文件夹 /log)

#!/bin/sh
#指定项目路径
export project_path=/usr/local/wulei/demo
#指定jar包名称
export JAR_NAME=app.jar
#指定java环境变量
export JAVA_HOME=/usr/local/wulei/jdk8

#清空之前的启动日志
echo "" > $project_path/nohup.out

echo -e "\033[47;34;5m ======= 开始启动项目..... ======= \033[0m"
#启动脚本
nohup $JAVA_HOME/bin/java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/log/app.dump -Xms20m -Xmx20m -jar $project_path/$JAR_NAME 2>&1 &

#打印启动日志
tail -f $project_path/nohup.out

  2. 可以看到OOM异常后生成了dump文件,我们可以通过jdk的jvisualvm.exe工具来分析问题。

  

  3. 根据stack堆栈调用轨迹,可以查看到哪一块代码在死锁,等待,修改代码。点进去查看详细信息能看到具体是  Web类的第16行的数组对象造成的。

   

====================================================================

什么是内存泄漏及如何避免

    内存泄漏即:对象可达但不可用,程序不需要用某个对象,但另一个正在使用的对象却持有它的引用,导致无法回收停留在堆内中。

     防止内存泄露:

      1.尽早释放无用对象的引用, 将不需要使用的对象设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄漏。

      2.程序进行字符串处理时,尽量避免使用String,而应该使用StringBuffer。

       3.尽量少用静态变量

      4.尽量运用对象池技术以提高系统性能

Jvm常见异常处理方案

  内存泄漏: java.lang.OutOfMemory Error:Java heap space

    解决思路:  1. 先查看是不是内存泄漏,如果是通过GC Root的路径来排查

            2. 查看堆内存是否有对象没释放。

            3. 加大物理内存 –Xms, -Xmx,最好-Xms = -Xmx,减少内存扩展的开销。

控制参数:  -Xms (starting 堆的起始大小)         -Xmx (max 堆的最大大小)         -Xmn (new 堆的新生代大小)

  内存溢出: java.OutOfMemory Error:PermGen space

解决思路:  增加参数:-XX:PrintGCDetails,-XX:+PrintGCTimeStamps和-XX:+PrintGCDateStamps 

  线程请求的栈深度大于虚拟机所允许的最大深度java.lang.StackOverflow Error

    解决思路: 可以将栈的深度,理解为数组的长度。

    控制参数:  1. 加大-Xss(每个线程的堆栈大小)参数。

           2. 更换64位虚拟机。

           3. 减少线程。

           4. 减少最大堆(Xmx)。

         java.lang.OutOfMemory Error,有allocate、Native字样

    解决思路:  加大本地内存-MaxDirectMemorySize如不指定则与-Xmx一致。

    以centos6.8 ,100G的内存 jdk1.7为例,参考配置如下:

      JAVA_OPTS="-Xms8g -Xmx8g

            -XX:ParallelGCThreads=8

            -XX:PermSize=2g

            -XX:MaxPermSize=4g

            -Xss512k -Xmn6g

            -XX:-DisableExplicitGC

            -XX:+UseCompressedOops

            -XX:+UseConcMarkSweepGC

            -XX:+CMSParallelRemarkEnabled"

   CATALINA_OPTS="-Xms8g -Xmx8g

            -XX:ParallelGCThreads=8

            -XX:PermSize=2g

            -XX:MaxPermSize=4g

            -Xss512k -Xmn6g

            -XX:-DisableExplicitGC

            -XX:+UseCompressedOops

            -XX:+UseConcMarkSweepGC

            -XX:+CMSParallelRemarkEnabled"

四:JVM调优原理与常见异常处理方案的更多相关文章

  1. jvm系列(四):jvm调优-命令大全(jps jstat jmap jhat jstack jinfo)

    文章同步发布于github博客地址,阅读效果更佳,欢迎品尝 运用jvm自带的命令可以方便的在生产监控和打印堆栈的日志信息帮忙我们来定位问题!虽然jvm调优成熟的工具已经有很多:jconsole.大名鼎 ...

  2. jvm系列(四):jvm调优-命令篇

    运用jvm自带的命令可以方便的在生产监控和打印堆栈的日志信息帮忙我们来定位问题!虽然jvm调优成熟的工具已经有很多:jconsole.大名鼎鼎的VisualVM,IBM的Memory Analyzer ...

  3. JVM调优原理

    JVM堆栈 栈是运行时的单位,而堆是存储的单位. 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据:堆解决的是数据存储的问题,即数据怎么放.放在哪儿. 在Java中一个线程就会相应有一个线程栈 ...

  4. JVM调优——之CMS 常见参数解析

    最近在学习使用CMS这个GC,这里记录下常用的参数. 1. UseCMSCompactAtFullCollection 与 CMSFullGCsBeforeCompaction 有一点需要注意的是:C ...

  5. jvm系列(七):jvm调优-工具篇

    16年的时候花了一些时间整理了一些关于jvm的介绍文章,到现在回顾起来还是一些还没有补充全面,其中就包括如何利用工具来监控调优前后的性能变化.工具做为图形化界面来展示更能直观的发现问题,另一方面一些耗 ...

  6. JVM参数设置及条调优原理

    http://unixboy.iteye.com/blog/174173/     堆大小设置JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟 ...

  7. JVM调优(四)——tomcat远程debug

    JVM调优(四)--tomcat远程debug tomcat远程debug jdwp协议 使用步骤 登录远程服务器,进入tomcat目录,并打开文件: //tomcat/bin/startup.sh ...

  8. JVM调优总结(四)-分代垃圾回收详述

    为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对象, ...

  9. JVM调优总结 + jstat 分析(转)

    [转] JVM调优总结 + jstat 分析 JVM调优总结 + jstat 分析 jstat -gccause pid 1 每格1毫秒输出结果jstat -gccause pid 2000 每格2秒 ...

随机推荐

  1. #417 Div2 Problem B Sagheer, the Hausmeister (DFS && 枚举)

    题目链接:http://codeforces.com/contest/812/problem/B 题意 : 给出一个 n (1 ≤ n ≤ 15)层的教学楼, 每一层楼包含 m (1 ≤ m ≤ 10 ...

  2. Luogu P2678 跳石头

    题目链接:Click here Solution: 最小值最大,显然二分,二分出mid后贪心去除石头,判断m次内是否可行即可 Code: #include<bits/stdc++.h> # ...

  3. luogu P1077 摆花 x

    P1077 摆花 题目描述 小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆.通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1到n标号.为了在门口展出更多种花,规定第i种花不能 ...

  4. Android处理未捕获的异常(应用全局异常)

    public class CrashHandler implements UncaughtExceptionHandler { private static CrashHandler instance ...

  5. 二分类算法的评价指标:准确率、精准率、召回率、混淆矩阵、AUC

    评价指标是针对同样的数据,输入不同的算法,或者输入相同的算法但参数不同而给出这个算法或者参数好坏的定量指标. 以下为了方便讲解,都以二分类问题为前提进行介绍,其实多分类问题下这些概念都可以得到推广. ...

  6. Linux小记 -- apt-get install build-essential和yum groupinstall "Development Tools"

    Ubuntu的apt install build-essential 作用:配置Debian系统编译环境,就是下载安装支持编译Debian包的依赖/包,比如gcc等. 直接执行 #apt instal ...

  7. CentOS7服务器配置

    CentOS7服务器配置 1.更换yum软件源 下载阿里源 cd /etc/yum.repos.d sudo wget -nc http://mirrors.aliyun.com/repo/Cento ...

  8. jQuery file upload callback options

    autoUpload By default, files added to the widget are uploaded as soon as the user clicks on the star ...

  9. ACL 2019 分析

    ACL 2019 分析 word embedding 22篇! Towards Unsupervised Text Classification Leveraging Experts and Word ...

  10. ES6正则拓展

    字符串的正则方法 字符串对象共有 4 个方法,可以使用正则表达式:match().replace().search()和split(). ES6 将这 4 个方法,在语言内部全部调用RegExp的实例 ...