2019独角兽企业重金招聘Python工程师标准>>>

最近遇到了这个问题,在修改了-Xmx后有时仍然会出现,下文分析的很有启发,看了下文重新分析我的应用,在项目中我使用了spring mvc作为控制层,由于使用到了微信公众平台开发,配置请求的时候又在web.xml中使用普通的Servlet配置,这样tomcat中Container的classloder加载了一个servlet类,spring容器又加载了自己的控制层,然后在业务层里两个容器的加载的类之间出现了调用,及spring controller中引用了另一个Servlet中的对象,当另一方的Servlet被释放时并没有被gc回收,这样就出现了classloder内存泄露。

我的web.xml配置如下:

<display-name>chihuo</display-name><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:beans.xml</param-value></context-param><!-- 微信监听器 --><listener><listener-class>com.banmacoffee.weixin.demo.Engine</listener-class></listener><!-- Bootstrap the root application context as usual using ContextLoaderListener --><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><servlet><servlet-name>banma</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>banma</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!-- 微信配置开始 --><!-- 提供给微信服务器调用的接口(对被动消息进行响应,也就是微信要你填写的URL) --><servlet><servlet-name>WeixinmpAccessServlet</servlet-name><servlet-class>com.banmacoffee.weixin.demo.WeixinmpAccessServlet</servlet-class></servlet><servlet-mapping><servlet-name>WeixinmpAccessServlet</servlet-name><url-pattern>/weixinapi</url-pattern></servlet-mapping>

转载原文如下:http://www.tuicool.com/articles/eAnayu

在你重新部署你的应用程序到应用服务器(比如tomcat、weblogic等)时,你是否也遇到过 java.lang.OutOfMemoryError:PermGen space error? 是否也曾一边抱怨这个应用服务器,一边重启,然后继续你的工作,同时脑子里还在想着这一定是该服务器的一个BUG。那些应用服务器开发者们,应该仔细一点,对吗?或许吧,但是你有想过,这的的确确是你的过错吗?

我们先看一下下面这段代码,看似没有任何问题的一个Servlet类:

public class MyServlet extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// Log at a custom levelLevel customLevel = new Level("OOPS", 555) {
};
Logger.getLogger("test").log(customLevel, "doGet() called");
}
}

试着重复发布这个例子几次,我敢打赌最终你会看到java.lang.OutOfMemoryError: PermGen space error.如果你想知道发生了什么,请继续往下看。

问题描述:

使用应用程序服务器(比如Glassfish、Tomcat等),我们可以同时部署多个应用程序。而应用的开发总是迭代进行的,添加或者改变现有的代码可能像家常便饭一样。然后,为了测试新的改动,你重新编译,然后重新部署,而不影响其他已经发布的应用程序(这种方式称之为热部署),因为不用重启应用服务器。这种热部署的机制很多应用服务器都支持(比如Glassfish,Tomcat等)。

而热部署的实现方式,就是使用不同的classloader去加载每一个应用程序。简单地说,一个classloader就是一个从jar文件中加载.class文件的简单的类。当你卸载应用时,该classloader连同所有由该classloader加载的类都将被垃圾回收掉(可能不会立即回收,但是没用任何引用的对象,最终都会被gc回收)。

但是,有时候有些对象会防不胜防地引用到classloader,这样gc就无法对其进行回收。这就是java.lang.OutOfMemoryError:PermGen space error 的由来。

永久区

什么是永久区?java虚拟机中的内存被分为几个部分,其中一个部分被称之为永久区,或者方法区。这个区域是用来加载类文件的。这个区域的大小在JVM中是固定的,当JVM运行之后,该区域大小不会改变。你可以通过-XX:MaxPermSize参数来指定该区域的大小。在Sun 的HotSpot虚拟机里,该值默认是64MB

如果存在classloader泄露,而你又经常加载新的类,那么最终这块区域将被使用完,尽管整个堆被占用的很少。就算你使用了-Xmx参数也无济于事,因为该参数只会影响整个堆的大小,但是不会影响该方法区的大小。

比如如下这段简短的代码:

private void x1() {for (;;) {
List c = new ArrayList();
}
}

