记因PHP的内存溢出导致的事故之解决

如果对您有用记得关注,更多干货。

今天上午刚到公司,就有同事在公司群里反映某个计划任务出现问题了。我就怀着刨根问底的心,去查看了log。发现挺有意思的一个问题,PHP内存溢出导致脚本执行失败。那就一起来看个究竟吧!

  1. 首先查看了计划任务的Log

记因PHP的内存溢出导致的事故之解决

从报错信息字面意思可以看出,允许的134217728 bytes的内存已经用尽,还要试图分配12961640 bytes
内存。

给你(当前脚本)分配的内存你已经用完了,你还想问系统要内存。系统这时想对你说:

地主家也没有余粮啊(借用葛优大爷的一句话)

记因PHP的内存溢出导致的事故之解决

  1. 模拟一下"案发现场"

  • 新建一个mem_exhausted.php文件 copy过来一个2.4M的log文件做测试用

  • 写个简单的脚本重现"案发现场" 故意分配1M的内存 来读取2.4M的log

  • 执行脚本,"案发现场"重现

记因PHP的内存溢出导致的事故之解决

记因PHP的内存溢出导致的事故之解决

记因PHP的内存溢出导致的事故之解决

  1. 分析"事故"原因

    脚本一次性读取了大量的数据(可能是读的文件,可能是读取的数据库)

    如下图: 往杯子(分配给当前脚本的内存)里面倒数水(log文件的数据),杯子容量(内存)不够用

  2. 解决方案

    a. 既然杯子小 就换个大杯子(增大给脚本分配的内存)治标不治本: ini_set('memory_limit','100M');

记因PHP的内存溢出导致的事故之解决

记因PHP的内存溢出导致的事故之解决

b. 把水分批次倒入杯子中(循环,分段读取数据,读数据库的话可以用limit)

记因PHP的内存溢出导致的事故之解决

看看结果

记因PHP的内存溢出导致的事故之解决

分段读取也是可以解决问题滴

  1. 其他优化方案

    • 应当尽可能减少静态变量的使用,在需要数据重用时,可以考虑使用引用(&)。

    • 数据库操作完成后,要马上关闭连接;

    • 一个对象使用完,要及时调用析构函数(__destruct())

    • 用过的变量及时销毁(unset())掉

    • 可以使用memory_get_usage()函数,获取当前占用内存 根据当前使用的内存来调整程序

    • unset()函数只能在变量值占用内存空间超过256字节时才会释放内存空间。(PHP内核的gc垃圾回收机制决定)

    • 有当指向该变量的所有变量(如引用变量)都被销毁后,才会释放内存

      (PHP变量底层实现是一个_zval_struct结构体,is_ref__gc表示引用计数 is_ref__gc表示是否为引用)

出处:https://www.cnblogs.com/gaohj/p/6727069.html?utm_source=tuicool&utm_medium=referral

展开阅读全文

关于解决日文字符导致内存溢出的笨办法

04-01

