java中gc的整理

gc 只能清除在堆上分配的内存(纯java语言的所有对象都在堆上使用new分配内存),而不能清除栈上分配的内存(当使用JNI技术时,可能会在栈上分配内存,例如java调用c程序,而该c程序使用malloc分配内存时).因此,如果某些对象被分配了栈上的内存区域,那gc就管不着了,对这样的对象进行内存回收就要靠finalize().


正解,在JVM里面GC是一条优先级非常低的thread,调用时机一般是在heap要快满了或者当前java进程闲的蛋疼的时候,但可以调整GC的频率。

是程序员手动调用的,这只是给jvm提个醒而已,告诉他该回收垃圾了,但是jvm什么时候真的去回收垃圾,我们就没有办法控制了。



java提供了从语言角度能够强制jvm进行垃圾回收,在我们的程序中可以通过调用System.gc去强制jvm进行垃圾回收,通过源码我们可以看到实际上是调用了Runtime去强制gc

[java]  view plain copy
  1. public static void gc() {  
  2.     Runtime.getRuntime().gc();  
  3.     }  

Runtime的gc方法是native方法也就是Rumtime.c中的

[cpp]  view plain copy
  1. JNIEXPORT void JNICALL  
  2. Java_java_lang_Runtime_gc(JNIEnv *env, jobject this)  
  3. {  
  4.     JVM_GC();  
  5. }  

而JVM_GC方法是在jvm.cpp中实现

[cpp]  view plain copy
  1. JVM_ENTRY_NO_ENV(void, JVM_GC(void))  
  2.   JVMWrapper("JVM_GC");  
  3.   if (!DisableExplicitGC) {  
  4.     Universe::heap()->collect(GCCause::_java_lang_system_gc);  
  5.   }  
  6. JVM_END  

我们看到参数DisableExplicitGC,从代码中可以使用-XX:+DisableExplicitGC 可以关闭system,gc


我们把GC分成4种类型

1.  SerialGC 

参数-XX:+UseSerialGC

就是Young区和old区都使用serial 垃圾回收算法,

2.  ParallelGC 

参数-XX:+UseParallelGC

Young区:使用Parallel scavenge 回收算法

Old  区:可以使用单线程的或者Parallel 垃圾回收算法,由 -XX:+UseParallelOldGC 来控制

3.  CMS  

参数-XX:+UseConcMarkSweepGC

Young区:可以使用普通的或者parallel 垃圾回收算法,由参数 -XX:+UseParNewGC来控制

Old 区:只能使用Concurrent Mark Sweep 

4. G1 

参数:-XX:+UseG1GC

没有young/old区

对于程序员来说,分配对象使用new关键字;释放对象时,只要将对象所有引用赋值为null,让程序不能够再访问到这个对象,我们称该对象为"不可达 的". GC  将负责回收所有"不可达"对象的内存空间。 


    finalize 是位于Object类的一个方法,该方法的访问修饰符为protected,由于所有类为Object的子类,因此用户类很容易访问到这个方法。由 于,finalize函数没有自动实现链式调用,我们必须手动的实现,因此finalize函数的最后一个语句通常是 super.finalize()。通过这种方式,我们可以实现从下到上实现finalize的调用,即先释放自己的资源,然后再释放父类的资源。 

    根据Java 语言规范,JVM保证调用finalize函数之前,这个对象 是不可达的,但是JVM不保证这个函数一定会被调用。另外,规范还保证finalize函数最多运行一次。



JVM的垃圾回收采用有向图方式来管理内存中的对象,因此可以方便的解决循环引用的问题。采用有向图来管理内存中的对象具有较高的精度,但缺点是效率较低。判断一个对象是否可回收的标准就在于该对象是否被引用,一个对象在堆内存中运行时,可以把它分为三种:可达状态、可恢复状态、不可达状态。


由于JVM肯定不会回收被强引用所引用的java对象,因此强引用是造成java内存泄露的主要原因之一。

垃圾回收方式:复制、标记清除、标记压缩

复制:将堆内存分成两个相同的空间,从根开始访问每一个关联的可达对象,将空间A的可达对象全部复制到空间B,然后一次性回收整个空间A。

标记清除:也就是不压缩回收,垃圾回收器先从根开始访问所有的可达对象,将它们标记为可达状态,然后再遍历一次整个内存区域,对所有的没有标记为可达的对象进行回收处理。---两次遍历堆内存空间,整理后的对内存不连续,堆内存的碎片会很多。

标记压缩:也就是压缩回收,结合上面两种算法的优点,从根访问所有可达对象,标记为可达对象,接下来垃圾回收器会将这些活动对象搬迁在一起,然后垃圾回收机制再次回收那些不可达对象的内存空间,这样就避免了回收产生内存的碎片。


现行的垃圾回收器用分代的方式来采用不同的回收设计,分代的基本思路是根据对象生存的时间长短,把堆内存分为三个代:

Young(新生代)、Old(老年代)、Permanent(永生代)

垃圾回收器会根据不同代的特点采用不同的回收算法:

Young代:对Young代的对象而言,大部分对象都会很快就进入不可达状态,只有少量的对象能熬到垃圾回收执行时,而垃圾回收器只需要保留Young代中处于可达状态的对象,如果采用复制算法只需要少量的复制成本,因此大部分大部分垃圾回收器对Young代都采用复制算法。

Young代由一个Eden区和两个Survior区构成,绝大部分对象先分配到Eden区中,同一时间,两个Survivor区一个用来保存对象,一个是空的,用来在下次垃圾回收的时候保存Young代中的对象。每次复制就是将Eden和第一个Survivor区的可达对象复制到第二个Survivor区,然后清空Eden与第一个Survivor区。

Old代:如果Young代中的对象经过数次垃圾回收依然没有被回收掉,垃圾回收机制会将这个对象转移到Old代。Old代的垃圾回收具有如下两个特征:Old代垃圾回收的执行频率无需太高,因为很少有对象会死掉;每次Old代执行垃圾回收都需要更长的时间来完成。基于上述考虑,垃圾回收器通常会使用标记压缩算法,这可以避免复制Old代的大量对象,而且由于Old代的对象不会很快死亡,回收过程不会大量的产生内存碎片,相对比较划算。

Permanent代Permanent代主要用于装载Class、方法等信息,默认为64MB,对于那些需要加载很多类的服务器程序,往往需要加大Permanent代的内存,否则可能会因为内存不足而导致程序终止,垃圾回收机制通常不会回收Permanent代中的对象。在运行Hibernate、Spring程序时遇到java.lang.OutOfMemoryError:PermGen space的错误,这就是Permanent代内存耗尽所导致的错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值