这段代码持续地分配内存,但是这个应用并不会内存溢出。那是因为这些被创建的对象都是可以被垃圾回收的,当没有足够的内存去创建新的对象时,gc将会对那些死对象(没有被任何对象引用的对象(s))进行回收。

首先,我们再次简化上面Servlet,看一下内存引用图。

public class Servlet1 extends HttpServlet {private static final String STATICNAME = "Simple";protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}

当上面的Servlet被加载之后,内存中将会有如下对象:

图中被application classloader加载的类用黄色标识,其余的使用绿色可以看到,一个容器对象(Container)引用了两个对象,一个是用于加载该应用程序的classloader,还有一个引用到了Servlet1(主要为了当有web请求进来的时候,可以执行doGet()方法)需要注意的是,STAtICNAME 对象是被Servlet1的class对象持有的。其他需要注意的是:

1、 像每个对象一样,Servlet1实例引用了其class对象 
2、 每一个class对象都引用了加载它的classloader对象 
3、 每一个classloader对象都持有着所有由它加载的类对象

这里一个重要的结果是:如果其他classloader加载的对象引用了由AppClassLoader加载的对象,那么所有由AppClassLoader加载的类都将无法被gc回收。当应用程序被销毁的时候,容器对象(Container)取消对Servlet1和AppClassLoader的引用。这个时候的内存引用图如下:

正如图所示,所有的类对象都是无法到达的,因此这些对象都将被gc回收。现在我们看一下,使用最上面的那个例子会发生什么。

public class LeakServlet extends HttpServlet {private static final String STATICNAME = "This leaks!";private static final Level CUSTOMLEVEL = new Level("test", 550) {
}; // anon class!protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Logger.getLogger("test").log(CUSTOMLEVEL, "doGet called");
}
}

请注意,CUSTOMLEVEL的类是一个匿名类,这是因为Level的构造函数是protected大的。我们看下对应的内存引用图:

从这个图片你可以看到一些意外的结果,Level 类引用了所有被创建的Level实例。JDK中Level的构造函数如下:

protected Level(String name, int value, String resourceBundleName) {if (name == null) {throw new NullPointerException();
}this.name = name;this.value = value;this.resourceBundleName = resourceBundleName;
synchronized (Level.class) {
known.add(this);
}
}

其中,known是Level中的一个静态的ArrayList。那么,现在当该应用被销毁时,会发生什么?

只有LeakServlet对象可以被gc回收。因为AppClassloader之外的对象引用了CUSTOMLEVEL,导致CUSTOMLEVEL匿名类无法被gc回收,间接导致AppClassLoader也不能被gc回收,最终导致所有被AppClassLoader加载的类都无法被gc回收。

总结:如果由一个classloader加载的对象被另一个classloader加载的对象引用,可能会引起classloader内存泄露。

为什么classloader内存泄露值得注意:

1、如果一个classloader存在内存泄露,那么它将会一直持有其加载的所有类对象,而每个类对象又持有了其所有静态变量。而在一般的应用程序当中,静态对象中常常维护了对象的缓存,单例对象以及各种配置和应用程序状态等数据。即使,在你的应用中也许没有任何静态的缓存,那也不意味着你使用的框架以及一些第三方资源不会这么做。因此,classloader内存泄露,导致的后果是往往是很惨重的。

2、那么classloader内存泄露,很难发生吗?错。一不小心引用了一个由另一个classloader加载的对象,就会导致classloader内存泄露。尽管这个对象似乎是无害的,但是,它依然维持了classloader的引用和所有相关的应用程序数据。应用中一个这样的误操作可能将会导致最终的java.lang.OutOfMemoryError:PermGen space error 
所以,classloader内存泄露,很容易发生。

英文地址: http://frankkieviet.blogspot.com/2006/10/classloader-leaks-dreaded-permgen-space.html

转载于:https://my.oschina.net/freegeek/blog/302422

