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