Java垃圾回收总结

 

一、在理解垃圾回收前需要了解一个对象在堆内存中的引用状态,分为三种:

1.可达状态:对象创建后,有一个或以上的引用变量引用它。

2.可恢复状态:程序中某个对象不再有任何引用变量引用它,但是还是有可能重新被其他引用变量引用。

3.不可达状态:对象与所用引用变量的引用都被切断,且jvm执行finalize()方法进行资源清理后没有成为可达状态,那么这个对象将永久的失去引用,编程不可达状态。(GC真正回收的正是处于这种状态的对象)。

 

public void test(){
     //此处对象为可达状态
     String a=new String("this is a string object");
     //此处a引用只想了另一个变量,则上面的string对象成为可恢复状态
     a=new String("another string object");
}

 

 

二、垃圾回收有如下机制:

1.垃圾回收只负责回收堆内存中的对象,不会回收任何物理资源(如:数据库连接,网络IO等)。

2.程序无法精确控制垃圾回收的运行,垃圾回收只有再合适的时候进行。对象成为不可达状态后,JVM会再合适的时候回收该对象。

3.垃圾回收任何对象前之前总会调用这个对象的finalize方法,进行资源清理。finalize方法可能使该对象重新成为可达状态,从而使使JVM取消对它的回收。

 

 

三、强制垃圾回收:

强制垃圾回收其实是建议JVM立即进行GC,但JVM是否进行gc操作是不确定的,垃圾回收机制只是在接收到通知尽快进行垃圾回收。强制垃圾回收有如下两种方式,

System.gc();

Runtime.getRuntime().gc();

 

 

四、理解finalize方法:

系统在垃圾回收前总会调用finalize方法进行资源清理,这个操作和垃圾回收操作是一起发生但要先于GC。只有程序任务需要额外的内存时才会进行GC。finalize有如下特点:

1.不要主动调用finalize方法,该方法应交给垃圾回收机制调用。

2.finalize方法何时执行无法确定。

3.JVM执行finalize方法时,有可能将对象从可恢复状态变成可达状态。

4.JVM执行finalize方法时,垃圾回收机制不会报错,程序继续执行。

如下例子说明垃圾回收的不确定性:

public class FinalizeTest {
	private static FinalizeTest ft=null;
	public void info(){
		System.out.println("调用系统的finalize方法");
	}
	
	public static void main(String[] args) throws InterruptedException {
		
		//创建对象语句也会进行垃圾回收
		new FinalizeTest();
		
		//通知系统立即进行垃圾回收
		System.gc();//1
		
		
//		Thread.sleep(2000);//3
		
		ft.info();
		
	}
	//重写了finalize方法
	public void finalize(){
		ft=this;
	}
}

 上述例子中注释掉3处代码可以发现程序报空指针异常,取消注释后,程序打印“ 调用系统的finalize方法”,说明程序显示执行垃圾回收,但finalize方法不会立即执行,当延迟2s后发现垃圾回收执行了。如果在1和3处代码中间加入下列代码则可强制调用finalize方法。

 

//强制垃圾回收调用可恢复对象的finalize方法,执行此方法前一定先通知系统进行垃圾回收操作
//		Runtime.getRuntime().runFinalization();
		System.runFinalization();//2

 

五、是否应该在程序中显示调用垃圾回收?

首先你需要知道两个概念:

JVM的gc有minor GC和major GC(也就是大家说的Full GC),JVM会很频繁的做minor GC,如果内存块占满的话,JVM会做Full GC,Full GC是对整个JVM内存堆做GC,所示耗时比minor GC要长很多。System.gc()最终执行的是Full GC
1,服务器老是崩溃是因为内存很快被占满,在允许的条件下你可以适当加大内存配置,用mx和ms控制内存堆大小
-Xms
-Xmx
2,如果你的程序需要通过System.gc()来减少JVM崩溃的几率,那么你的程序10有8,9存在问题,需要从你程序本身去进行优化
3,可以把GC配置为CMS回收方式,以提高回收效率
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+UseParNewGC
-XX:CMSFullGCsBeforeCompaction=0
-XX:CMSInitiatingOccupancyFraction=75
-XX:+CMSParallelRemarkEnabled
-XX:+CMSPermGenSweepingEnabled
-XX:+CMSClassUnloadingEnabled
4,-XX:+PrintGCTimeStamps  -XX:+PrintGCDetails -XX:+PrintHeapAtGC用这些参数输出GC日志,然后分析,

 

通过上述描述可知道,System.gc()最终执行的是Full GC,执行时会对整个JVM堆内存做GC,这将给程序带来一个严重的性能损失。一般不建议这么做。

 

六:为防止内存泄露,对象引用用完后都清空?

清空对象引用应该是一种例外,而不是一种规范行为,这样做会使代码很乱,一般而言只要类是自己管理内存,程序员就应该警惕内存泄露问题。

要防止内存泄露,下面是一些快速上手的实用技巧:
1. 当心集合类,比如 HashMap,ArrayList等,因为这是最容易发生内存泄露的地方.当集合对象被声明为static时,他们的生命周期一般和整个应用程序一样长。
2. 注意事件监听和回调.当注册的监听器不再使用以后,如果没有被注销,那么很可能会发生内存泄露.
3. "当一个类自己管理其内存空间时,程序员应该注意内存泄露." 常常是一个对象的成员变量需要被置为null 时仍然指向其他对象。

下面举一个模拟栈的例子:

public class Stack {
	private Object[] elements;
	private int size=0;
	private static int CAPACITY=16;
	public Stack(){
		elements=new Object[CAPACITY];
	}
	
	public void push(Object e){
		ensureCapacity();
		elements[size++]=e;
	}
	
	public Object pop(){
		if(size==0)
			throw new EmptyStackException();
		return elements[--size];
	}
		
	private void ensureCapacity(){
		if(elements.length==size){
			elements=Arrays.copyOf(elements, 2*size+1);
		}
	}
	
}

 此例子会有一个内存泄露的现象,从栈中弹出来的对象不会当做垃圾回收,即使栈不再引用这些对象,这是因为,栈内部还维护者这些弹出对象的过期引用。所谓过期引用是指永远不会被解除的引用。随着时间推移,无法回收对象越来越多,程序性能越来越差最后可能导致内存溢出。此时可以做如下处理:

 

       public Object pop2(){
		if(size==0)
			throw new EmptyStackException();
		Object result= elements[--size];
		elements[size]=null;
		return result;
	}

一旦数组元素变成非活动的一部分,就手动清空这些元素。

 

七、对象的软、弱和虚引用,

正常程序中的引用变量为强引用。当程序中需要避免在执行期间将对象留在内存中,可以使用如下几种引用:

1.软引用:当系统内存空间不足时,会回收它,但空间足够时,不会回收,程序也可以使用改对象。

2.弱引用:跟软引用类似,比软引用级别耕地,当GC操作时,不管内存是否足够,对象总会回收。

3.虚引用:和没有引用效果大致相同,主要用于跟踪对象垃圾回收状态,不能单独使用,必须和引用队列(ReferenceQueue)联合使用。

 

你可能感兴趣的:(java,GC,垃圾回收,内存泄露)