Java 自带性能分析工具
命令行工具的功能都很强大,像jmap、jstat、jstack、jps这些,功能和一些收费软件差不多,但是没有GUI看起来就有些费劲。由于使用Windows分析就使用自带的Jmc来用了,只要在命令行输入jmc就可以启动。
jmc示例图
cpu占用排查
由于已经知道cpu占用原因,这里就直接给出错误的示例。如果在服务器上排查需要结合上述命令行工具,具体使用可以参考这里。
- 错误示例
public class TestCpu {
public static void main(String[] args) throws InterruptedException {
TaskThread thread = new TaskThread();
thread.setName("Test Cpu");
thread.start();
// 为了让程序不退出,这里休眠一个较长时间
// sleep 在执行后基本不产生性能损耗
Thread.sleep(1000000);
}
}
class TaskThread extends Thread {
// 由于while循环占用锁导致变量无法观测到变化,可以使用volatile解决,也可以使用并发包下的集合解决
private volatile Queue<Runnable> tasks = new ArrayDeque<>();
public synchronized void add(Runnable runnable) {
tasks.add(runnable);
}
@Override
public void run() {
while (true) {
while (!tasks.isEmpty()) {
Runnable runnable = tasks.remove();
runnable.run();
}
}
}
}
上述代码在运行又占用cpu稳定在15%左右,机器配置为I7 4核8G
-
使用jcm分析性能消耗
飞行记录器
先使用飞行记录器记录一段时间的运行情况
线程
可以看到测试线程基本占用了所有资源(实际服务器上业务逻辑相对复杂,占有率一般不会占这么高)。
但是代码实际只是判断了一个集合是否为空,为什么CPU占用会这么高呢?原因可以参考这里
简单解释就是在Windows上如果一个进程得到了 CPU 时间,除非它自己放弃使用 CPU ,否则将完全霸占 CPU。在Linux上相对好一点,因为时基于时间片算法的。
在知道原因后就比较好办了,解决方法无非就是在不需要执行无谓的循环的时把CPU释放出来。最简单的做法就是在while循环种调用Thread.sleep方法释放CPU的占用。但这样存在频繁的切换线程调度的问题。 -
使用wait和notify阻塞和唤醒线程
wait和notify的原理可以参考这里。对TaskThread修改代码如下,配合这2个方法可以保证在线程空闲时处于一个挂起状态,CPU占用在空闲时也基本为0了。
class TaskThread extends Thread {
private final Deque<Runnable> tasks = new ArrayDeque<>();
public void add(Runnable runnable) {
synchronized (tasks) {
tasks.add(runnable);
if (tasks.pollFirst() == runnable) {
tasks.notify();
}
}
}
@Override
public void run() {
try {
while (true) {
synchronized (tasks) {
if (tasks.isEmpty()) {
tasks.wait();
}
}
while (!tasks.isEmpty()) {
Runnable runnable = tasks.remove();
runnable.run();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
网友评论