实例介绍
OutOfMemoryError_8种典型案例分享,定位java内存问题
public class OOM t static final int SIZE=2*1024*1024 public static void main(String] a)i int[] i new int[SIzE] 内存泄漏示例 这个示例更真实一些。在Jaa中,创建一个新对象时,例如 ntegernum= new Integer(5);},并不需要手动分配 内存。因为JM自动封装并处理了内存分配在程序执行过程中,JVM会在必要时检查内存中还有哪些对象仍在使 用,而不再使用的那些对象则会被丢弃,并将其占用的内存回收和重用。这个过程称为垃圾收集.JVM中负责垃圾回 收的模块叫做垃圾收集器(GC。 Java的自动内存管理依赖Gc,GC会一遍乂一遍地打描内存区域,将不使用的对象删除.简单来说,Java中的内存泄 漏,就是那些逻辑上不再使用的对象,却没有被垃圾收集程序给干掉.从而导致垃圾对象继续占用堆内存中,逐 渐堆积,最后产生 java. lang. OutOfMemory Erro: Java heap space错误。 很容易写个BUG程序,来模拟内存泄漏: import java. util. * public class Keylessentry i static class Key t Integer id; Key(integer id)t this id= id: @Override public int hashCode oi return id hashCode oj public static void main(String[] args)i Map m= new HashMap(); While(true)t for (int i =0; i< 10000 ){ if(!m containsKey(new Key(i)))t mput(new Key (i),"Number: "+1); System. out. println("m size(="+m size) 粗略一看,可能觉得没什么问题,因为这最多缓存10000元素嘛但仔细审查就会发现,Key这个类只重写了 hashcode()方法,却没有重写 equals()方法,于是就会一直往 HashMap中添加更多的Key 请参考:aya中 t h code与gus方法的约定及重写原则 随着时间推移," cached"的对象会越来越多.当泄漏的对象占满了所冇的堆內存,GC又清理不了,就会抛出 java,ang. Out OfMemory Erro: Java heap space错误。 解决办法很简单,在Key类中恰当地实现 equals()方法即可: Override ublic boolean equals(object o)t boolean response false if (o instanceof Key)i response =(((Key)o).id)equals (this id); return response; 说实话,在寻找真正的内存泄漏原因时,你可能会死掉很多很多的脑细胞。 个 SpringMVc中的场景 译者曾经碰到过这样一种场景 为了轻易地兼容从 Struts2迁移到 SpringMVO的代码,在 Controller中直接获取 request 所以在 ControllerBase类中通过 hreadLocal缓存了当前线程所持有的 request对象 public abstract class ControllerBase i privatestaticThreadlocal<httpservletRequest>requestthreadlOcal=new Threadlocal<httpservlEtrequest>() public static HttpservletrequeSt getrequestoT return requestThreadLocal geto; public static void setrequest(hTtpservletrequest request if(null =z request) requestThreadLocal remove (i requestThreadLocal. set(request); 然后在 SpringMVC的拦截器 ntercept现类中,在 rehAndle方法里,将 request对象保存到 Threadlocal中 *登录拦截器 public class LogincheckInterceptor implements HandlerInterceptor i private List<string> excludelist new ArrayList<String>(; public void setExcludeList(List<string> excludeList)t this. excludelist excludelist privatebooleanvaliduri(httpservleTreqUestrequest //如果在排陰列表中 String uri request. getRequestURIO; Iterator<String> iterator excludeList iterator; while(iterator hasNext)t String eXURI iterator, nextO; if(null i= eXURI & uri contains(eXURI)[ return true; //可以进行登录和权限之类的判断 LoginUser user- ControllerBase getLoginUser(request); if(null != user)f return true //未登录,不允许 return false privatevoidinitrequestthreaDlocal(httpservletrequestrequest Controller Base setRequest(request) request setAttribute ("basePath", Controller Base base PathLessslash(request)i private void removeRequestThread(t ControllerBase. setRequest(null); @override public boolean prehandle(hTtpservletrequest request HttpservletrespOnse response Object handler throws Exception t initRequestThreadLocal(request); //如果不允许操作,则返回fal5e即可 if(false = validURI(request))t //此处抛出异常,允许进行异常统一欠理 throw new Need LoginException () return truei @override public void posthandle(httP servletrequest request Httpservletresponse response obJect handler Modelandview modelandview) throws Exception i removeRequestThreadLocalo; @override public void aftercompletion(httP servletrequest request Httpservletresponse response obJect handler exCeption ex) throws Exception I removeRequestThreadLocal() 在 portAnd1e和 afterCompletion方法中,清理 Threadlocal中的 request对象。 但在实际使用过程中,业务开发人员将一个很大的对象(如占用内存200MB左右的Lst)设置为 request的 Attributes,传递到JSP中 JSP代码中可能发生了异常,则 SpringMVC的 postHand1e和 afterCompletion方法不会被执行 Tomcat中的线程调度,可能会一直调度不到那个抛出了异常的线程,于是 Threadlocal一直hold住 request随着 运行时间的推移,把可用内存占满,一直在执行FGC,系统直接卡死。 后续的修正:通过 Filter,在 finall!y语句块中清理 Threadloca。 @WebFilter(value="/*, async Supported=true) public class ClearRequestCacheFilter implements Filter[ @Override public void doFilter(ServletRequest request, ServletResponse response, Filter chain chain) throws IOException ServletException t clear ControllerBaseThreadLocal(; chain. doFilter (request, response) t finally i clearControllerBaseThreadLocalo; private void clearControllerBaseThreadLocal() t ControllerBase. setRequest(null); Override public void init(FilterConfigfilterConfig) throws ServletException [I @override public void destroy o f 教训是:可以使用 Threadlocal,但必须有受控制的释放措施、一般就是try-f1n311y的代码形式 说明: SpringMVC的 Controller中,其实可以通过(@ Autowired注入 request,实际注入的是一个 Httpservletrequestwrapper对象,执行时也是通过 Threadlocal机制调用当前的 request. 常规方式:直接在 controller方法中接收 request参数即可 解决方案 如果设置的最大内存不满足程序的正常运行,只需要增大堆内存即可,配置参数可以参考下文。 但很多情况下,增加堆内存空间并不能解决问题。比如存在内存泄漏,增加堆内存只会推迟 java,lang. Out OfMemory Error.: Java heap space错误的触发时间。 当然,增人堆内存,可能会增加 GC pauses的时间,从而影响程序的吞叶量或延迟。 如果想从根本上解决问题,则需要排査分配内存的代码.简单来说,需要解决这些问题 1.哪类对象占用了最多内存? 2.这些对象是在哪部分代码中分配的 要搞清这一点,可能需要好儿人时间。下面是大致的流程: 获得在生产服务器上执行堆转储( heap dump)舶杖限。“转储(Dump)是堆内存的快照,稍后可以用于内存分析 这些快照中可能含有杋密信息,例如密码、信用卡账号等,所以有时候,由于企业的安全限,要获得生产环境 的堆转储并不容易。 ·在适当的时间执行堆转储。一般来说,内冇分析需要比对多个堆转储文件,假如获取的时机不对,那就可能是 个废”的快照.另外,每次执行堆转储,都会对JVM进行“冻结”,所以牛产环境中,也不能执行太多的Dump操作,否 则系统缓慢或者卡死,你的麻烦就大了 ·用另一台机器来加载Dump文件。一般来说,如果出问题的JM内存是8GB,那么分析 Heap Dump的机器内存 需要大」8GB.打廾转储分析软件(我们推荐 Eclipse MAT,当然你乜可以使用其他工具)。 ·检测快照中占用内存最大的 GC roots。详情请参考: Solving OutofMemory Error(at6- Dump is not a waste.。这对新手来说可能有点困难,但这也会加深你对堆内存结构以及 navigation机制的理解。 ·接下来,找出可能会分配大量对象的代码.如果对整个系统非常熟悉,可能很快就能定位了 打个广告,我们推荐 Plumb, the only Java monitoring solution with automatic root cause detection。 Plumb 能捕获所有的java,lang. OutOfMemoryError,并找岀其他的性能间题,例如最消耗内存的数据结构等等。 Plumb在后台负责收集数据—包括堆内存使用情况(只统计对象分布图,不涉及实际数据),以及在堆转储中 不容易发现的各种问题。如果发生 java. lang. OutofMemory Error,还能在不停机的情况下,做必要的数据处 理.下面是Pumb对一个java|ang. Out OfMemoryError的提醒 When an application throws an outofMemory Error: Java heap space. it means that the ava heap was completely filled with objects and it was not possible to allocate anything new. Plumb analyses the heap conten s to find the most l kely culprits of this Thcre was 1 major consumer of hoap sparc that oooupiod 174 MB out of tho 248 MB of used heap. o Active thread of class com. example. util. HealthMonitor field proxyservice holds oDject of class com. example. spiimpl. ProxyserviceImpl o field registries holds object of class c e s i ProxyserviceImplsProxyRegistry o fie d service holds object af class com. example.map impl. Mapservice If field mapService Context holds object of class com. example, map impl. DefaultMap Service Context-174 MB o ficd partitionContainers holds object of class com. example. map impl. Partition Container[] o holds 271 instances of cl 99% of them were allocated in method getMetrics of c ass com. example. emmonitorrest, service. impl. MetricMangerImpl at line 30 1% of th ist of class at line 27 强大吧,不需要其他工具和分析,就能直接看到: ·哪类对象占用了最多的内存(此处是271个 com. example. map.mp. Partitioncontainer实例,消耗了173MB内 存,而堆内存只有248MB 这些对象在何处创建(大部分是在 MetricManagerImp/类中,第304行处) 当前是谁在引用这些对象(从 GC root开始的完整引用链) 得知这些信息,就可以定位到问题的根源,例如是当地精简数据结构/模型,只占用必要的内冇即可 当然,根据内存分析的结果,以及 Plumb生成的报告,如果发现对象占用的内存很合理,也不需要修改源代码的话,那 就增大堆内存吧。在这种情况下,修改JM启动参数,(按比例)增加下面的值 -Xmx1024m 这里置Java堆内存最大为1024B。可以使用B/6表示GB,m/M代表MB,k/K表示KB 下面的这些形式都是等价的,设置Java堆的最大空间为1GB #等价形式:最大1GB内存 java -Xmx1073741824 com mycompany. MyClass java -Xmx1048576k com, my company. Myclass java -Xmx1024m com my company. Myclass java -Xmx1g com mycompany. Myclass OutofMemory Error系列(2): GC overhead limit exceeded Java运行时环境内置了垃圾收集G模坎.上一代的很多编程语言中并没有自动内存冋收机制,需要程序员手工编 写代码来进行内存分配和释放,以重复利用堆内存 在Java稈序屮,只需要关心内存分配就行。如果某块內存不再使用,垃圾畋集( Garbage Collection模块会自动执行 清理。GC的详细原理请参考GC性能优化系列文章,一般来说,JVM内置的垃圾收集算法就能够应刈绝大多数的业 务场景。 java/ang, Out Error: GC overhead limit exceeded这种情况发生的原因是,程序基本上耗尽了所有的可 用内存,GC也清理不了 原因分析 JVM拋出 java. lang Out OfMemory Error.: GC overhead limit exceeded错误就是发出了这样的信号:执行垃圾收集的 时间比例太大,有效的运算量太小.默认情况下,如果GC花费的时间超过98%,并且GC回收的内存少于2%,JVM就 会抛出这个错误。 Time used by GC pauses 98 2% 注意,java.lang. Out OfMemory Error: GC overhead limit exceeded错误只在迕续多次sc都只回收了不到2%的极端 情况下才会抛出。假如不抛出 OVerhead1imit错误会发牛什么情况呢?那就是G清理的这么点内存很快会再 次填满,迫使GC再次执行.这样就形成恶性循环,CPU使用率一直是100%,而GC却没有任何成果.系统用户就会看 到系统卡死-以前只需要几毫秒的操作,现在需要好几分钟才能完成。 这也是一个很好的快速失败原则的案例。 示例 以下代码在无限循环屮往Map里添加数据。这会导致“ GC overhead limit exceeded"错误: package com encounter. rtime; mport java util. Map; import java. util Random; public class Testwrapper i public static void main(String args[l) throws Exception I Map map= System. getProperties(; Random r= new Random o; while (true)( map. put(r. nextInto),"value"); 配置JVM参数:-Xmx12m。执行时产生的错误信息如下所示 Exception in thread "main"java, lang. OutofMemoryError: Gc overhead limit exceeded at java util. Hashtable. add Entry(Hashtable. java: 435) at java util. Hashtable put(Hashtable. java: 476) at com encounter rtime. Testwrapper main(Testwrapper java: 11) 你碰到的错误信息不一定就是这个。确实,我们执行的VM参数为 java -Xmx12m -XX: +UseParallelGc Testwrapper 很快就看到了 java/ang. OutOfMemory Error: GC overhead limit exceeded错误提示消息。但实际上这个示例是有 些坑的.因为配置不同的堆内存大小,选用不同的GC算法,产生的错误信息也不相同。例如,当Java堆内存设置为 10M时: java -Xmx10m -XX: +UseParallelGc Testwrapper DEBUG模式下错误信息如下所小 Exception in thread"main" java, lang. outofMemory Error: Java heap space at java util. Hashtable rehash(Hashtable. java: 401) at java util. Hashtable. add Entry (Hashtable. java: 425) at javautil. Hashtable put(Hashtable. java: 476) at com encounter rtime. Testwrapper main(Testwrapper java: 11) 读者应该试着修改参数,执行看看具体。错误提示以及堆栈信息可能不人一样。 这里在Map进行 rehash时抛出了间eva,lang. OutofMemory Error: Java heap space错误消息,如果使用其他拉圾 收集算法,比如ⅩⅩ+ UseConcMark SweepGC,或者ⅩX+UseG1GC,错误将被默认的 exception handler所获, 但是没有 stacktrace信息,因为在创建 Exception时没办法填充 stacktrace信息 例如配置: Xmx12m -xX: +Useg1GC 在Win7x64,Java8环境运行,产生的错误信息为 【实例截图】
【核心代码】
标签:
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明
网友评论
我要评论