JAVA內存泄漏
由于JAVA與C++的主要區(qū)別在于,JAVA存在特有的垃圾回收機制,JAVA程序員看似不用考慮程序的內存使用情況,這種誤解使得JAVA內存泄漏的知識點成為面試的核心問題。而在我之前看過很多技術類的書中,大部分書中都將內存溢出與內存泄漏混為一談,給初學者帶來了很大的困擾。
目錄
- 內存泄漏的兩種情景
- JVM可回收資源的內存泄漏
- JVM不可回收資源的內存泄漏
1.JVM可回收資源的內存泄漏
內存泄漏產生的兩個條件:
- 使用數(shù)組(或集合)形式存儲對象
- 數(shù)組(或集合)使用額外的控制變量控制數(shù)組(或集合)的有效區(qū)間
舉例:例如棧,我們使用數(shù)組a表示棧,申請了10個大小的空間,我們首先push10次,然后pop9次,這時a中僅僅包含1個元素,如果我們在pop操作中,沒有銷毀數(shù)組中元素所執(zhí)行的內存空間,而僅僅是移動了棧頂指針,那么a[1]~a[9]元素所指向的內存空間由于棧頂指針的限制是訪問不到的,因而其指向的內存空間都應該是垃圾,然而在現(xiàn)在的場景下,這些垃圾仍然有引用指向該內存,這就是內存泄漏。
實例程序如下:
package com.feng.fresh.service;
/**
* Created by xinfeng.xu on 2016/11/14.
*/
public class StackDemo {
static final int MAX_SIZE = 10;
static class Student{
public Student(String name, int id) {
this.name = name;
this.id = id;
}
String name;
int id;
}
private Student[] a = new Student[MAX_SIZE];
private int stackTop = 0;
public void push(Student student){
if(stackTop >= MAX_SIZE){
return;
}
a[stackTop++] = student;
}
public Student pop(){
if(stackTop<=0){
return null;
}
return a[--stackTop];
}
public static void main(String[] args) {
StackDemo demo = new StackDemo();
for(int i=0; i<MAX_SIZE; i++){
Student student = new Student(i+"", i);
demo.push(student);
}
for(int i=MAX_SIZE; i>1; i++){
demo.pop();
}
}
}
其內存堆棧的分配如下圖所示:
內存泄漏.png
或許會有人提問:
- 如果再次往push元素,原位置上的元素不就沒有引用而變?yōu)槔藛?/li>
- 如果該類只被用于方法內,方法調用完成之后,整個對象都會成為垃圾
上面兩種說法都是正確的,但是這只是盡早的覆蓋了內存泄漏的問題,但我們不能總是保證會push元素或者只是在局部變量使用該對象,內存泄漏在該例中是確實存在的,我們應該從程序的角度杜絕內存泄漏。
解決方案,修改StackDemo中的pop方法:
public Student pop(){
if(stackTop<=0){
return null;
}
Student student = a[--stackTop];
a[stackTop] = null;
return student;
}
在pop操作時,我們應該讓數(shù)組中相應位置的引用置為null,當進行垃圾回收時,JVM就可以回收該資源了,而不會出現(xiàn)不可控的局面。
2. JVM不可回收資源的內存泄漏
JVM的垃圾回收機制,只能回收堆中的內存,而對于一些資源(例如IO流,線程資源等等)是不受JVM管理的,因為使用該類資源應該手動關閉,否則該類資源就會越來越多,占用越來越多的資源,最終會導致服務器癱瘓,引起巨大的損失。