内存泄漏是什么意思(内存泄漏的原因及解决办法)
什么是内存泄露?
内存泄漏( Memory Leak )是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费;导致程序运行速度减慢甚至系统崩溃等严重后果
内存泄漏缺陷具有隐蔽性、积累性的特征; 比其他内存非法访问错误更难检测,因为内存泄漏的产生原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷;此外,内存泄漏通常不会直接产生可观察的错误症状,而是逐渐积累,降低系统整体性能,极端的情况下可能使系统崩溃
随着计算机应用需求的日益增加,应用程序的设计与开发也相应的日趋复杂; 开发人员在程序实现的过程中处理的变量也大量增加,如何有效进行内存分配和释放,防止内存泄漏的问题变得越来越突出
例如: 服务器应用软件,需要长时间的运行,不断的处理由客户端发来的请求; 如果没有有效的内存管理,每处理一次请求信息就有一定的内存泄漏;这样不仅影响到服务器的性能,还可能造成整个系统的崩溃;因此,内存管理成为软件设计开发人员在设计中考虑的主要方面
内存泄漏的危害
长时间运行,程序变卡,性能严重下降
程序莫名其妙挂掉
OutOfMemoryError错误
乱七八糟的错误,还不易排查
内存泄漏原因
以产生的方式来分类,内存泄漏可以分为四类:
1 、常发性内存泄漏
发生内存泄漏的代码会被多次执行到,每次被执行时都会导致一块内存泄漏
2 、偶发性内存泄漏
发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生;常发性和偶发性是相对的; 对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要
3 、一次性内存泄漏
发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块且仅有一块内存发生泄漏
4 、隐式内存泄漏
程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存; 严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存;但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存 所以,我们称这类内存泄漏为隐式内存泄漏
从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在; 真正有危害的是内存泄漏的堆积,这会最终耗尽系统所有的内存;从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到
总之内存泄漏原因太多了; 说不定就是某一行代码不对就会出现这种情况,关键的还是如何找出哪个地方出现了内存泄漏,代码好修改,错误不易查
代码运行结果如下:
大量使用静态变量
静态变量的生命周期与程序一致;因此常驻内存
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n37" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class StaticTest { public static List<Integer> list = new ArrayList<>(); public void populateList() { for (int i = 0; i < 10000000; i++) { list.add((int)Math.random()); } System.out.println("running......"); } public static void main(String[] args) { System.out.println("before......"); new StaticTest().populateList(); System.out.println("after......"); } }</pre>现在可以使用jvisualvm运行一边,看看内存效果
- 带static关键字(使用静态变量)
从上图可以看到,堆内存从一开始的135M左右飙升了到了200M。直接占据了65M的内存。
- 不使用static关键字(不使用静态变量)
由于全局变量与程序周期不一致,因此不使用时,就会进行回收。此时内存最高150M。
总结:由于静态变量与程序生命周期一致,因此对象常驻内存,造成内存泄漏
连接资源未关闭
每当建立一个连接,jvm就会为这么资源分配内存。比如数据库连接、文件输入输出流、网络连接等等
<pre mdtype="fences" cid="n64" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class FileTest { public static void main(String[] args) throws IOException { File f=new File("G:\\nginx配套资料\\笔记资料.zip"); System.out.println(f.exists()); System.out.println(f.isDirectory()); } }</pre>依然使用jvisualvm运行一边,看看内存效果。
可以看出,在连接文件资源时,jvm会为本资源分配内存
ThreadLocal的错误使用
ThreadLocal主要用于创建本地线程变量,不合理的使用也有可能会造成内存泄漏
上面这张图详细的揭示了ThreadLocal和Thread以及ThreadLocalMap三者的关系
1、Thread中有一个map,就是ThreadLocalMap
2、ThreadLocalMap的key是ThreadLocal,值是我们自己设定的
3、ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收
重点来了,ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象; 那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏
解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况
如何避免内存泄露?
确保没有在访问空指针
每个内存分配函数都应该有一个 free 函数与之对应,alloca 函数除外
每次分配内存之后都应该及时进行初始化,可以结合 memset 函数进行初始化,calloc 函数除外
每当向指针写入值时,都要确保对可用字节数和所写入的字节数进行交叉核对
在对指针赋值前,一定要确保没有内存位置会变为孤立的
每当释放结构化的元素(而该元素又包含指向动态分配的内存位置的指针)时,都应先遍历子内存位置并从那里开始释放,然后再遍历回父节点
始终正确处理返回动态分配的内存引用的函数返回值
尾述
代码层面的检查可以帮助发现部分内存泄漏的问题,但是生产环境中的内存泄漏往往不容易提前发现,因为很多问题是在大并发场景下才会出现;因此还需要通过压力测试工具进行压力测试,提前发现潜在的内存泄漏问题
私信 发送 “底层源码” 即可 获取 完整代码 以及 更多Android学习笔记+源码解析+面试视频
技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面
其他文章
- 二维动画制作流程(动画制作教程分享)
- 均衡器调节最佳效果(音响均衡器最佳调试图)
- 打印机提示打印错误怎么解决(打印机无法打印原因及解决办法)
- 宽带光猫和路由器怎么连接(关于光猫和路由器的连接方法)
- 客群指数是什么意思(客流统计和客流分析)
- ps如何将图片转为矢量图(位图矢量图转换教程)
- 在360隔离沙箱中运行怎么办(隔离沙箱无法连接网络的处理)
- 聚划算整点聚怎么抢(聚划算抢购秒杀软件)
- 国外室内设计网站都有哪些(设计师必上的十个网站)
- lol怎么录视频(三个步骤教你录屏)
- iso光盘映像文件怎么打开(虚拟机iso镜像文件的使用)
- 怎么拍短视频好看(小视频拍摄技巧)
- 鲁大师节能降温有用吗(鲁大师节能模式的作用)
- 淘宝怎样群发信息(淘宝群发信息操作步骤)
- 视频怎么拼图合成(分享视频拼接的两种方法)
- ps储存为web格式是灰色的怎么办(ps导出旧版web不能用的原因)
- 如何合并磁盘到C盘(硬盘分区的合并技巧)
- 抖音月付怎么取消关闭(教你关闭抖音月付功能)
- ai如何把图片变成矢量图(教你两种提前图片方法)
- 支付宝邮箱注册入口(邮箱注册支付宝账号的流程)
- 2023年vn出装顺序(详解lol手游国服vn最强出装)
- 天猫秒杀技巧是什么(分享淘宝卡点抢东西诀窍)
- xp如何关闭445端口(手动关闭445端口操作教程)
- 华硕主板声卡驱动设置(五步教你搞定声卡驱动)
- 运放积分电路的作用(运放电路的工作原理)
- macbookair使用教程入门(苹果电脑使用入门教程)
- 微信背景变黑色了怎么恢复(微信使用方法与技巧)
- 国外代购网站有哪些(分享六大海外货源平台)
- 电脑密码忘记了怎么解开(忘记开机密码简单的解除方法)
- 怎么从图片提取电子签名(图片生成电子签名的方法)