笨是笨了点,不过还挺有用,找出来以后随便你怎么处理了,删除替换或者编码rnrnrnrn<%rnSet rs = Server.CreateObject ("ADODB.Recordset")rnsql = "Select * from news order by id desc"rnrs.Open sql,conn,1,1rn%>rn<%rnrnwhile not rs.eofrnstr=rs("content")rnrnJuncode=replace(str,"ゴ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ガ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ギ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"グ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ゲ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ザ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ジ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ズ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ヅ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"デ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ド","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ポ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ベ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"プ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ビ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"パ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ヴ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ボ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ペ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ブ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ピ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"バ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ヂ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ダ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ゾ","□□□□□□□□□□□□□□□□□□□□□□□□")rnJuncode=replace(Juncode,"ゼ","□□□□□□□□□□□□□□□□□□□□□□□□")rnrnif len(Juncode)<>len(str) thenrn response.write rs("id")rn response.endrnend ifrnrs.movenextrnwendrnrnrs.closernset rs=nothingrnconn.closernset conn=nothing%> 论坛

记一次内存溢出debug

12-10

源码如下rn[code=c]rnvoid GETFILEDLG::OnClickedButton1()rnrn // TODO: 在此添加控件通知处理程序代码rn CString str;rn BROWSEINFO bi;rn rn ZeroMemory(&bi, sizeof(BROWSEINFO));rnrn bi.hwndOwner = GetSafeHwnd();rn bi.lpszTitle = "将文件下载到";rnrn LPITEMIDLIST idl;rnrn idl = SHBrowseForFolder(&bi);rnrn if (idl == NULL)rn rn return;rn rn //ASSERTE( _CrtCheckMemory( ) );rn //char temp[MAX_PATH+1];rn SHGetPathFromIDList(idl, str.LockBuffer()); //溢出rn //SHGetPathFromIDList(idl, temp);rn //str = temp;rn GetDlgItem(IDC_EDIT_DIR)->SetWindowText(str.LockBuffer());rnrnrn CRemoteContrlDlg::SetPubDir(str);//bug:heap corruption detected rnrn[/code]rnrn主要功能为通过点击button->弹出浏览对话框,选择->获得选择项->转换为路径rn报错如下:rn[img=https://img-bbs.csdn.net/upload/201512/10/1449752241_665103.png][/img]rnrn经查询为内存溢出错误,使用断言ASSERTE( _CrtCheckMemory( ) );rn确定错误发生在SHGetPathFromIDList(idl, str.LockBuffer()); rnMSDN中对第二个参数的描述为rnPointer to a buffer to receive the file system path. This buffer must be at least MAX_PATH characters in sizern改正见源码注释rnrn说明:这段代码时间比较久了,其在vc6.0中并没有崩溃rnrn现有问题:rn1关于Cstring::LockBuffer我并没有在MSDN中找到说明,在CstringT中也一样rn2我猜测Cstring::LockBuffer返回实际值:str.lockbuffer()返回一个字符串指针,但是大小为0,所以赋值的时候会溢出,我的猜测对吗?rnrnrn新手一枚,欢迎讨论[img=https://forum.csdn.net/PointForum/ui/scripts/csdn/Plugin/003/monkey/27.gif][/img]rn 论坛

Spring导致内存溢出问题

12-17

各位看官,我先描述下问题背景:rn 我的应用运行了两年多,期间内存溢出每个星期2次左右,内存溢出后内存又恢复正常,内存溢出不影响 正常使用,内存溢出不宕机,java进程也不变,Tomcat没有重启。rn 因为不影响使用,也就一直没有管,但是近期发现CPU使用率特别高,不管都不行了。rn 排查方法:http://www.blogjava.net/hankchen/archive/2012/08/09/377735.htmlrn 很好用,定位了问题代码也:rn [code=java]public String getActionName(HttpServletRequest request) rn String _actionName = null;rn if ( null != request && null != this.methodNameResolver ) rn try rn _actionName = this.methodNameResolver.getHandlerMethodName(request);rn catch (NoSuchRequestHandlingMethodException e) rn logger.error("获取请求方法名称时失败!", e);rn rn rn return _actionName;rn [/code]rn 问题出在第5行代码,查看Spring源码发现,是这句代码:rn[code=java]String servletPath = (String) request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE);[/code]rn 我测试了下,发现从request.getAttribute中取数据就会特别慢,CPU就高,然后我写了测试代码rn [code=java]rn int count = 0;rn Enumeration em = request.getAttributeNames();rn while(em.hasMoreElements())rn count++;rn String name = em.nextElement().toString();rn logger.info("name:"+name+",value:"+request.getAttribute(name).toString());rn System.out.println("name:"+name+",value:"+request.getAttribute(name).toString());rn rn if(count>20)rn logger.info("====================================================count:"+count+",URI:"+uri);rn [/code]rn 结果第3行一直占用CPU,我纠结了,什么情况?rn 以上几段代码描述的是问题一,CPU占用高。rn 我换了个写法,暂时绕开了这个问题,但是没有解决,求高人指点,代码如下:rn[code=java] public String getActionName(HttpServletRequest request) rn String _actionName = null;rn if(null != request)rn String uri = request.getRequestURI();rn _actionName = uri.substring(uri.lastIndexOf("/")+1,uri.indexOf(".do")); rn rn return _actionName;rn [/code]rn 在这里主要是请教下内存溢出问题,内存溢出是使用ibm memory analyzer工具分析的,分析了五六个内存溢出文件,都是同一个问题,最多的对象是java.lang.Object[293479271] @ 0x708309d20 是个Object数组,数组中存放的都是org.springframework.web.servlet.DispatcherServlet.LOCALE_RESOLVER 或者 org.springframework.web.servlet.DispatcherServlet.THEME_RESOLVER。全部是spring request Attribute 中的某个key值,每次溢出都是包含几百万个key(某一个key,重复的)rn[img=https://img-bbs.csdn.net/upload/201312/17/1387280076_538438.jpg][/img]rn Object数组中存放的数据见下图:rn[img=https://img-bbs.csdn.net/upload/201312/17/1387280145_932082.jpg][/img]rn rn 开始以为是存在内存泄漏,我输出gc日志,查看内存很平稳,只是忽然下就内存溢出了,而且溢出时间没有规律,非高峰期,有时都是凌晨3点、5点,根本很少人访问。用gcviewer工具分析gc日志,截图如下:rn[img=https://img-bbs.csdn.net/upload/201312/17/1387278701_789749.jpg][/img]rn 最高的那根线就是内存溢出了,之后自己就恢复了。rn 分析了好几天,也没有思路了,但是总是觉得cpu高和内存溢出是同一个问题导致的,内存溢出时,内存中存放的都是spring request Attribute 中的某个key值,而cpu高,是从request Attribute中取值。难道是Attribute中存放了几百万个key?rn 麻烦大家帮忙看看,给个思路也好啊,多谢了! 论坛

没有更多推荐了,返回首页