JVM工具系列第7篇-jconsole:Java监视与管理控制台

star2017 1年前 ⋅ 265 阅读

jconsole介绍

JConsole(java monitoring and management console)是一款基于JMX的可视化监视和管理工具。

启动JConsole

  1. 点击JDK/bin 目录下面的“jconsole.exe”即可启动
  2. 然后会自动自动搜索本机运行的所有虚拟机进程
  3. 选择其中一个进程可开始进行监控

JConsole基本介绍

JConsole 基本包括以下基本功能:概述、内存、线程、类、VM概要、MBean

运行下面的程序、然后使用JConsole进行监控;注意设置虚拟机参数

  1. package com.jvm.jconsole;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * <a href="http://www.itsoku.com/archives">Java干货铺子,只生产干货,公众号:javacode2018</a>
  6. * <p>
  7. * 设置虚拟机参数:-Xms100M -Xms100m -XX:+UseSerialGC -XX:+PrintGCDetails
  8. */
  9. public class Demo1 {
  10. static class OOMObject {
  11. public byte[] placeholder = new byte[64 * 1024];
  12. }
  13. public static void fillHeap(int num) throws InterruptedException {
  14. Thread.sleep(20000); //先运行程序,在执行监控
  15. List<OOMObject> list = new ArrayList<OOMObject>();
  16. for (int i = 0; i < num; i++) {
  17. // 稍作延时,令监视曲线的变化更加明显
  18. Thread.sleep(50);
  19. list.add(new OOMObject());
  20. }
  21. System.gc();
  22. }
  23. public static void main(String[] args) throws Exception {
  24. fillHeap(1000);
  25. while (true) {
  26. //让其一直运行着
  27. }
  28. }
  29. }

打开JConsole查看上面程序


可以切换顶部的选项卡查看各种指标信息。

内存监控

“内存”页签相当于可视化的jstat 命令,用于监视受收集器管理的虚拟机内存的变换趋势。

还是上面的程序:

