欢迎转载,转载请注明来源: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规则,然后扫描,是行的通的。