java什么会导致内存泄漏,关于java:面试官小伙子你给我说一下Java中什么情况会导致内存泄漏呢...

概念

内存泄露:指程序中动静分配内存给一些长期对象,但对象不会被GC回收,它始终占用内存,被调配的对象可达但已无用。即无用对象继续占有内存或无用对象的内存得不到及时开释,从而造成的内存空间节约。

可达性剖析算法

JVM应用可达性剖析算法判断对象是否存活。

【腾讯云】云产品限时秒杀,爆款1核2G云服务器,首年99元

GC Root

通过一系列名为“GC Roots”的对象作为终点,从这些结点开始向下搜寻,搜寻所走过的门路称为“援用链(Reference Chain)”,当一个对象到GC Roots没有任何饮用链相连时,则证实此对象是不可用的。

object4、object5、object6尽管有相互判断,然而它们到GC Rootd是不可达的,所以它们将会断定为是可回收对象。

能够作为GC Roots的对象有:

虚拟机栈(栈帧中的本地变量表)中的援用的对象;

办法区中的类动态属性援用的对象;

办法区中的常量援用的对象;

本地办法栈中JNI的援用的对象

尽管Java有垃圾收集器帮组实现内存主动治理,尽管GC无效的解决了大部分内存,然而并不能齐全保障内存的不透露。

内存透露

内存透露就是堆内存中不再应用的对象无奈被垃圾收集器革除掉,因而它们会不必要地存在。这样就导致了内存耗费,升高了零碎的性能,最终导致OOM使得过程终止。

内存透露的体现:

应用程序长时间间断运行时性能重大降落;

应用程序中的OutOfMemoryError堆谬误;

自发且奇怪的应用程序解体;

应用程序偶然会耗尽连贯对象;

可能导致内存透露的起因:

1. static字段引起的内存透露

大量应用static字段会潜在的导致内存透露,在Java中,动态字段通常领有与整个应用程序相匹配的生命周期。

解决办法:最大限度的缩小动态变量的应用;单例模式时,依赖于提早加载对象而不是立刻加载的形式(即采纳懒汉模式,而不是饿汉模式)

2. 未敞开的资源导致内存透露

每当创立连贯或者关上流时,JVM都会为这些资源分配内存。如果没有敞开连贯,会导致继续占有内存。在任意状况下,资源留下的凋谢连贯都会耗费内存,如果不解决,就会升高性能,甚至OOM。

解决办法:应用finally块敞开资源;敞开资源的代码,不应该有异样;JDK1.7之后,能够应用太try-with-resource块。

3. 不正确的equals()和hashCode()

在HashMap和HashSet这种汇合中,经常用到equal()和hashCode()来比拟对象,如果重写不合理,将会成为潜在的内存透露问题。

解决办法:用最佳的形式重写equals()和hashCode().

4. 援用了外部类的外部类

非动态外部类的初始化,总是须要外部类的实例;默认状况下,每个非动态外部类都蕴含对其外部类的隐式援用,如果咱们在应用程序中应用这个外部类对象,那么即便在咱们的外部类对象超出范围后,它也不会被垃圾收集器革除掉。

解决办法:如果外部类不须要拜访外部类蕴含的类成员,能够转换为动态类。

5. finalize办法导致的内存透露

重写finalize()办法时,该类的对象不会立刻被垃圾收集器收集,如果finalize()办法的代码有问题,那么会潜在的印发OOM;

解决办法:防止重写finalize()办法。

6. 常量字符串造成的内存透露

如果咱们读取一个很大的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;

}

能够看到,比拟两个字符串的时候,首先比拟两个字符串对象是否地址雷同,不同再挨个比拟字符。这样就大大放慢了比拟的速度。否则若每次都挨个比拟将是十分耗时的。

7. 应用ThreadLocal造成内存透露

应用ThreadLocal时,每个线程只有处于存活状态就可保留对其ThreadLocal变量正本的隐式调用,且将保留其本人的正本。使用不当,就会引起内存透露。

一旦线程不再存在,该线程的threadLocal对象就应该被垃圾收集,而当初线程的创立都是应用线程池,线程池有线程重用的性能,因而线程就不会被垃圾回收器回收。所以应用到ThreadLocal来保留线程池中的线程的变量正本时,ThreadLocal没有显式地删除时,就会始终保留在内存中,不会被垃圾回收。

解决办法:不再应用ThreadLocal时,调用remove()办法,该办法删除了此变量的以后线程值。不要应用ThreadLocal.set(null),它只是查找与以后线程关联的Map并将键值中这个threadLocal对象所对应的值为null,并没有革除这个键值对。

最初

感激你看到这里,看完有什么的不懂的能够在评论区问我,感觉文章对你有帮忙的话记得给我点个赞,每天都会分享java相干技术文章或行业资讯,欢送大家关注和转发文章!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值