很多人认为方法区(或者Hotspot中的永久代)是没有垃圾收集的,jvm规范中确实说过可以不要求在方法区实现垃圾收集,而且在方法区进行垃圾收集的性价比比较低:在堆中,新生代的常规应用进行一次垃圾收集一般可以回收70%~95%的空间,而永久代的垃圾收集效率远低于此 。
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类 。回收废弃常量和回收java堆中的对象非常相似 。以常量池中字面量的回收为例,假如一个字符串“abc"已经进入了常量池中,但是当前系统没有任何一个String对象是叫做“abc”的,换句话说,就是没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量,如果这是发生内存回收,而且必要的话,这个“abc"常量就会被系统清理出常量池 。常量池中的其他类(接口)、方法、字段的符号引用也与此类似 。
判断一个常量是否是废弃常量比较简单,而要判定一个类是否是无用的类的条件相对苛刻很多 。类需要同时满足下面三个条件才能算是无用的类:
该类的所有的实例都已经被回收,也就是java堆中不存在该类的任何实例;
加载该类的ClassLoader被回收;
该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法 。
3.3 垃圾收集算法
JVM中的堆,一般分为三大部分:新生代、老年代、永久代 。
当前商业虚拟机的垃圾收集都采用分代收集算法,这种算法是根据各个年代的特点采用最适当的收集算法 。一般是把java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法 。在新生代中,每次垃圾收集时都发现有大量对象死去,只有少量存活,那就选用复制算法,只需要付出少量的复制成本就可以完成收集 。而老年代中因为对象存活率高,没有额外空间对它进行分配担保,就必须使用标记-清除或者标记-整理算法进行回收 。
新生代主要是用来存放新生的对象 。一般占据堆的1/3空间 。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收 。新生代又分为 Eden区、ServivorFrom、ServivorTo三个区 。当JVM无法为新建对象分配内存空间的时候(Eden满了),Minor GC被触发 。因此新生代空间占用率越高,Minor GC越频繁 。
MinorGC的过程:采用复制算法 。
首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,一般是15,则赋值到老年代区)
同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区)
然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区 。
文章插图
老年代的对象比较稳定,所以MajorGC不会频繁执行 。
在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发 。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间 。
MajorGC采用标记—清除算法:
首先扫描一次所有老年代,标记出存活的对象
然后回收没有标记的对象 。
MajorGC的耗时比较长,因为要扫描再回收 。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配 。
当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常 。
永久代指内存的永久保存区域,主要存放Class和Meta(元数据)的信息 。
Class在被加载的时候被放入永久区域 。它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理 。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常 。
在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代 。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现 。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存 。因此,默认情况下,元空间的大小仅受本地内存限制 。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中. 这样可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制 。
Major GC和Full GC区别
Full GC:收集young gen、old gen、perm gen
推荐阅读
- Java 线程池 ThreadPoolExecutor 八种拒绝策略浅析
- B-Tree 数据结构详解及Java代码实现
- Docker容器与虚拟机有什么区别
- Java序列化是什么?你知道什么时候需要序列化吗?
- Java Overload 与 Override 差别
- 详解Java多线程锁之Lock和ReadWriteLock
- Java面向对象——成员变量和局部变量
- java第一次调用 Hadoop Java API
- 2019年最流行的五大JavaScript 自动化测试框架
- Java虚拟机最多支持多少个线程?