一个java的Profile工具
Posted 生若秋叶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个java的Profile工具相关的知识,希望对你有一定的参考价值。
场景:我们在系统运行中,需要监控某个代码段的运行时间,我们完全可以使用currentTimeMillis来做,但是做起来比较麻烦,尤其是需要阶段监控的时候,那么这个工具就出现啦~~~
先说下想要实现的功能:1.能够对代码段进行运行时间的监控,比如代码行a->代码行b的运行时间。2.能够监控代码行嵌套的运行时间监控,比如a->b->c->d中a->d和b->c的运行时间监控(类似括号一样,形成配对的方式)。3.能够在运行范围内的jvm一些指标的监控,比如内存使用量等。
/** * 性能相关的调试工具,支持,线程正式场景,做运行时间的profile,运行性能监控(不建议线上使用,因为需要开启监控线程) * 该工具不会抛出任何异常 * @author Administrator * @version $Id: ProfileUtils.java, v 0.1 2016年9月5日 下午11:02:45 Administrator Exp $ */ public class Profiler { /** debug模式 */ // private static volatile boolean debug = false; private final static String LOG_TEMPLATE = "[messag=%s][startTime=%s][endTime=%s][durationTime=%sms][processors=%s][memUse=%s]"; private final static String SIMPLE_LOG_TEMPLATE = "[durationTime=%sms][message=%s]"; private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss"); /** profile日志,建议运行中别做修改,否则有些配置会导致残留线程 */ private static ThreadLocal<ProfileConfig> configHolder = new ThreadLocal<ProfileConfig>() { protected ProfileConfig initialValue() { return new ProfileConfig( false, false, 0); }; }; /** 开始monitor的时间 */ private static ThreadLocal<Stack<MonitorResource>> resStackHolder = new ThreadLocal<Stack<MonitorResource>>() { protected java.util.Stack<MonitorResource> initialValue() { return new Stack<MonitorResource>(); }; }; /** 监控线程 */ private static ThreadLocal<MonitorThread> monitorThreadHolder = new ThreadLocal<MonitorThread>(); /** * 开始monitor */ public static void enter(Object msgObj) { try { Stack<MonitorResource> monitorResStack = resStackHolder.get(); monitorResStack.push(new MonitorResource(msgObj, System.currentTimeMillis())); ProfileConfig config = configHolder.get(); //开启监控线程 if (config.isUseMonitorThread()) { if (monitorThreadHolder.get() != null) { killThread(); } MonitorThread monitorThread = new MonitorThread(getCurrentMonitorRes(), config); monitorThreadHolder.set(monitorThread); monitorThread.start(); } } catch (Throwable e) { // if (debug) { // e.printStackTrace(); // } return; } } /** * 结束monitor * @return */ public static MonitorResource release() { try { Stack<MonitorResource> monitorResStack = resStackHolder.get(); MonitorResource monitorResource = getCurrentMonitorRes(); monitorResource.setEndTime(System.currentTimeMillis()); ProfileConfig config = configHolder.get(); //监控线程关闭 if (config.isUseMonitorThread()) { killThread(); } return monitorResStack.pop(); } catch (Throwable e) { // if (debug) { // e.printStackTrace(); // } return new MonitorResource(e.getMessage(), 0); } } /** * 使用新的messageObj替换原来的 * @param messageObj * @return */ public static MonitorResource release(Object messageObj) { MonitorResource monitorResource = release(); monitorResource.setMessageObj(messageObj); return monitorResource; } /** * 结束monitor并且打印日志 * @param logger * @return */ public static MonitorResource releaseAndLog(Logger logger, Object messageObj) { MonitorResource resource = release(messageObj); LoggerUtils.info(logger, resource); return resource; } /** * 结束monitor并且打印日志 * @param logger * @return */ public static MonitorResource releaseAndLog(Logger logger) { MonitorResource resource = release(); LoggerUtils.info(logger, resource); return resource; } /** * 设置profile配置 * @param config */ public static void setProfileConfig(ProfileConfig config) { configHolder.set(config); } /** * Setter method for property <tt>debug</tt>. * * @param debug value to be assigned to property debug */ // public static void setDebug(boolean debug) { // Profiler.debug = debug; // } /** * 移除监控线程 */ private static void killThread() { try { MonitorThread futureTask = monitorThreadHolder.get(); monitorThreadHolder.remove(); futureTask.interrupt(); } catch (Throwable e) { // ignore // if (debug) { // e.printStackTrace(); // } } } /** * 获取当前的monitorRes * @return */ public static MonitorResource getCurrentMonitorRes() { try { Stack<MonitorResource> resStack = resStackHolder.get(); return resStack.get(resStack.size() - 1); } catch (Exception e) { // if (debug) { // e.printStackTrace(); // } return new MonitorResource(e.getMessage(), 0); } } /** * 资源使用情况,比如cpu最大使用量等。 * @author Administrator * @version $Id: Profile.java, v 0.1 2016年9月5日 下午11:38:39 Administrator Exp $ */ public static class MonitorResource { /** 当前资源的标志 */ private Object messageObj = null; private long startTime = 0; private long endTime = 0; private int processorNums = 0; private List<Long> memUse = Lists.newArrayList(); /** * @param messageObj * @param startTime */ public MonitorResource(Object messageObj, long startTime) { super(); this.messageObj = messageObj; this.startTime = startTime; } /** * Setter method for property <tt>messageObj</tt>. * * @param messageObj value to be assigned to property messageObj */ public void setMessageObj(Object messageObj) { this.messageObj = messageObj; } public String getMemUse() { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < memUse.size(); i++) { stringBuilder.append(memUse.get(i) / 1024L + "K"); if (i != memUse.size() - 1) { stringBuilder.append(","); } } return stringBuilder.toString(); } /** * 获取整个profile堆栈 * @return */ public Stack<MonitorResource> getMonitorResStack() { return resStackHolder.get(); } /** * @see java.lang.Object#toString() */ @Override public String toString() { return configHolder.get().isUseSimpleLogTemplate() ? (String.format(SIMPLE_LOG_TEMPLATE, endTime - startTime, messageObj)) : (String.format(LOG_TEMPLATE, messageObj, DATE_FORMAT.format(new Date(startTime)), DATE_FORMAT.format(new Date(endTime)), endTime - startTime, processorNums, getMemUse())); } /** * 获取运行时间 * @return */ public long getDurTime() { return endTime - startTime; } public void putMemUse(long l) { memUse.add(l); } /** * Setter method for property <tt>endTime</tt>. * * @param endTime value to be assigned to property endTime */ public void setEndTime(long endTime) { this.endTime = endTime; } /** * Getter method for property <tt>messageObj</tt>. * * @return property value of messageObj */ public Object getMessageObj() { return messageObj; } /** * Setter method for property <tt>processorNums</tt>. * * @param processorNums value to be assigned to property processorNums */ public void setProcessorNums(int processorNums) { this.processorNums = processorNums; } } public static class ProfileConfig { private boolean useSimpleLogTemplate = false; private boolean useMonitorThread = false; private int monitorCollectDurTime = 500; /** * @param useSimpleLogTemplate * @param useMonitorThread * @param monitorCollectDurTime */ public ProfileConfig(boolean useSimpleLogTemplate, boolean useMonitorThread, int monitorCollectDurTime) { super(); this.useSimpleLogTemplate = useSimpleLogTemplate; this.useMonitorThread = useMonitorThread; this.monitorCollectDurTime = monitorCollectDurTime; } /** * Getter method for property <tt>useSimpleLogTemplate</tt>. * * @return property value of useSimpleLogTemplate */ public boolean isUseSimpleLogTemplate() { return useSimpleLogTemplate; } /** * Setter method for property <tt>useSimpleLogTemplate</tt>. * * @param useSimpleLogTemplate value to be assigned to property useSimpleLogTemplate */ public void setUseSimpleLogTemplate(boolean useSimpleLogTemplate) { this.useSimpleLogTemplate = useSimpleLogTemplate; } /** * Getter method for property <tt>useMonitorThread</tt>. * * @return property value of useMonitorThread */ public boolean isUseMonitorThread() { return useMonitorThread; } /** * Setter method for property <tt>useMonitorThread</tt>. * * @param useMonitorThread value to be assigned to property useMonitorThread */ public void setUseMonitorThread(boolean useMonitorThread) { this.useMonitorThread = useMonitorThread; } /** * Getter method for property <tt>monitorCollectDurTime</tt>. * * @return property value of monitorCollectDurTime */ public int getMonitorCollectDurTime() { return monitorCollectDurTime; } /** * Setter method for property <tt>monitorCollectDurTime</tt>. * * @param monitorCollectDurTime value to be assigned to property monitorCollectDurTime */ public void setMonitorCollectDurTime(int monitorCollectDurTime) { this.monitorCollectDurTime = monitorCollectDurTime; } } private static class MonitorThread extends Thread { private static final AtomicLong threadCount = new AtomicLong(); private MonitorResource monitorResource; private final ProfileConfig config; /** * */ public MonitorThread(MonitorResource resource, ProfileConfig config) { monitorResource = resource; setName("monitor-thread-" + threadCount.getAndIncrement()); setDaemon(true); this.config = config; } /** * @see java.lang.Thread#run() */ @Override public void run() { monitorResource.setProcessorNums(Runtime.getRuntime().availableProcessors()); while (true) { monitorResource.putMemUse( Runtime.getRuntime().maxMemory() - Runtime.getRuntime().freeMemory()); try { Thread.sleep(config.getMonitorCollectDurTime()); } catch (InterruptedException e) { // if (debug) { // e.printStackTrace(); // } return; } } } } }
可以看到,我们有个监控资源的概念,每个阶段都对应一个监控资源,比如a->d和b->c都对应了一个监控资源。从实现上,每次进入监控的时候,会生成一个监控资源,并且记录当前时间,并且把该监控资源压栈。同时,还会启动一个监控线程,将jvm的状态不断写入该监控资源中,所以针对a->d和b->c都生成了自己的监控线程,并且吧jvm状态写入自己的监控资源中。 当release的时候,会kill掉监控线程,并且把监控资源出栈。
根据上面的设计可以看到,该工具的使用需要注意下面的问题:
1.如果不启用监控线程,那么可以用于线上的场景。
2.如果启用了监控线程,那么只适合debug分析的场景,因为如果是线上,当监控的代码块并发量大起来的时候,会以1:1的比例创建监控线程,这个时候会有风险。
3.启用监控线程的场景比较适合于,并发量较小(创建的监控线程少),但是执行时间长的场景。这个时候可以对代码块进行执行分析。
以上是关于一个java的Profile工具的主要内容,如果未能解决你的问题,请参考以下文章