内存泄露:指程序中动态分配内存给一些临时对象,但对象不会被GC回收,它始终占用内存,被分配的对象可达但已无用。即无用对象持续占有内存或无用对象的内存得不到及时释放,从而形成的内存空间浪费。java
JVM使用可达性分析算法判断对象是否存活。算法
经过一系列名为“GC Roots”的对象做为起点,从这些结点开始向下搜索,搜索所走过的路径称为“引用链(Reference Chain)”,当一个对象到GC Roots没有任何饮用链相连时,则证实此对象是不可用的。性能
object四、object五、object6虽然有互相判断,可是它们到GC Rootd是不可达的,因此它们将会断定为是可回收对象。this
能够做为GC Roots的对象有:spa
虽然Java有垃圾收集器帮组实现内存自动管理,虽然GC有效的处理了大部份内存,可是并不能彻底保证内存的不泄漏。线程
内存泄漏就是堆内存中再也不使用的对象没法被垃圾收集器清除掉,所以它们会没必要要地存在。这样就致使了内存消耗,下降了系统的性能,最终致使OOM使得进程终止。code
内存泄漏的表现:对象
大量使用static字段会潜在的致使内存泄漏,在Java中,静态字段一般拥有与整个应用程序相匹配的生命周期。blog
解决办法:最大限度的减小静态变量的使用;单例模式时,依赖于延迟加载对象而不是当即加载的方式(即采用懒汉模式,而不是饿汉模式)生命周期
每当建立链接或者打开流时,JVM都会为这些资源分配内存。若是没有关闭链接,会致使持续占有内存。在任意状况下,资源留下的开放链接都会消耗内存,若是不处理,就会下降性能,甚至OOM。
解决办法:使用finally块关闭资源;关闭资源的代码,不该该有异常;JDK1.7以后,可使用太try-with-resource块。
在HashMap和HashSet这种集合中,经常用到equal()和hashCode()来比较对象,若是重写不合理,将会成为潜在的内存泄漏问题。
解决办法:用最佳的方式重写equals()和hashCode().
非静态内部类的初始化,老是须要外部类的实例;默认状况下,每一个非静态内部类都包含对其外部类的隐式引用,若是咱们在应用程序中使用这个内部类对象,那么即便在咱们的外部类对象超出范围后,它也不会被垃圾收集器清除掉。
解决办法:若是内部类不须要访问外部类包含的类成员,能够转换为静态类。
重写finalize()方法时,该类的对象不会当即被垃圾收集器收集,若是finalize()方法的代码有问题,那么会潜在的印发OOM;
解决办法:避免重写finalize()方法。
若是咱们读取一个很大的String对象,并调用了intern(),那么它将放到字符串池中,位于PermGen中,只要应用程序运行,该字符串就会保留,这就会占用内存,可能形成OOM。(针对JDK1.6及之前,常量池在PermGen永久代中)
解决办法:增长PermGen的大小,-XX:MaxPermSize=512M;JDK1.7之后字符串池转移到了堆中。
intern()方法详解:
String str1 = "abc"; String str2 = "abc"; String str3 = new String("abc"); String str4 = str3.intern(); System.out.println(str1 == str2); System.out.println(str2 == str3); System.out.println(str1 == str4); System.out.println(str3 == str4); true, false, true, false
intern()方法搜索字符串常量池,若是存在指定的字符串,就返回之;
不然,就将该字符串放入常量池并返回之。
换言之,intern()方法保证每次返回的都是 同一个字符串对象
String str1 = "abc"; String str2 = "abc"; String str3 = new String("abcd"); String str4 = str3.intern(); String str5 = "abcd"; System.out.println(str1 == str2); System.out.println(str2 == str3); System.out.println(str1 == str4); System.out.println(str3 == str4); System.out.println(str4 == str5); true false false false true
为什么要使用intern()方法?看看equals方法的源码:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
能够看到,比较两个字符串的时候,首先比较两个字符串对象是否地址相同,不一样再挨个比较字符。这样就大大加快了比较的速度。不然若每次都挨个比较将是很是耗时的。
使用ThreadLocal时,每一个线程只要处于存活状态就可保留对其ThreadLocal变量副本的隐式调用,且将保留其本身的副本。使用不当,就会引发内存泄漏。
一旦线程再也不存在,该线程的threadLocal对象就应该被垃圾收集,而如今线程的建立都是使用线程池,线程池有线程重用的功能,所以线程就不会被垃圾回收器回收。因此使用到ThreadLocal来保留线程池中的线程的变量副本时,ThreadLocal没有显式地删除时,就会一直保留在内存中,不会被垃圾回收。
解决办法:再也不使用ThreadLocal时,调用remove()方法,该方法删除了此变量的当前线程值。不要使用ThreadLocal.set(null),它只是查找与当前线程关联的Map并将键值中这个threadLocal对象所对应的值为null,并无清除这个键值对。
感谢你看到这里,看完有什么的不懂的能够在评论区问我,以为文章对你有帮助的话记得给我点个赞,天天都会分享java相关技术文章或行业资讯,欢迎你们关注和转发文章!