好像刚开始接触Java的时候有这么一句话考验Java开发者:Java不像C++那样自己管理内存,Java虚拟机负责垃圾回收,所以就有了不再有内存泄漏问题。但是随着开发经验的增长,已经开发的应用程序越来越多,应用程序中需要加载的类也越来越多,经常会遇到内存溢出(OOM)。或者更准确的说,加载类增加导致的内存溢出是java.lang.OutOfMemoryError:PermGenspace这时候一般解决OOM的方法是:1.分析应用代码是否有问题,可以通过一些工具观察应用程序中占用内存较多的Class类型(例如通过JVisualVM——多功能profiling工具JVisualVM分析Java七兵器系列多情指环,或者通过MAT分析)2.修改JVM启动参数和增加PermGen的配置。在Tomcat这样的应用服务器中,由于作为应用容器运行,其自身的PermGen可能不会占用太多,但需要考虑部署在容器中的应用。有些应用程序依赖大量的第三方类库,有些应用程序在运行时动态生成大量的类。这些内容的加载很容易导致PermGen的OOM。对于OOM的处理,启动时内部会占用一小块内存,产生OOM时释放,暂时缓解。这称为oomParachute。此外,Tomcat还提供了在managerapplication中查找内存泄漏的功能。图上的描述写的很清楚。该函数主要用于分析应用停止、重新部署、卸载时是否发生了内存泄漏。请求后,管理器上方信息显示区会提示是否存在当前应用导致的内存泄漏。不过需要注意的是,这个函数会触发FullGC的执行。代码中使用了System.gc(),生产环境需要谨慎使用。那么,在什么情况下会造成所谓的应用程序内存泄漏呢?我们都知道,为了实现应用程序之间的类隔离,Tomcat会为每个应用程序使用一个单独的WebappClassLoader。这样一来,即使多个应用使用同一个WebappClassLoader类库的不同版本,也不会互相影响而引起冲突。但是,在这种情况下,当一个应用程序已经停止或重新部署时,会生成一个新的classLoader来加载新部署的应用程序类信息。我们知道,在Java中,类之间存在着引用关系,类似于强引用、弱引用、幻引用,用来在GC时回收一些不需要的类,腾出空间。按理说前面的classLoader应该是垃圾回收的,但是在某些时候,由于某些类前面的引用关系,这个classLoader和它加载的一系列类文件并不能被标记为垃圾。此时这些类仍然驻留在PermGen中,应用多次启停,多次重新部署后,出现了PermGen的OOM。一般使用以下类库很容易导致类加载器逃避垃圾回收而造成内存泄漏:JDBC驱动注册了一些日志框架,ThreadLocal使用了unstoppedThread,使用一些JavaAPI也很容易导致这个问题,比如javax.imageioAPIXML解析RMI使用因为这些类很容易占用classLoader,无法回收。如果将这些类交给各个应用程序的类加载器加载,PermGen中的类就会越来越多,导致泄漏。为此,在Tomcat中引入了组件JreMemoryLeakPreventionListener。思路是在Tomcat启动时通过SystemclassLoader加载这些类。由于类加载器的加载原则(默认父类优先,这些系统类会委托给系统类加载器加载),这些类不会被WebclassLoader重新加载,从而减少了内存泄漏的产生。默认情况下,这个组件在Tomcat的配置server.xml中已经启用,所以你已经在不知不觉中使用了这些功能。【本文为专栏作家“侯书城”原创稿件,转载请通过作者微信公众号“Tomcat物语”获得授权】点此查看本作者更多好文
