当前位置: 首页 > 编程日记 > 正文

Classloader内存泄露

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

最近遇到了这个问题,在修改了-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

相关文章:

Springboot + oauth2 单点登录 - 原理篇

OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。授权码模式(authorization code)密码模式(resource owner password credentials)客户端模式(client credentials) 不常用。

Java 类型判断方法

Java 类型判断方法有三种,分别是instanceof是关键字,isInstance和isAssignableFrom是Class中的方法。> cls);

Docker-Compose搭建单体SkyWalking 6.2

SkyWalking简介 SkyWalking是一款高效的分布式链路追踪框架&#xff0c;对于处理分布式的调用链路的问题定位上有很大帮助 有以下特点&#xff1a; 性能好 针对单实例5000tps的应用&#xff0c;在全量采集的情况下&#xff0c;只增加 10% 的CPU开销。支持多语言探针支持自动及手…

gprof 性能优化工具

gprof用于分析函数调用耗时&#xff0c;可用gprof分析最耗时的函数&#xff0c;以便优化程序。 gcc链接时也一定要加-pg参数&#xff0c;以使程序运行结束后生成gmon.out文件&#xff0c;供gprof分析。 gprof默认不支持多线程程序&#xff0c;默认不支持共享库程序。 gcc 编…

基于Metronic的Bootstrap开发框架经验总结(7)--数据的导入、导出及附件的查看处理...

在很多系统模块里面&#xff0c;我们可能都需要进行一定的数据交换处理&#xff0c;也就是数据的导入或者导出操作&#xff0c;这样的批量处理能给系统用户更好的操作体验&#xff0c;也提高了用户录入数据的效率。我在较早时期的EasyUI的Web框架上&#xff0c;也介绍过通过Exc…

resin php,resin竟然开始支持PHP

真是好消息&#xff0c;resin开始支持PHP了&#xff0c;早上查资料&#xff0c;意外的看到了这个消息。由Resin 3.0.17开始&#xff0c;Resin里多了一个称为Quercus的东西&#xff0c;Quercus其实就是用Java实作的PHP语言模组。一直以来WEB语言都是各自为政&#xff0c;都有自己…

NSD WINDOWS--2014.8.11

实验01&#xff1a;不同网段的计算机远程配置交换机实验目标&#xff1a;实现不同网段的计算机远程配置交换机实验环境:实验步骤&#xff1a;一、分别配置pc0和pc1的ip地址网关二、配置交换机的管理ip地址和网关三、配置交换机远程管理密码和enable密码四、配置路由器的ip地址五…

day1-数据库基础

数据库基本概念 数据库是什么 数据库是用来存储数据的仓库&#xff0c;本质上就是一套基于CS架构的服务端和客户端程序&#xff0c;最终将数据存储在服务器端的磁盘中。之前学过的数据存储方式有&#xff1a; 列表 字典......等等&#xff0c;是在内存中的数据&#xff0c;缺点…

双重指针作为函数参数的妙用

双重指针作为函数参数&#xff0c;可以在函数函数内部修改外部指针的值。主要用法包括&#xff1a; 1. 在函数内部分配内存&#xff0c;作为函数参数返回&#xff1b; 2. 在函数内部设置指针为空&#xff1b; #include <stdio.h> #include <stdlib.h> #include …

什么是常函数?

类的成员函数后面加 const&#xff0c;表明这个函数不会对这个类对象的数据成员&#xff08;准确地说是非静态数据成员&#xff09;作任何改变。 在设计类的时候&#xff0c;一个原则就是对于不改变数据成员的成员函数都要在后面加 const&#xff0c;而对于改变数据成员的成…

matlab整型和浮点的区别,技术帖 | 心理学MATLAB初学者教程--简单数据类型介绍(逻辑型数据,整型/浮点型数据,字符型)......

1.2.1 什么是变量和变量名变量这个词似乎在许多地方都有出现&#xff0c;在计算机中变量是指是一段有名字的连续存储空间(摘自百度百科)&#xff0c;而这个所谓的名字就叫做变量名。举个例子说现在我们再MATLAB的命令窗口中输入&#xff1a;A 1 然后按回车&#xff0c;我们会看…

为窗口添加滚动条事件

为窗口添加滚动条事件其实非常的简单&#xff0c; window.οnscrοllfunction(){};注意在获取滚动条距离的时候谷歌不识别document.documentElement.scrollTop&#xff0c;必须要加上document.body.scrollTop&#xff1b;即var scrolltopdocument.documentElement.scrollTop||d…

当远程桌面到Windows终端服务器,出现终端服务器超出了最大允许连接数,怎么办...

如果是老版本的MSTSC则使用 MSTSC /console /v:ip如果是新版本 MSTSC /admin /v:ip转载于:https://www.cnblogs.com/SharkXu/archive/2012/10/08/MSTSC.html

连续地址数据(数组或者malloc的内存)作为函数参数

在编程时&#xff0c;一簇连续的内存单元&#xff0c;比如数组或者malloc的内存块,如下的数组a 或者指针p. int a[4]; int *p malloc(4 * sizeof(int)); 我们要想修改上述连续的内存块&#xff0c;可以写一个函数(一维指针做参数传入起始地址即可&#xff09;来搞定。 If …

Linux 文件系统及 ext2 文件系统

linux 支持的文件系统类型 Ext2:有点像 UNIX 文件系统。有 blocks,inodes,directories 的概念。Ext3:Ext2 的加强版&#xff0c;添加了日志的功能。支持 POSIX ACL(Access control Lists,访问控制列表) 。Isofs(iso9660):CDROM 文件系统。Sysfs:基于 ram 的文件系统&#xff0c…

php每分钟刷新一次的验证码,php如何在进入页面的时候自动刷新一次验证码

