背景
在我们开发阶段无法用到,但是到了生产环境,程序出问题不可能每次都重启服务,这个时候就需要我们对程序进行监控以及性能调优
- 背景
Jvm
- jps
- jstat
- jstatd
- jmc
一、Jvm参数类型
- 标准类型 (基本不变)
- -help
- -server client
- -version -showversion
- X参数类型 (非标准化参数)
- -Xint 解释执行
- -Xcomp 第一次使用就编译成本地代码
- -Xmixed 混合模式,JVM自己决定是否便以本地代码
- XX参数类型 (不稳定、主要用在调优)
- -XX:[+-]
表示启用或者禁用name属性 - -XX:
= 例如 -Xmx
- -XX:[+-]
Jstat查看JVM统计信息
Jstat pid
- 类装载
- 例如:jstat -class 52122 1000 10
- 垃圾收集
- 例如:jstat -gc 52122 1000 10
- JIT编译 (编译的一些信息)
- 例如:jstat comoiler pid
Jvm内存结构
二、内存溢出
2.1、种类
- 堆内存溢出
实现原理(一直循环向全局集合中存值)
List<User> userList = Lists.newArrayList();
@GetMapping("/heap")
public void heap(){
int i = 0;
while (true) {
userList.add(new User(i++, UUID.randomUUID().toString()));
}
}
- 非堆内存溢出
2.2、内存溢出自动导出
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./
2.3、jmap命令手动导出
2.4、MAT查看内存泄漏
- 对象数量
- 对象内存
三、使用JVisualVM可视化工具监控本地或者远程的java进程
详细请见第六篇Jvm调优
注意
其中连接远程只是添加主机,但是监控某一个进程就需要修改tomcat的Catalina.sh文件
四、基于Btrace监控调优
- 运行的程序不需要重启情况下注入最终代码动态修改字解码
- JavaComplierApi、JVMTI、Agent、Instrumentation+ASM
4.1、安装
- 新建环境变量 BTRACE_HOME 路径
- 添加Path:%BTRACE_HOME%\bin
4.2、使用
- 编写一个Btrace类
- 进入该目录输入指令:btrace pid 文件名.java
4.3、拦截方法
- 普通方法@OnMethod(class="", method="")
- 构造函数@OnMethod(class="", method="
")
4.4、拦截时机
- Kind.ENTRY:入口、默认
- Kind.RETURN:返回
- Kind.THROW:异常
- Kind.Line:行
注意
- 只能本地运行
- 生产环境可以使用、但修改后字节码不会被还原
五、Tomcat性能调优
5.1、步骤
- 启动添加脚本
-agentlib:jdwp=transport=dt_socket,adress=54321,server=y,suspend=n
- IDEA 或者Eclipse添加一个remote启动方式
5.2、tomcat-manager监控
- 文档:docs/manager-howto.html
- 步骤:
- 1、conf/tomcat-users.xml 添加用户
- 2、新建conf/Catalina/localhost/manager.xml配置允许远程连接
- 3、重启
- 1、conf/tomcat-users.xml 添加用户
5.3、PSI监控
5.4、tomcat优化
- 内存优化
- 线程优化(doc/config/http.html)
- maxConneions
- acceptCount
- maxThreads
- minSpareThreads
- 配置优化
- autoDeploy设置为true(文档:docs/config/host.html)
- enableLookups(文档:docs/config/http.html)
- reloadable生产设置false,可以查看class和静态文件变化
- protocol( 文档:conf/server.xml)
- session:(jsp页面,应该禁用session)
六、nginx优化
6.1、安装
6.2、原生连接信息监控连接信息(ngx_http_stub_status)
location = /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
- 本机执行:weget http://127.0.0.1/nginx_status
- 保存成文件
6.3、ngxto监控请求信息
6.4、nginx-rrd图形化监控
6.5、nginx优化
- 增加工作线程数和并发连接数
- 启用长连接
- 启用缓存、压缩
(提高性能)
- 操作系统优化
- 关于tcp连接协议的优化
- 进程打开文件限制的设定
- 其他优化
- 关于tcp连接协议的优化
七、JVM调优
7.1、JVM内存结构
- 运行时数据区
- 程序计数器
- 虚拟机栈:线程的堆栈、内存的模型
- 堆:区间共享,存放对象实例
- 方法区(非堆):区间共享,存放类常量编译后代码等数据
- 产量词:存放编译期生成的各种字面量和符号应用
- 本地方法栈
-Xms -Xmx 最大内存 最小内存
-XX:NewSize -XX:MaxNewSize 新生代大小 新生代最大大小
-XX:NewRatio -XX:SurvivorRatio 比例
-XX:MetaspaceSize -XX:MaxMetaspaceSize Metaspace区大小(包含下面四个大小)
-XX:+UseCompressedClassPointers 是否启用压缩类指针启用CCS,最大1G大小
-XX:CompressedClassSpaceSize 压缩类空间大小
-XX:InitialCodeCacheSize CodeCache大小
-XX:ReservedCodeCacheSize CodeCache最大大小
7.2、垃圾回收算法
创建对象后,一直被程序保持有占用,自动垃圾回收无法回收
-
思想:枚举根节点,做可达性分析
-
根节点:类加载、Thread、虚拟机本地变量表、static成员、常量引用、本地方法栈的变量等等
-
标记清除
- 算法:分为标记和清除两个阶段:首先标记所有需要回收的对象,再标记完成后统一回收所有
- 缺点:效率不高,易产生碎片,碎片太多会导致没内存,提前GC
-
标记整理
- 算法:和标记清除一样,但后续步骤不是对对象直接清除,而是将存活的对象向一端移动,然后直接清理边界外的内存
- 缺点:没有内存碎片,但比较耗时
-
复制算法
- 算法:他可将内存划分大小相同两块,每次只使用其中一块,当一块用完了,会将存活的对象复制到另一块上,然后把已使用的内存一次清理掉
- 优缺点:实现简单,当空间利用效率低
-
分带垃圾回收
- Young区使用复制算法
- Old区使用标记清除和标记整理
-
对象分配
- 对象优先在Eden区分配
- 大对象直接进入老年代:-XX:PretenureSizeThreshold
- 长期存活的对象进入老年代:- XX:MaxTenuringThreshold
-XX:+PrintTenuringDistribution -XX:TargetSurcicorRatio
7.3、垃圾收集器
种类
- 串行收集器Serial:Serial、Serial Old。单线程收集器
-
- XX:+UseSerialGC -XX:+UseSerialOldGC
-
- 并行收集器Parallel:Paraller Scavenge、Parallel Old、吞吐量
指多条垃圾及线程并行工作,但此时用户线程仍然处于等待状态。适合科技计算、后台处理等弱交互场景- 吞吐量优先
-
- XX:+UseParallelGC -XX:+UseParallelOldGC
- Server模式下默认收集器
- 并发收集器:Concurrent:CMS、G1、停顿时间
指用户线程和垃圾收集线程同时执行(但不一定是并行,也可能会交替执行),垃圾收集线程在执行时候不会停顿用户程序的运行,适合对响应时间有要求的场景,如web- 响应时间优先
- CMS:XX:UseConcMarkSweepGC -XX:+UseParNewGC
- G1:-XX:+UseG1GC
停顿时间VS吞吐量
- 停顿时间:垃圾收集器做垃圾回收时候中断应用执行的时间
-XX:MacGCPauseMillis - 吞吐量:垃圾回收的时间和应用时间的占比
-XX:GCTimeRatio=,垃圾收集时间占比1/1+n
垃圾收集器搭配
如何选择收集器
- 优先调整堆大小让服务器自己选择
- 如果内存小于100M,使用串行收集器
- 如果是单核,没有停顿时间要求,串行或者JVM自己选择
- 如果允许停顿超过1s,选择并行或者Jvm自己选择
- 如果响应时间重要,并且不能超过1S,使用并发收集器
并行收集器Parallel Collector
- -XX:useParallelGC 手动开启,Server默认开启
- -XX:ParallelGCThreads=
多少个GC线程
CPC > 8 N=5/8
CPU < 8 N=CPU -
- XX:MaxGCPauseMillis=
- XX:MaxGCPauseMillis=
- -XX:GCTimeRatio=
- -XMx
动态内存调整
- -XX:YoungGenerationSizeIncrement=
- -XX:TenuredGenerationSizeIncrement=
- -XX:AdaptiveSizeDecrementScaleFactor=
CMS垃圾收集过程
1、CMS initial mark:初始标记Root,STW
2、CMS concurrent mark:并发标记
3、CMS-concurrent-preclean:并发预清理
4、CMS remark:重新标记,STW
5、CMS concurrent sweep:并发清除
6、CMS concurrent reset:并发重置
- 缺点
- CPU敏感(CPU占用)
- 浮动垃圾
- 空间碎片
- 相关参数
- -XX:ConcGCThreads:并发GC线程数
- -XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩
- -XX:CMSFullGCsBeforeCompaction:多少次FullGC后压缩一次
- -XX:CMSInitiatingOccupancyFraction:处罚FullGC
- -XX:+UseCMSInitiatingOccupancyOnly:是否动态调
- -XX:+ CMSScavengeBeforeRemark:FullGC之前先做YGC
- -XX:+CMSClassUnloadingEnabled:启用回收perm区
G1垃圾收集
新生代和老生代的收集器
YoungGC
- 新对象进入Eden区
- 存活对象拷贝到Survivor区
- 存活时间达到年龄阀值,就存到Old区
7.4 可视化GC理智分析工具
- 在线工具:http//gceasy.io/
- GCViewer
7.7、GC调优步骤
根据日志里面信息调参数
- 打印GC日志
- 根据日志得到关键性性能指标
- 分析GC原因,调优JVM参数
实战GC调优
paralleGC调优
- 除非确定,否则不要设置最大堆内存
- 优先设置吞吐量
- 如果吞吐量不达标,调大最大内存,不能让OS设置Swap,如果还不达标,则降低目标
- 吞吐量能达标,GC时间过长,设置停顿时间目标
G1 GC调优
关于MixGC调优
八、字节码指令
i和i区别
- 区别:
先引用再+1,++i先+1再赋值引用
字符串拼接+
每次循环时候,每次都需要new Builder
Try-Finally
会将变量拷贝两份,一个用于返回,一个用于finally赋值
public static String f1() {
String str = "hello"
try {
return str;
} finally {
str = "imooc"
}
}
九、常用代码优化
- 尽量重复使用对象,不要循环创建对象
- 容器初始化指定长度
- ArrayList随机遍历快,LinkList添加删除快
- 集合遍历尽量减少重复计算
- 遍历Map使用Entry
- 大数组拷贝尽量使用system.arraycopy
- 尽量使用基本类型,而不是包装类型
- 不要手动system.gc
- 及时消除过期对象的引用,防止内存泄漏
- 尽量使用局部变量,减少变量的作用域
- 尽可能用非同步的容器ArrayList和Vector(内部加锁)
- 尽量减少同步作用范围,synchronized方法块VS代码块、
- 尽量延迟加载
// 单例,由JVM类加载机制加载,不引用类不会加载,线程安全
- 少用反射,改成第一次使用反射,后面使用缓存取
- 尽量使用连接池、线程池、对象池、缓存
- 及时释放资源、IO流、socket、数据库连接
- 慎用异常,不要用抛异常来表示正常业务逻辑
- String尽量少用正则表达式。例:replace VS replaceAll
- 日志输出注意不同的级别
- 日志参数拼接使用占位符