通过《深入理解Java虚拟机》一书中,需要了很多,今天是对性能工具和故障处理的学习笔记:

在给系统定位问题的时候,知识,经验是关键基础,数据是依据,工具是处理数据的手段。此处所说的数据包括:GC日志,运行日志,异常堆栈,线程快照(Threaddump/javacore文件),

堆转储快照(heapdump/hprof文件)等。经常使用虚拟机监控和分析工具可以加快分析数据、定位解决问题的速度。

1)Java的命令行工具:bin下的Java.ext, javac.exe是运行和编译工具,几乎所有的体积都非常小,因为大多是类库中的一层薄包装,linux中的JDK,很多都是用shell脚本直接写成的

jdk 的故障和监控工具如下所示:

jps:虚拟机进程状况工具

  是使用命令最高的JDK命令行工具,因为其他的JDK工具需要输入的条件LVMID需要这个命令查询出来,来确定要监控的进程是哪一个虚拟机进程。

jstat:虚拟机统计监控信息工具

用于监控虚拟机各个运行状态信息的命令行工具。它可以显示本地或者远程上的类装载,内存,垃圾收集,JIT编译等运行时数据。在没有GUI图像界面时,只提供纯文本控制台环境的服务器上,它将是定位运行期虚拟机性能问题的首选工具。

例如:jstat gc 2764 250 20: 表示需要250毫秒查询一次进程2764垃圾收集情况,一共查询20次

gc的位置可以换做其他的字段

 jmap:Java内存映像工具,用于生成堆转储快照,一般成dump文件或者heapdump文件,还可以通过配置VM参数获取:-XXHeapDumpOnOutOfMemoryError

例如:jmap -dump:format=b,file=eclipse.bin 3500 生成一个正在运行的eclipse的dump快照文件的例子,后面的3500用于标识进程,可通过jps查询得知

jhat:虚拟机转储快照分析工具,与jmap搭配使用,结果分析后,可以在网页上查看具体情况,不过一般不这么做,因为分析工具是一种耗时耗资源的进程,可放到其他机器上运行,且分析功能简陋,再次不再叙述。

jstack:Java堆栈跟踪工具,用于生成当前时刻的线程快照,目的是定位线程出现长时间停顿的原因,如线程间的死锁,死循环,请求外部资源导致长时间停顿的原因。

线程停顿的时候,通过jstack查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情,或者在等待什么资源。

jstack [option] vmid   :option处有3中情况:

1)F:当正常输入的请求不被响应时,强制输出线程堆栈

2)-l:除堆栈外,显示关于锁的附加信息

3)-m:如果调用到本地方法的话,可以显示C/C++的堆栈

JConsole:JDK的可视化工具,自动搜索出本机运行的所有虚拟机进程,不需要用户自己再使用jps来查询了,对远程虚拟机进程监控。概览页有4部分,堆内存使用情况,线程,类,CPU使用情况。概览是后面页的概述情况。内存页相当与可视化的jstat命令,用于监视收集器管理的虚拟机内存的变化趋势。

下面针对内存页的监视案例:

import java.util.ArrayList;
import java.util.List;

/**
 * 内存占位符对象,一个Object对象大约占64K
 * 虚拟机的参数设置为:-Xms100m -Xmx100m -XX:+UseSerialGC
 */
public class OOMObject {

    public byte[] placeholder=new byte[64*1024];
    
    public static void fileHeap(int num)throws InterruptedException{
        List<OOMObject> list=new ArrayList<OOMObject>();
        for(int i=0;i<num;i++) {
            //稍作延时,令监视曲线的变化更加明显
            Thread.sleep(50);
            list.add(new OOMObject());
        }
        System.gc();
    }
    public static void main(String[] args) throws Exception {
        fileHeap(1000);
    }
}

运行bin下的jconsole.exe,会出现

 

选择你需要的程序文件,比如OOMObject类,在运行时,就会在下拉框中出现这个类的选项,截图如下所示;

下拉框可以选择想看的内存区域,Eden Space 区域是呈折线状的,扩大到整个堆后,是一条向上增长的曲线,老年代的圆柱仍然保持峰值,说明被填充进堆的数据在System.gc()后仍然存活,从这监控图里面,可以看出,Eden大小为27328KB,因为没有设置-XXSurvivorRadio(设置survivor与Eden的比值)参数,采用的默认比例Eden:survivor=8:1.

则整个新生代空间大约为27328*125%=34160KB。

 线程监控

线程长时间等待的原因:等待外部资源(数据库连接,网络资源,设备资源等),死循环,所等待(死锁和活锁)。

线程等待的演示实例代码如下所示:

package com.three.twentynine;

import java.io.BufferedReader; import java.io.IOException;
import java.io.InputStreamReader;

public class TestThread {

   
    //线程死循环方法
    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) {//此处的synchronized用来实现加锁机制,只有一个线程执行锁住的代码,其他线程需要等待
                    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();
    }
}

在运行后,在线程中选择main查看:截图如下所示:

接着选择死循环方法,会看到,此循环一直在执行空循环,从堆栈中可以看到哪个类中的哪行代码中停留,而且没有看到线程归还执行令牌的动作,会一直执行直到线程切换,这种等待会消耗较多的CPU资源。截图如下所示;

划横线的地方就是具体哪个类中的哪个方法,在哪行出的问题,这个是说TestThread.java类中的第16行代码,就是while(true);这行代码。

 而线程锁等待部分,testLockThread线程在等待着lock对象的notify,notifyall方法,线程这时就是waiting状态,在被唤醒前不会被分配执行时间。

 

 

posted on 2019-04-10 00:13  奋斗的小鸟gogogo  阅读(163)  评论(0编辑  收藏  举报