場景:咱們在系統運行中,須要監控某個代碼段的運行時間,咱們徹底可使用currentTimeMillis來作,可是作起來比較麻煩,尤爲是須要階段監控的時候,那麼這個工具就出現啦~~~java
先說下想要實現的功能: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掉監控線程,而且把監控資源出棧。app
根據上面的設計能夠看到,該工具的使用須要注意下面的問題:jvm
1.若是不啓用監控線程,那麼能夠用於線上的場景。ide
2.若是啓用了監控線程,那麼只適合debug分析的場景,由於若是是線上,當監控的代碼塊併發量大起來的時候,會以1:1的比例建立監控線程,這個時候會有風險。工具
3.啓用監控線程的場景比較適合於,併發量較小(建立的監控線程少),可是執行時間長的場景。這個時候能夠對代碼塊進行執行分析。性能