Classloader内存泄露相关推荐

  1. Analyzer tool(MAT)分析JVM内存泄露案例

    1.监控目的 2.常用分析工具 3.分析案例-MAT MAT 介绍及分析: 启动MAT, 然后选择菜单项 File- Open Heap Dump 来加载需要分析的堆转储文件.文件加载完后,可以看到如 ...

  2. java 内存泄漏场景_Java内存泄露的例子

    在定位JVM性能问题时可能会遇到内存泄露导致JVM OutOfMemory的情况,在使用Tomcat容器时如果设置了reloadable="true"这个参数,在频繁热部署应用时也 ...

  3. LeakCanary: 让内存泄露无所遁形

    LeakCanary: 让内存泄露无所遁形 09 May 2015 本文为LeakCanary: Detect all memory leaks!的翻译.原文在: https://corner.squ ...

  4. ThreadLocal 内存泄露的实例分析

    前言 之前写了一篇深入分析 ThreadLocal 内存泄漏问题是从理论上分析ThreadLocal的内存泄漏问题,这一篇文章我们来分析一下实际的内存泄漏案例.分析问题的过程比结果更重要,理论结合实际 ...

  5. js内存泄露 垃圾回收_Java内存体系结构(模型),垃圾回收和内存泄漏

    js内存泄露 垃圾回收 Java内存架构(Java内存模型) 上面是堆的Java内存模型以及Java虚拟机(JVM)中运行的任何Java应用程序的PermGen. 还提供了比率,以使您更好地了解如何在 ...

  6. ThreadLocal中的3个大坑,内存泄露都是小儿科!

    我在参加Code Review的时候不止一次听到有同学说:我写的这个上下文工具没问题,在线上跑了好久了.其实这种想法是有问题的,ThreadLocal写错难,但是用错就很容易,本文将会详细总结Thre ...

  7. java 内存泄露监控,Linux下实用的JAVA内存泄露监控命令

    内存泄露一直是比较头疼的事情,下面三个命令我个人觉得相当有用. 1.jstack (linux下特有) 可以观察到jvm中当前所有线程的运行情况和线程当前状态 jstack 2083 输出内容如下: ...

  8. Java中七个潜在的内存泄露风险,你知道几个?

    虽然Java程序员不用像C/C++程序员那样时刻关注内存的使用情况,JVM会帮我们处理好这些,但并不是说有了GC就可以高枕无忧,内存泄露相关的问题一般在测试的时候很难发现,一旦上线流量起来可能马上就是 ...

  9. 检测java内存泄露_MAT 检测 Java内存泄露检测

    一.Java内存泄露例子 Vector v = new Vector( 10 ); for ( int i = 1 ;i < 100 ; i ++ ){ Object obj = new Obj ...

最新文章

  1. mysql 储存过程
  2. Linux内核源代码分析-第三章 内核体系结构概述-1
  3. 图解opengl曲线和曲面绘制
  4. 区块链BaaS云服务(21)腾讯CCGP”跨链事务“
  5. AGC002E Candy Piles
  6. 结构体里有指针 scanf赋值_C++|链表中常见的链表节点指针操作
  7. android 内存占用大 卡顿,安卓手机用久了就会卡顿?那是内存使用率高了,你需要这么做...
  8. C++读图片——Mac下对于bmp文件读写读取过大的解决方案
  9. GC之7大垃圾收集器详解(上)
  10. bzoj1083 [SCOI2005]繁忙的都市(最小生成树)
  11. 下 文库 试读_数字资源专题导览 | 科学文库简介
  12. 来自intlsy‘s省选debug方法
  13. 2022年上半年系统分析师上午真题及答案解析
  14. 360浏览器打开Axure
  15. 音视频学习(三)——sip协议
  16. 索尼的hlg是什么_索尼摄像机上的hlg是什么意思
  17. mui 图片预览(3)
  18. java 策略模式会员_设计模式——策略模式:会员价格体系的简单实现
  19. Tecplot中用excel表格绘制xy图的一点理解
  20. 中国天气网免费天气预报接口API

热门文章

  1. 基于jsp的图书管理系统_计算机毕业设计基于JSP书籍租阅管理系统的设计与实现...
  2. python图像相似度识别_一个用SIFT特征比较图像相似度的python小程序
  3. git add 文件夹_Git的下载安装以及基本操作
  4. 【工具软件】webstorm的实用快捷操作(持续积累)
  5. throws throw 自定义异常
  6. python自动化ppt_python自动化怎么操作ppt?
  7. 【动画2】CALayer动画
  8. C/C++利用三元组实现稀疏矩阵运算
  9. 核方法---径向基函数网络
  10. 四个超好用的优质资源搜索网站,海量优质资源等你发现!