四:JVM调优原理与常见异常处理方案
在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调优原理与常见异常处理方案的更多相关文章
- jvm系列(四):jvm调优-命令大全(jps jstat jmap jhat jstack jinfo)
文章同步发布于github博客地址,阅读效果更佳,欢迎品尝 运用jvm自带的命令可以方便的在生产监控和打印堆栈的日志信息帮忙我们来定位问题!虽然jvm调优成熟的工具已经有很多:jconsole.大名鼎 ...
- jvm系列(四):jvm调优-命令篇
运用jvm自带的命令可以方便的在生产监控和打印堆栈的日志信息帮忙我们来定位问题!虽然jvm调优成熟的工具已经有很多:jconsole.大名鼎鼎的VisualVM,IBM的Memory Analyzer ...
- JVM调优原理
JVM堆栈 栈是运行时的单位,而堆是存储的单位. 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据:堆解决的是数据存储的问题,即数据怎么放.放在哪儿. 在Java中一个线程就会相应有一个线程栈 ...
- JVM调优——之CMS 常见参数解析
最近在学习使用CMS这个GC,这里记录下常用的参数. 1. UseCMSCompactAtFullCollection 与 CMSFullGCsBeforeCompaction 有一点需要注意的是:C ...
- jvm系列(七):jvm调优-工具篇
16年的时候花了一些时间整理了一些关于jvm的介绍文章,到现在回顾起来还是一些还没有补充全面,其中就包括如何利用工具来监控调优前后的性能变化.工具做为图形化界面来展示更能直观的发现问题,另一方面一些耗 ...
- JVM参数设置及条调优原理
http://unixboy.iteye.com/blog/174173/ 堆大小设置JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟 ...
- JVM调优(四)——tomcat远程debug
JVM调优(四)--tomcat远程debug tomcat远程debug jdwp协议 使用步骤 登录远程服务器,进入tomcat目录,并打开文件: //tomcat/bin/startup.sh ...
- JVM调优总结(四)-分代垃圾回收详述
为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对象, ...
- JVM调优总结 + jstat 分析(转)
[转] JVM调优总结 + jstat 分析 JVM调优总结 + jstat 分析 jstat -gccause pid 1 每格1毫秒输出结果jstat -gccause pid 2000 每格2秒 ...
随机推荐
- BZOJ 2121: 字符串游戏 区间DP + 思维
Description BX正在进行一个字符串游戏,他手上有一个字符串L,以及其他一些字符串的集合S,然后他可以进行以下操作:对 于一个在集合S中的字符串p,如果p在L中出现,BX就可以选择是否将其删 ...
- 2018山东省赛 H Dominoes ( 搜索 )
题目链接 题意 : 给出一个 n * m 的矩阵,用规格 1 * 2 的多米诺去填充,题目数据保证最后只有一个格子是空白的(即没有被多米诺骨牌覆盖),问你现在通过移动多米诺能够产生多少种不同的状态(空 ...
- luogu P1063 能量项链 x
P1063 能量项链 题目描述 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子, ...
- es之路由:进一步提高Elasticsearch的检索效率(适用大规模数据集)
1:一条数据是如何落地到对应的shard上的 当索引一个文档的时候,文档会被存储到一个主分片中. Elasticsearch 如何知道一个文档应该存放到哪个分片中呢? 首先这肯定不会是随机的,否则将来 ...
- 记一次CTF实验吧的代码审计
0X01 0X01 Burp抓包找到hint 访问地址看源码 <?php $info = ""; $req = []; $flag="xxxxxxxxxx" ...
- 利用IKVM在C#中调Java程序(总结+案例)
IKVM.NET是一个针对Mono和微软.net框架的java实现,其设计目的是在.NET平台上运行java程序.本文将比较详细的介绍这个工具的原理.使用入门(如何java应用转换为.NET应用.), ...
- SRCNN 卷积神经网络
2019-05-19 从GitHub下载了代码(这里) 代码量虽然不多,但是第一次学,花了时间还是挺多的.根据代码有跑出结果(基本没有改),但是对于数据集的处理还是看的很懵逼,主要是作者的实现都是用类 ...
- Linux添加用户到sudoers组
切换用户至rootvim /etc/sudoers 找到root ALL=(ALL) ALL,在下方新增 stack ALL=(ALL) NOPASSWD: ALL w ...
- XDebug安装配置教程
笔者的开发环境如下:Windows8.1+Apache+PhpStorm+XDebug+Firefox(XDebug helper 1.4.3插件). 转载http://www.jb51.net/ar ...
- QbztDay1游记
qbzt爆零记Day1 T1 我们为什么不把这个机器人直接报废掉呢? 素质题目 其实这就是个膜你模拟 坑点: 如果走到了水池并掉进去了,位置并不是水池前面的格子,而是执行这条指令之前的位置 来自老师的 ...