歡迎轉載,轉載請註明來源:http://blog.csdn.net/lywybo/article/details/8774116
這樣一種情況:
現在生產環境中避免工程師誤用System.gc(),大部分企業級java應用都會把程序中System.gc()的調用禁止掉。
禁用方法:調整jvm參數-XX:+DisableExplicitGC
禁用之後避免人爲造成系統的full gc影響生產環境的性能。禁用之後不但代碼中的System.gc()被禁用,jar包中的System.gc()也被禁用了。
最近生產環境遇到的一個問題:
某天在系統壓力、併發很小、但是單用戶訪問很頻繁的情況下服務器掛掉了,報
java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:633)
at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:98)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288)
爲啥會出現這個問題呢?等我下篇問章在說,反正就是因爲,禁用了System.gc(),然後某些jar必須通過這種方式來釋放,於是乎就出現這個錯誤。
如何解決:
考慮把System.gc()給打開,但是打開會帶來多大的影響呢?不知道,因爲我們不清楚系統依賴的jar包中有多少個jar用到了System.gc(),於是乎其他團隊的童鞋來求助我,有麼有什麼辦法能找出來,系統依賴的jar中哪些用到了gc。
當然最簡單的方法,一個一個jar去找。。。明顯不靠譜,哈哈
因爲jar大多數是class文件,字節碼,於是我就想到了findbugs。寫一條專門找System.gc()的規則,然後建立一個ant任務直接掃描系統的lib目錄,不就可以實現了麼?
想到就動手,首先寫gc的規則,規則開發參考:http://blog.csdn.net/lywybo/article/details/5335748
/**
* 這個規則類主要用於檢查代碼中是否有System.gc這樣的輸出語句
*
* @author yuezhen
* @version $Id: AlipayForbiddenSystemClass.java,v 0.1 2013-04-08 下午05:12:43
* yuezhen Exp $
*/
public class AlipayForbiddenSystemGcClass extends OpcodeStackDetector {
private final BugReporter bugReporter;
private static final String SYSTEMFLG = "java/lang/System";
private static final String GC = "gc";
/**
* @param bugReporter
*/
public AlipayForbiddenSystemGcClass(BugReporter bugReporter) {
this.bugReporter = bugReporter;
}
/**
* visit方法,在每次進入字節碼方法的時候調用 在每次進入新方法的時候清空標誌位
*/
@Override
public void visit(Code obj) {
super.visit(obj);
}
/**
* 每掃描一條字節碼就會進入sawOpcode方法
*
* @param seen
* 字節碼的枚舉值
*/
@Override
public void sawOpcode(int seen) {
if (seen == INVOKESTATIC) {
// 判斷是否爲SYSTEMFLG類型,並且使用了GV方法
if (getClassConstantOperand().equals(SYSTEMFLG) && getNameConstantOperand().equals(GC)) {
BugInstance bug = new BugInstance(this, "ALP_ALIPAY_SYSTEM_GC_CLASS", HIGH_PRIORITY).addClassAndMethod(this)
.addSourceLine(this, getPC());
bug.addInt(getPC());
bugReporter.reportBug(bug);
}
}
}
}
悲催的是,寫好規則之後,測試時候發現,原來findbugs默認已經有這條規則了。。。。我了個汗。不過複習了一遍規則的開發。
既然有規則了,那麼要做的就簡單了,只需要寫個ant任務,來掃描即可。並且我們需要設置只掃描GC那條規則。否則規則太多影響分析。
<project name="scan">
<property name ="findbugs.path" value ="/home/admin/findbugs/findbugs-1.3.9/lib" />
<property name ="findbugs.result" value ="/home/admin/findbug" />
<property name ="findbugs.scan.path" value ="/home/admin/....項目lib路徑.../lib/" />
<property name ="findbugs.include.filter" value ="/home/admin/findbugs/rule.xml" />
<path id ="findbug.path" >
<fileset dir ="${findbugs.path}" >
<include name ="*.jar" />
</fileset >
</path >
<!--定義findBugs任務 -->
<taskdef name ="findbugs" classpathref ="findbug.path" classname ="edu.umd.cs.findbugs.anttask.FindBugsTask" />
<target name ="findbug" >
<delete dir ="${findbugs.result}" />
<mkdir dir ="${findbugs.result}" />
<findbugs home ="${findbugs.path}" output ="html" outputFile ="${findbugs.result}/bug.html" jvmargs ="-Xmx1500m" timeout ="6000000" includeFilter="${findbugs.include.filter}" >
<!-- 掃描的class、jar路徑 -->
<class location ="${findbugs.scan.path}/" />
<!-- 依賴的jar路徑,findbugs掃描過程中 -->
<auxClasspath>
<fileset dir ="${findbugs.scan.path}" >
<include name ="*.jar" />
</fileset >
</auxClasspath >
</findbugs >
</target >
</project>
下面來一個findbugs的過濾器,來保證只掃描gc規則,規則詳解參考:http://hulongzhou.blog.hexun.com/33024717_d.html
<FindBugsFilter>
<Match>
<BugCode name ="Dm"/>
<Bug pattern="DM_GC" />
</Match>
</FindBugsFilter>
於是乎,規則寫好了,直接運行之,看報告,很清楚的可以知道,如下幾個jar用到了gc
----------------------------------------------分割線----------------------------------------------------------
其實這提供了一種思路,如果想查找jar中某些特定規則的代碼,其實通過寫findbugs規則,然後掃描,是行的通的。