java内存泄漏

java中的内存泄漏

可参考文章:https://mp.weixin.qq.com/s?__biz=MzA3ODQ0Mzg2OA==&mid=2649048578&idx=1&sn=b3deed16b62389d77851d165c5e644c8&chksm=87534231b024cb272465bd8f9a857e81b87d542aed07db438c7e7c72306ebe6157b69726bae5&scene=21#wechat_redirect

1.java中采用的垃圾处理的算法是,

根可达算法,引用计数法
2.常见的垃圾算法:
标记清除(mark sweep) - 位置不连续 产生碎片 效率偏低(两遍扫描)
拷贝算法 (copying) - 没有碎片,浪费空间
标记压缩(mark compact) - 没有碎片,效率偏低(两遍扫描,指针需要调整)
垃圾处理的区域为堆处理区,所以发生内存泄漏的区域也为堆区域
3.内存泄漏的症状:
应用程序长时间连续运行时性能严重下降
应用程序中的OutOfMemoryError堆错误
自发且奇怪的应用程序崩溃
应用程序偶尔会耗尽连接对象
3-1,由static字段引起的内存泄漏:
    静态字段通常拥有与整个应用程序相匹配的生命周期(除非 ClassLoader复合垃圾回收的条件)。

预防内存泄漏:

最大限度地减少静态变量的使用
使用单例时,依赖于延迟加载对象而不是立即加载的方式

3.2未关闭的资源导致的内存泄漏
每当我们创建连接或打开一个流时,JVM都会为这些资源分配内存。例如数据库连接,输入流或者会话对象。
忘记关闭这些资源会导致持续占有内存,从而使他们无法GC。如果异常阻止程序执行到达处理关闭这些资源的代码,则甚至可能发生这种情况。
在任一种情况下,资源留下的开放连接都会消耗内存,如果我们不处理他们,他们可能会降低性能,甚至可能导致 OutOfMemoryError。
如何预防呢?
始终使用 finally块来关闭资源
关闭资源的代码(甚至在 finally块中)本身不应该有任何异常
使用Java 7+时,我们可以使用 try-with-resources块
3.3不正确的 equals()和 hashCode()实现
HashSet 和 HashMap 在许多操作中使用这些方法,如果它们没有被正确覆盖,那么它们可能成为潜在的内存泄漏问题的来源。
3.4引用了外部类的内部类
这种情况发生在非静态内部类(匿名类)的情况下。对于初始化,这些内部类总是需要外部类的实例。
默认情况下,每个非静态内部类都包含对其包含类的隐式引用。如果我们在应用程序中使用这个内部类'对象,那么即使在我们的包含类'对象超出范围之后,它也不会被垃圾收集。
如何预防呢?
如果内部类不需要访问包含的类成员,请考虑将其转换为静态类
3.5 finalize()方法造成的内存泄漏
使用 finalizers是潜在的内存泄漏问题的另一个来源。每当重写类的 finalize()方法时,该类的对象不会立即被垃圾收集。相反,GC将它们排队等待最终确定,这将在稍后的时间点发生。
另外,如果用 finalize()方法编写的代码不是最佳的,并且终结器队列无法跟上Java垃圾收集器,那么迟早,我们的应用程序注定要遇到 OutOfMemoryError。
3.6常量字符串造成的内存泄漏
如果我们读取一个庞大的大量String对象,并在该对象上调用intern(),那么它将转到字符串池,它位于PermGen(永生代)中,并且只要我们的应用程序运行就会保留在那里。这会占用内存并在我们的应用程序中造成重大内存泄漏。
如何预防呢?
解决此问题的最简单方法是升级到最新的Java版本,因为String池从Java版本7开始转移到HeapSpace
如果处理大型字符串,请增加PermGen空间的大小以避免任何潜在的OutOfMemoryErrors:
-XX:MaxPermSize=512m
3.7 使用 ThreadLocal造成的内存泄漏
ThreadLocal (在Introduction to ThreadLocal in Java 中详细介绍),是一种能将状态隔离到特定线程,从而保证我们实现线程安全的结构。
使用此结构时,每个线程只要处于存活状态即可将保留对其ThreadLocal变量副本的隐式引用,并且将保留其自己的副本,而不是跨多个线程共享资源。
ThreadLocal中的内存泄漏
一旦保持线程不再存在, ThreadLocals应该被垃圾收集。但是当 ThreadLocals与现代应用程序服务器一起使用时,问题就出现了。
现代应用程序服务器使用线程池来处理请求而不是创建新请求(例如在Apache Tomcat的情况下为Executor)。此外,他们还使用单独的类加载器。
由于应用程序服务器中的线程池在线程重用的概念上工作,因此它们永远不会被垃圾收集 - 相反,它们会被重用来处理另一个请求。
现在,如果任何类创建 ThreadLocal 变量但未显式删除它,则即使在Web应用程序停止后,该对象的副本仍将保留在工作线程中,从而防止对象被垃圾回收。
如何预防呢?
在不再使用 ThreadLocals时清理 ThreadLocals是一个很好的做法- ThreadLocals提供了 remove()方法,该方法删除了此变量的当前线程值
不要使用 ThreadLocal.set(null) 来清除该值 - 它实际上不会清除该值,而是查找与当前线程关联的 Map并将键值对设置为当前线程并分别为null
最好将 ThreadLocal 视为需要在 finally块中关闭的资源,以 确保它始终关闭

你可能感兴趣的:(Java)