平常你是怎么对Java服务进行调优的
图 1.Java 性能优化分层模型
OS 诊断
图 2.top 命令示例
图 3.vmstat 命令示例
1)时间片用完,CPU 正常调度下一个任务;
2)被其它优先级更高的任务抢占;
3)执行任务碰到 I/O 阻塞,挂起当前任务,切换到下一个任务;
4)用户代码主动挂起当前任务让出 CPU;
5)多任务抢占资源,由于没有抢到被挂起;
6)硬件中断。
for(Category c =
this; c !=
null; c=c.parent) {
// Protected against simultaneous call to addAppender, removeAppender,…
synchronized(c) {
if (c.aai !=
null) {
write += c.aai.appendLoopAppenders(
event);
}
…
}
}
图 4.Linux 性能观测工具
图 5. 通过 top –H -p 查看运行时间较长 Java 线程
图 6.jstack 查看线程堆栈
图 7. 通过 JProfiler 进行内存分析
图 8.jstat 命令示例
图 9.MAT 示例
图 10. 常用 GC 参数
JVM 调优:GC 之痛
清单 2.DGC 守护线程源代码
private
static
class Daemon extends Thread {
public void run() {
for (;;) {
//…
long d = maxObjectInspectionAge();
if (d >= l) {
System.gc();
d =
0;
}
//…
}
}
}
定位问题后解决起来就比较容易了。一种是通过增加-XX:+DisableExplicitGC 参数,直接禁用系统 GC 的显示调用,但对使用 NIO 的系统,会有堆外内存溢出的风险。
另一种方式是通过调大 -Dsun.rmi.dgc.server.gcInterval 和-Dsun.rmi.dgc.client.gcInterval 参数,增加 Full GC 间隔,同时增加参数-XX:+ExplicitGCInvokesConcurrent,将一次完全 Stop-The-World 的 Full GC 调整为一次并发 GC 周期,减少应用暂停时间,同时对 NIO 应用也不会造成影响。
从图 11 可知,调整之后的 Full GC 次数 在 3 月之后明显减少。
图 11.Full GC 监控统计
应用层调优:嗅到代码的坏味道
图 12. 通过 MAT 分析堆栈现场
清单 3. 网站数据懒加载代码
private
static Map<Long, UnionDomain> domainMap =
new HashMap<Long, UnionDomain>();
private boolean isResetDomains() {
if (CollectionUtils.isEmpty(domainMap)) {
// 从远端 http 接口获取网站详情
List<UnionDomain> newDomains = unionDomainHttpClient
.queryAllUnionDomain();
if (CollectionUtils.isEmpty(domainMap)) {
domainMap =
new HashMap<Long, UnionDomain>();
for (UnionDomain domain : newDomains) {
if (domain !=
null) {
domainMap.put(domain.getSubdomainId(), domain);
}
}
}
return
true;
}
return
false;
}
(1)采用 ConcurrentHashMap 或者同步块的方式解决上述并发问题;
(2)在系统启动前完成网站缓存加载,去除懒加载等;
(3)采用分布式缓存替换本地缓存等。
(1)代码可读性差,无基本编程规范;
(2)对象生成过多或生成大对象,内存泄露等;
(3)IO 流操作过多,或者忘记关闭;
(4)数据库操作过多,事务过长;
(5)同步使用的场景错误;
(6)循环迭代耗时操作等。
数据库层调优:死锁噩梦
图 13. 死锁语句
结语
提问:请问下博文中,“如果一旦使用二级索引进行加锁后,会尝试将主键索引进行加锁。” 这句话怎么理解,能否细说一下?
回答:非常感谢您的提问,具体来说,这是由于innodb引擎的二级索引的构造原理决定的。
1)InnoDB的主键是采用聚簇索引存储,使用B+Tree作为索引结构,其叶子节点存储的是索引值和数据本身(与MyISAM不同)
2)InnoDB的二级索引不使用聚簇索引,叶子节点存储的是Key字段+主键值。因此通过二级索引查询首先查到的是主键值,然后再根据主键索引找到相应的数据块。
1)在RC隔离级别下,对命中的二级索引和聚簇索引(即主键索引)均加recordlock(行锁);
2)在RR隔离级别下,会在命中的二级索引上加next-keylock(即索引记录加recordlock(行锁)+索引记录两边的间隙加上间隙所(Gaplock)),对应的聚簇索引上加recordlock(行锁)。
index‘idx_groupdomain_accountid’
spaceid 5726 page no 8658的RECORDLOCK(二级索引)
PRIMIARY 在
spaceid 5726 page no 12990的RECORDLOCK(主键索引)
