堆内存与垃圾回收
堆是Java进程在运行时,用来存放类对象的区域。堆主要分为三个区域。
-
新生代(Young Generation)
一般情况下,所有新生成的对象首先都是放在新生代。新生代又分为Eden区和两个Survivor(survivor0,survivor1)区,大部分对象在Eden区中生成。 -
老年代(Old Generation)
老年代存放的都是一些生命周期较长的对象,在新生代中经历了N次垃圾回收后仍然存活的对象就会被放到老年代中。 -
永久代(Permanent Generation)
永久代主要用于存放静态文件,如Java类、方法等。永久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如使用反射、动态代理、CGLib等bytecode框架时,在这种时候需要设置一个比较大的永久代空间来存放这些运行过程中新增的类。
注:Java SE8之后,永久代已被Metaspace(元空间)替代
GC是JVM内部的一个进程,垃圾回收的过程,就是回收堆内存中无效对象(也就是没有任何引用的对象)所占用的空间,用于将来的分配。
Java的垃圾回收是由JVM自动进行的,对于程序开发者来说,垃圾回收是不可控的。也就是说我们调用System.gc()方法时,不一定会立即进行垃圾回收。
过程:
-
当创建一个对象时,会首先到Eden空间分配内存,如果Eden内存空间充足,就会 把对象放到Eden中,否则,就会进行Minor GC。
-
Minor GC进行时,会把Eden空间仍然存活的对象,复制到S0区域,然后清除掉Eden区域中的不存活的对象。如果S0区域没有空间了,又会进行Minor GC,就会把Eden区域和S0区域的存活对象复制到S1区域,然后清空Eden区域和S0区域,并且还会交换S1和S0区域,只要S1区域能够存放S0和Eden区域的存活对象,这个过程会反复进行。
-
当S1区域无法存放S0和Eden区域的存活对象时,又或者,一个对象在经历了指定次数的垃圾回收之后,还存活,那么,就会把它放进老年代。
-
接下来,如果老年代内存空间也用完了,就会进行Full GC(也叫Major GC),也就是新生代和老年代都进行垃圾回收。
-
当Full GC进行之后,内存仍然不够的话,就会抛出OutOfMemoryError异常。
垃圾回收的类型:
-
Minor GC:对新生代进行回收,不会影响到年老代。因为新生代的 Java 对象大多死亡频繁,所以 Minor GC 非常频繁,一般在这里使用速度快、效率高的算法,使垃圾回收能尽快完成。
-
Full GC:也叫 Major GC,对整个堆进行回收,包括新生代、老年代和永久代。由于Full GC需要对整个堆进行回收,所以比Minor GC要慢,因此应该尽可能减少Full GC的次数,导致Full GC的原因包括:老年代被写满、永久代(Perm)被写满和System.gc()被显式调用等。
注:JVM在进行垃圾回收时,会停止当前应用程序的执行。除了GC运行所需要的线程以外,其他线程都会等到GC完成之后才会继续执行。所以当我们对GC优化时,就要尽量减少GC的次数。
网友评论