一、JVM堆:
JVM的运行时内存又叫JVM/JAVA堆,几乎所有的java对象都存放在堆中,而且堆内存是完全自动化管理的,通过垃圾回收机制,垃圾对象会被自动清理,虽然根据垃圾回收算法的不同,堆结构也可能有很大不同,但是最常见的是将堆分为新生代,老生代和永久代。如下图所示:
二、新生代,老年代和永久代:
1、新生代:
JVM新创建的对象(除了大对象)会被存放在新生代,默认占用1/3堆内存空间。由于JVM会频繁创建对象,所以新生代会频繁出发MinorGC进行垃圾回收。新生代又分为Eden区和两个Survivor区(又被称为S0和S1或者from和to区域,两个区域可以互换角色)。
何为大对象:大对象的定义和具体的jvm版本,堆大小和垃圾回收算法有关,一般为2kb~128kb,可以通过XX:PretenureSizeThreshold设置大小
- Eden区:Java新创建的对象首先会被存放在Eden区,如果是大对象则直接放在老年区。在Eden区内存空间不足时会触发MinorGC,堆新生代进行一次GC;
- SurvivorTo区:保留上一次MinorGC时的幸存者;
- SurvivorFrom区:将上一次MinorGC时的幸存者作为这一次MinorGC的被扫描者;
何为MinorGC:新生代的GC过程被称作MinorGC,采用复制算法实现。MinorGC发生时,首先会把在Eden区和From区中存活的对象复制到To区。有三种情况对象会被直接复制到老年代:
1.对象年龄达到老年代的标准(多次GC依旧存活,也就是年龄够大,通过一次GC年龄+1);
2.To区域满;
3.大对象也会被直接送往老年代;
复制完之后清空eden和From区,并且将From和To区域互换完成一次MinorGC。因为新生代绝大多数对象都是朝生夕死(大约百分之九十的新建对象会很快被回收),因此比较适合用复制算法。
2、老年代:
老年代主要存放长生命对象和大对象。老年代的GC过程叫做MajorGC。因为老年代的存活率几乎可以达到100%,MajorGC不会被频繁触发。在进行MajorGC前,JVM会进行一次MinorGC,在MinorGC过后仍然出现老年代且当老年代空间不足或无法找到足够大的连续内存空间分配给新创建的大对象时,会触发MajorGC进行垃圾回收,释放内存。
何为MajorGC:老年代的GC过程被称为MajorGC,采用标记压缩法或者标记清除法,标记清除算法过程分为两个阶段:标记和清除,标记过程首先通过根节点标记所有从根节点开始的可达节点,也就是存活对象,未被标记到的会在清除阶段被回收;至于标记压缩算法就是在标记清除后把剩余存活对象复制整理到一个空闲内存中,避免了标记清除会遗留小空间无法被有效利用的弊端。
3、永久代:
永久代指的是内存的永久保存区域,主要存放Class和Meta(元数据)信息。Class在类加载时被放入永久代。永久代不会发生GC过程,所以随着程序运行过程永久代会随着加载的Class文件的增加而增加。如果加载Class过多会产生OutOfMenory异常。
在Java8之后,永久代已经被元数据区取代,但是元数据区并没有使用虚拟机内存,而是直接使用操作系统本地内存,所以元数据去不受JVM内存影响。