JConsole:Java监视与管理控制台

JConsole:Java监视与管理控制台,第1张

JConsole:Java监视与管理控制台

JConsole是一款基于JMX的可视化监视、管理工具。它的主要功能是通过JMX的MBean对系统进 行信息收集和参数动态调整。
点击JDK/bin 目录下面的jconsole.exe 即可启动

内存监控

“内存”页面相当于可视化的jstat命令,用于监视受收集器管理的虚拟机内存(Java堆和永久代)的变化趋势。我们通过运行下面代码来查看下监视功能。运行时设置的虚拟机参数为

-Xms100m  -Xmx100m   -XX:+UseSerialGC

这段代码的作用是以64KB/50毫秒的速度往Java堆中填充数据,一共填充1000次,使用JConsole的“内存”页签进行监视,观察曲线和柱状图指示图的变化。

public class OOMObjectTest {
    
    static class OOMObject {
        public byte[] placeholder = new byte[64 * 1024];
    }

    public static void fillHeap(int num) throws InterruptedException {
        Thread.sleep(20000); //先运行程序,在执行监控
        List list = new ArrayList();
        for (int i = 0; i < num; i++) {
            // 稍作延时,令监视曲线的变化更加明显
            Thread.sleep(50);
            list.add(new OOMObject());
        }
        System.gc();
    }

    public static void main(String[] args) throws Exception {
        fillHeap(1000);
        while (true) {
            //让其一直运行着
        }
    }
}



程序运行后,在“内存”页签中能够看到内存池Eden区的运行趋势呈现折线状,以下图所示。而监视范围扩大至整个堆后,会发现曲线是一条向上增加的平滑曲线。而且从柱状图能够看出,在1000次循环执行结束,运行了System.gc()后,虽然整个新生代Eden和Survivor区都基本被清空了,可是表明老年代的柱状图仍然保持峰值状态,说明被填充进堆中的数据在System.gc()方法执行以后仍然存活。

线程监控

如果说JConsole的“内存”页签相当于可视化的jstat命令的话,那“线程”页签的功能就相当于可视化 的jstack命令了,遇到线程停顿的时候可以使用这个页签的功能进行分析。前面讲解jstack命令时提到 线程长时间停顿的主要原因有等待外部资源(数据库连接、网络资源、设备资源等)、死循环、锁等 待等。

 
    public static void createBusyThread() {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {

                }

            }
        }, "testBusyThread");
        thread.start();
    }

    
    public static void createLockThread(final Object lock) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "testLockThread");
        thread.start();
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        br.readLine();
        createBusyThread();
        br.readLine();
        Object obj = new Object();
        createLockThread(obj);
    }

程序运行后,首先在“线程”页签中选择main线程,如下图所示。堆栈追踪显示BufferedReader在readBytes方法中等待System.in的键盘输入,这时线程为Runnable状态,Runnable状态的线程会被分配运行时间,但readBytes方法检查到流没有更新时会立刻归还执行令牌,这种等待只消耗很小的CPU资源。

接着监控testBusyThread线程,如下图所示,testBusyThread线程一直在执行空循环,从堆栈追踪中看到一直在Test.java代码的13行停留,这一行行为:while(true)。这时候线程为Runnable状态,而且没有归还线程执行令牌的动作,会在空循环上用尽全部执行时间直到线程切换,这种等待会消耗较多的CPU资源。

下图显示testLockThread线程在等待着lock对象的notify或notifyAll方法的出现,线程这时候处于WAITING状态,在被唤醒前不会被分配执行时间。

testLockThread线程正在处于正常的活锁等待,只要lock对象的notify()或notifyAll()方法被调用,这个线程便能激活以继续执行。下面代码演示了一个无法再被激活的死锁等待。

public class DeadLockTest {
    
    static class SynAddRunalbe implements Runnable {
        int a, b;
        public SynAddRunalbe(int a, int b) {
            this.a = a;
            this.b = b;
        }

        @Override
        public void run() {
            synchronized (Integer.valueOf(a)) {
                synchronized (Integer.valueOf(b)) {
                    System.out.println(a + b);
                }
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new SynAddRunalbe(1, 2)).start();
            new Thread(new SynAddRunalbe(2, 1)).start();
        }
    }

}


图中很清晰地显示,线程Thread-141在等待一个被线程Thread-140持有的Integer对象,而点击线 程Thread-140则显示它也在等待一个被线程Thread-141持有的Integer对象,这样两个线程就互相卡住,除 非牺牲其中一个,否则死锁无法释放。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5694568.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存