面试:Java内存泄露的根本原因是什么?
内存泄露:程序运行期间分配的对象,用完之后却没有被GC回收,始终占据着内存,既用完弃用后又无法gc回收就发生了内存泄露。
内存泄露根本原因
长命周期的对象持有短生命周期对象的引用就很有可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是Java中内存泄露的发生场景。
有哪些常见的内存泄漏的场景?如何解决?
- 静态集合类引起内存泄露:
静态的集合对象,例如:list HashMap、Vector等的使用最容易出现内存泄露。这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们也将一直被List等引用者。
public class RevealList {
static List list= new ArrayList();
public void addList() {
for (int i = 1; i<10; i++)
{
Object o = new Object();
list.add(o);
}
}
public void readList() {
for (Object o:list)
{
System.out.println(o);
}
}
public static void main(String[] args) {
RevealList reveal=new RevealList();
reveal.addList();
reveal.readList();
}
}
在这个例子中,循环申请Object对象,并将锁申请的对象放入一个List中,list引用了object的短生命对象,这些短生命对象堆GC来说是不可回收的。
因此,如果对象加入到List后,还不许从List中删除,最简单的方法就是调用list.clear()。
- 单例模式引起的内存泄露
由于单例的静态特性使得其生命周期跟应用的生命周期一样长,所以如果使用不恰当的话,很容易造成内存泄露。
public class Singleton {
private static Singleton instance;
private List list;
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public static void main(String[] args) {
List list= new ArrayList();
for (int i = 1; i<10; i++)
{
Object o = new Object();
list.add(o);
}
Singleton.getInstance().setList(list);
}
}
- 常见的tcp连接
比如数据库连接(dataSourcec.getrConnection())、网络连接(socket)和io连接,除非其显示地调用了其close()方法将其连接关闭,否则是不会自动被GC回收的。
解决:
在try里面去的连接,在finally里面释放连接。
面试:内存溢出跟内存泄露的区别
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;
内存泄露:程序运行期间分配的对象,用完之后却没有被GC回收,始终占用着内存,既用完弃用后又无法gc回收,就发生了内存泄露,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早被占光,最终演变为内存溢出。
- java.lang.OutOfMemoryError:Metaspace(方法区溢出)
我们知道jvm通过持久代实现了java虚拟机规范中的方法区,而运行时常量池就是保存在方法区中的,因此发生这种溢出可能是运行时常量池溢出。
或是由于程序中使用了大量的jar或class,使得方法区中保存的class对象没有被及时回收或者class信息占用的内存超过了配置的大小。
- java.lang.OutOfMemoryError:java heap space(堆溢出)
发生这种溢出的原因一般是创建的对象太多了,在进行垃圾回收之前对象数量达到了最大堆的容量限制。
解决这个区域异常的方法一般是通过内存映像工具(比如jprofiler)对Dump出来的堆转储快照进行分析,看到底是内存溢出还是内存泄漏。
如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,定位出泄漏代码的位置,修改程序或算法;
如果不存在内存泄露,就是说内存中的对象确实都还必须存活,那就应该检查虚拟机的堆参数-Xmx(最大堆大小)和-Xms(初始堆大小),与机器物理内存对比看是否可以调大。
- 虚拟机栈和本地方法栈溢出
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError。