面试官:小伙子,你给我说一下Java中什么情况会导致内存泄漏呢?
概念
内存泄露:指程序中动态分配内存给一些临时对象,但对象不会被GC回收,它始终占用内存,被分配的对象可达但已无用。即无用对象持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间浪费。
可达性分析算法
JVM使用可达性分析算法判断对象是否存活。
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,并没有清除这个键值对。
最后
大家看完有什么不懂的可以在下方留言讨论,也可以关注我私信问我,我看到后都会回答的。也欢迎大家关注我的公众号:前程有光,马上金九银十跳槽面试季,整理了1000多道将近500多页pdf文档的Java面试题资料放在里面,助你圆梦BAT!文章都会在里面更新,整理的资料也会放在里面。谢谢你的观看,觉得文章对你有帮助的话记得关注我点个赞支持一下!
面试官:小伙子,你给我说一下Java中什么情况会导致内存泄漏呢?的更多相关文章
- 一线大厂面试官最喜欢问的15道Java多线程面试题
前言 在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分.如果你想获得更多职位,那么你应该准备很多关于多线程的问题. 他们会问面试者很多令人混淆的Java线程问题.面试官只是想确信面试者 ...
- 【Spring注解驱动开发】面试官:如何将Service注入到Servlet中?朋友又栽了!!
写在前面 最近,一位读者出去面试前准备了很久,信心满满的去面试.没想到面试官的一个问题把他难住了.面试官的问题是这样的:如何使用Spring将Service注入到Servlet中呢?这位读者平时也是很 ...
- 面试官:如何在Integer类型的ArrayList中同时添加String、Character、Boolean等类型的数据? | Java反射高级应用
原文链接:原文来自公众号:C you again,欢迎关注! 1.问题描述 "如何在Integer类型的ArrayList中同时添加String.Character.Boolean等 ...
- 搞定面试官:咱们从头到尾再说一次 Java 垃圾回收
接着前几天的两篇文章,继续解析JVM面试问题,送给年后想要跳槽的小伙伴 万万没想到,面试中,连 ClassLoader类加载器 也能问出这么多问题..... 万万没想到,JVM内存区域的面试题也可以问 ...
- 面试官:我们来聊一聊Redis吧,你了解多少就答多少
哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新,建议收藏关注 一.前言 作为一名Java程 ...
- 面试官:说一说Zookeeper中Leader选举机制
哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 今天又是一个阳光明媚的一天,我又 ...
- 如何写出面试官欣赏的Java单例
单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中一个类只有一个实例. 今天我们不谈单例模式的用途,只说一说如果在面试的时候面试官让你敲一段代码 ...
- 【JAVA秒会技术之秒杀面试官】秒杀Java面试官——集合篇(一)
[JAVA秒会技术之秒杀面试官]秒杀Java面试官——集合篇(一) [JAVA秒会技术之秒杀面试官]JavaEE常见面试题(三) http://blog.csdn.net/qq296398300/ar ...
- Java面试官最爱问的volatile关键字
在Java的面试当中,面试官最爱问的就是volatile关键字相关的问题.经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用 ...
随机推荐
- Typora图片一键传——picgo使用兰空图床
Typora picgo使用兰空图床 最近smms图床免费版的速度眼看着越来越慢,特别是到晚上,图片几乎是在原地打转,于是我想替换掉Typora中picgo默认使用的smms图床,网上的教程清一色地只 ...
- RocketMQ单节点搭建
RocketMQ服务搭建 下载RocketMQ源码: http://mirror.bit.edu.cn/apache/rocketmq/4.4.0/rocketmq-all-4.4.0-source- ...
- node.js操作MySQL数据库
MySQL数据库作为最流行的开源数据库.基本上是每个web开发者必须要掌握的数据库程序之一了. 基本使用 node.js上,最受欢迎的mysql包就是mysql模块. npm install mysq ...
- Dockerfile 笔记
Dockerfile ARGARG <name>[=<default value>]The ARG instruction defines a variable that ...
- 050_Dos命令
目录 Dos命令 打开Dos控制台 管理员方式运行 常用的Dos命令 Dos命令 打开Dos控制台 开始->附件->命令提示符 Window+R 输入cmd(推荐使用) 在任意文件夹下-& ...
- vue table切换 (不使用路由功能)
背景: 一个小场景,感觉使用路由功能太浪费了,考虑用一个简单的类控制 实例代码 //v-for 实现循环<ul class="nav-bar"> <li v-fo ...
- JUC---12深入理解CAS
一.什么是CAS Compare and Swap, 翻译成比较并交换,是java.util.concurrent.atomic包下的类里面的CompareAndSet()方法:java.util.c ...
- python实现银行系统模拟程序
银行系统模拟程序 关注公众号"轻松学编程"了解更多. 1.概述 使用面向对象思想模拟一个简单的银行系统,具备的功能:管理员登录/注销.用户开户.登录.找回密码.挂失.改密.查询 ...
- Java 将JSON反射到实体类
通过服务间调用拿到的数据返回的格式是JSON,如果你当前这个服务有实体数据类型可以对应上,那么就可以轻松愉快的搞定. 如果数据格式对不上,例如这个JSON里面有些数据是我们不想要的,这样我们实体的数据 ...
- leetcode104:permutations
题目描述 给出一组数字,返回该组数字的所有排列 例如: [1,2,3]的所有排列如下 [1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2], [3,2,1]. (以数字在数 ...