页面直接进入login页面的时候无法获取到$_SESSION["auth"]值&#xff0c;只能刷新一次验证码才能获取到值&#xff0c;怎么才能在用户访问的时候自动刷新一次验证码&#xff0c;求大神支招。。。验证码回复讨论(解决方案)哪里获取到$_SESSION["auth"]值&am…

vs2010快捷方式

【窗口快捷键】 CtrlW,W: 浏览器窗口 CtrlW,S: 解决方案管理器 CtrlW,C: 类视图 CtrlW,E: 错误列表 CtrlW,O: 输出视图 trlW,P: 属性窗口 CtrlW,T: 任务列表 CtrlW,X: 工具箱 CtrlW,B: 书签窗口 CtrlW,U: 文档大纲 CtrlD,B: 断点窗口 CtrlD,I: 即时窗口 CtrlTab: 活…

移动端rem屏幕设置

//修改页面title var pageTitledocument.getElementsByTagName("title")[0].innerHTML; if(location.href.indexOf("index.html")>-1 || location.href.indexOf("html")-1){document.getElementsByTagName("title")[0].innerHTML&q…

二维指针操作链表

背景 Linus slashdot: https://meta.slashdot.org/story/12/10/11/0030249 Linus大婶在slashdot上回答一些编程爱好者的提问&#xff0c;其中一个人问他什么样的代码是他所喜好的&#xff0c;大婶表述了自己一些观点之后&#xff0c;举了一个指针的例子&#xff0c;解释了…

php多选框怎么传值,tp3.2如何处理多选框传参和判断状态

创建多选框&#xff1a;(1)普通的多选&#xff1a;123(2)在数据库中遍历出来的多选框,value和data-id都要赋值(大家都懂&#xff0c;我就不说啦)&#xff1a;{$vo.title} 2.我是用jq做的异步,我是使用英文“&#xff0c;”分割字段存储的。我们的前端是自己封装过的,大家根据自…

UIWebView和UIActivityIndicatorView的结合使用

环境&#xff1a;Xcode6.1 UIWebView是iOS开发中常用的一个控件&#xff0c;是内置的浏览器控件&#xff0c;我们可以用它来浏览网页&#xff0c;加载文档等。这篇文件将结合UIActivityIndicatorView控件制作一个小实例(加载apple的官网)。效果如下&#xff1a; 一.建立一个Sin…

Python对象类型——字符串、列表、元组

字符串Python连接多个字符串可用“”号&#xff0c;但这个操作不如把所有子字符串放到一个列表或可迭代对象中&#xff0c;然后调用一个join方法来把所有内容连接在一起节约内存。原始字符串操作符&#xff08;R/r&#xff09;&#xff1a;相当于取消“\”转义&#xff0c;在使…

leetcode--1:(python)Two Sum

2019.5.25: #1 Given an array of integers, return indices of the two numbers such that they add up to a specific target. You may assume that each input would have exactly one solution, and you may not use the same element twice. Example: 我的解法&#xff1…

memset初始化内存

在suse 9,运行man memset 查看memset的解释如下&#xff1a; “NAME memset - fill memory with a constant byte SYNOPSIS #include <string.h> void *memset(void *s, int c, size_t n); DESCRIPTION The memset() function fills the fir…

php 数字变量,php入门变量之数字_PHP教程

在介绍变量时&#xff0c;我明确指出PHP具有整型和浮点型(小数)数字类型。但是&#xff0c;依据我的经验&#xff0c;这两种类型都可以归类到一般的数字之下(在极大程度上是这样的)。下面列举下PHP中有效的数字类型的变量&#xff1a;83.1410980843985-4.23985084.4e2注意&…

BZOJ 1597: [Usaco2008 Mar]土地购买( dp + 斜率优化 )

既然每块都要买, 那么一块土地被另一块包含就可以不考虑. 先按长排序, 去掉不考虑的土地, 剩下的土地长x递增, 宽y递减dp(v) min{ dp(p)xv*yp1 }假设dp(v)由i转移比由j转移优(i>j), 那么dp(i)xv*yi1 < dp(j)xv*yj1化简得 (dp(i) - dp(j))/(yi1-yj1) > -xv然后就斜率优…

雨季来临 对车辆涉水说“NO”

七月的上海开始进入暴雨频发的季节。在城市排水系统受到考验的同时&#xff0c;车主们的车辆也同样经受着雨水的考验。而每年都会有相当一部分车辆因为“水害”&#xff0c;使车辆自身的价值受到很大的影响。为此开新通过以下案例为大家做个分析。并推荐几个实用的技巧以备不时…

C#编写dll进行sql server数据库扩展储存过程

一、编写C#函数文件 1、新建一个类库文件 备注&#xff1a;sqlserver 2008只能用.net3.5版本。 2、如有想加入强命名的话可如下步骤&#xff1a; 参考博文&#xff1a;https://blog.csdn.net/donnie88888888/article/details/52743064 1、运行在“开始菜单”-“程序”-“Micros…

malloc(0)-malloc 0 字节

C17中有如下描述&#xff1a; 7.22.3 Memory management functions 1 The order and contiguity of storage allocated by successive calls to the aligned_alloc, calloc, malloc, and realloc functions is unspecified. The pointer returned if the allocation succeeds …

php常见排序算去,PHP兑现常见排序算法

PHP实现常见排序算法//插入排序(一维数组)function insert_sort($arr){$count count($arr);for($i1; $i$tmp $arr[$i];$j $i - 1;while($arr[$j] > $tmp){$arr[$j1] $arr[$j];$arr[$j] $tmp;$j--;}}return $arr;}//选择排序(一维数组)function select_sort($arr){$coun…