代码运行,控制台也会输出gc日志:

  1. [GC (Allocation Failure) [DefNew: 27328K->3392K(30720K), 0.0112139 secs] 27328K->19901K(99008K), 0.0112664 secs] [Times: user=0.00 sys=0.01, real=0.01 secs]
  2. [GC (Allocation Failure) [DefNew: 30720K->3392K(30720K), 0.0133413 secs] 47229K->40117K(99008K), 0.0133708 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
  3. [GC (Allocation Failure) [DefNew: 30664K->3374K(30720K), 0.0140975 secs] 67389K->65091K(99008K), 0.0141239 secs] [Times: user=0.00 sys=0.02, real=0.01 secs]
  4. [Full GC (System.gc()) [Tenured: 61716K->66636K(68288K), 0.0098835 secs] 66919K->66636K(99008K), [Metaspace: 9482K->9482K(1058816K)], 0.0100578 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

线程监控

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

下面三个方法分别等待控制台输入、死循环演示、线程锁等待演示

第一步:运行如下代码:

  1. package com.jvm.jconsole;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. /**
  6. * <a href="http://www.itsoku.com/archives">Java干货铺子,只生产干货,公众号:javacode2018</a>
  7. */
  8. public class Demo2 {
  9. public static void main(String[] args) throws IOException {
  10. waitRerouceConnection();
  11. createBusyThread();
  12. createLockThread(new Object());
  13. }
  14. /**
  15. * 等待控制台输入
  16. *
  17. * @throws IOException
  18. */
  19. public static void waitRerouceConnection() throws IOException {
  20. Thread thread = new Thread(new Runnable() {
  21. @Override
  22. public void run() {
  23. BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  24. try {
  25. br.readLine();
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. }, "waitRerouceConnection");
  31. thread.start();
  32. }
  33. /**
  34. * 线程死循环演示
  35. */
  36. public static void createBusyThread() {
  37. Thread thread = new Thread(new Runnable() {
  38. @Override
  39. public void run() {
  40. while (true) {
  41. ;
  42. }
  43. }
  44. }, "testBusyThread");
  45. thread.start();
  46. }
  47. /**
  48. * 线程锁等待演示
  49. */
  50. public static void createLockThread(final Object lock) {
  51. Thread thread = new Thread(new Runnable() {
  52. @Override
  53. public void run() {
  54. synchronized (lock) {
  55. try {
  56. lock.wait();
  57. } catch (InterruptedException e) {
  58. e.printStackTrace();
  59. }
  60. }
  61. }
  62. }, "testLockThread");
  63. thread.start();
  64. }
  65. }

第二步:打开jconsole中查看上面程序运行情况,可以查看到3个目标线程

第三步:查看目标线程信息
waitRerouceConnection线程处于读取数据状态,如下图:

testBusyThread线程位于代码45行,处于运行状态,如下图:

testLockThread处于活锁等待状态,如下图:

只要lock对象的notify()或notifyAll()方法被调用,这个线程便可能激活以继续执行

通过“线程”这个窗口可以很方便查询虚拟机中的线程堆栈信息,对发现系统中的一些问题非常有帮助。

线程死锁演示

第一步:运行下面代码:

  1. package com.jvm.jconsole;
  2. /**
  3. * <b>description</b>: <br>
  4. * <b>time</b>:2019/6/2 22:39 <br>
  5. * <b>author</b>:ready likun_557@163.com
  6. */
  7. public class Demo3 {
  8. public static void main(String[] args) {
  9. User u1 = new User("u1");
  10. User u2 = new User("u2");
  11. Thread thread1 = new Thread(new SynAddRunalbe(u1, u2, 1, 2, true));
  12. thread1.setName("thread1");
  13. thread1.start();
  14. Thread thread2 = new Thread(new SynAddRunalbe(u1, u2, 2, 1, false));
  15. thread2.setName("thread2");
  16. thread2.start();
  17. }
  18. /**
  19. * 线程死锁等待演示
  20. */
  21. public static class SynAddRunalbe implements Runnable {
  22. User u1, u2;
  23. int a, b;
  24. boolean flag;
  25. public SynAddRunalbe(User u1, User u2, int a, int b, boolean flag) {
  26. this.u1 = u1;
  27. this.u2 = u2;
  28. this.a = a;
  29. this.b = b;
  30. this.flag = flag;
  31. }
  32. @Override
  33. public void run() {
  34. try {
  35. if (flag) {
  36. synchronized (u1) {
  37. Thread.sleep(100);
  38. synchronized (u2) {
  39. System.out.println(a + b);
  40. }
  41. }
  42. } else {
  43. synchronized (u2) {
  44. Thread.sleep(100);
  45. synchronized (u1) {
  46. System.out.println(a + b);
  47. }
  48. }
  49. }
  50. } catch (InterruptedException e) {
  51. e.printStackTrace();
  52. }
  53. }
  54. }
  55. public static class User {
  56. private String name;
  57. public String getName() {
  58. return name;
  59. }
  60. public void setName(String name) {
  61. this.name = name;
  62. }
  63. public User(String name) {
  64. this.name = name;
  65. }
  66. @Override
  67. public String toString() {
  68. return "User{" +
  69. "name='" + name + '\'' +
  70. '}';
  71. }
  72. }
  73. }

thread1持有u1的锁,thread2持有u2的锁,thread1等待获取u2的锁,thread2等待获取u1的锁,相互需要获取的锁都被对方持有者,造成了死锁。程序中出现了死锁的情况,我们是比较难以发现的。需要依靠工具解决。刚好jconsole就是这个美妙的工具。

第二步:在jconsole中打开上面程序的监控信息:

从上面可以看出代码43行和50行处导致了死锁。

关于程序死锁的,我们还可以使用命令行工具jstack来查看java线程堆栈信息,也可以发现死锁。

最新资料

更多内容请访问:IT源点

全部评论: 0

    我有话说: