堆(Heap)
- Java堆内存是影响性能的主要因素,是线程共享的一块内存区域
- 一般用来存放对象和数组,所有的对象实例都需要在堆中进行分配
堆内存的划分
堆内存溢出是非常常见的问题,解决问题之前,了解堆内存基本结构是很有必要的
JVM堆内存图
- JVM内存分为堆内存和非堆内存(永久代)
- 堆内存分为新生代和老年代
新生代
- 默认约占1/3的堆内存
- JVM创建新对象的地方(大对象除外)
- 对象的创建和销毁最频繁的区域
- 垃圾回收的主要区域
- 特点:朝生夕死
新生代又分为Eden区(伊甸园区)和Survivor区(幸存者区)!
新生代对象的生命周期是由MinorGC决定的,先了解MinorGC的原理–复制算法(MinorGC三步:)
- 将Eden存活下来的和SurvivorFrom中的对象复制到To区,同时将这些对象的年龄加一,如果这些对象中已经存在达到老年代标准的对象或者To区的内存不足,就会将这些对象放到老年代
- 清空Eden和From区中的这些对象
- From与To相互交换,原来To区成为下一次MinorGC的From区,所以在一段时间内SurvivorFrom区和SurvivorTo区总有一个为空
老年代
- 存放有长生命周期的对象(指新生代中经过多次MinorGC达到老年代标准的对象)和 大对象
- 垃圾回收为MajorGC
- 对象比较稳定,不会频繁的触发垃圾回收
- 老年代内存空间不足时,会抛出OOM
永久代
- 指内存的永久储存区域
- 存储程序运行时长期存活的对象,比如类、方法、常量、属性等元数据
- GC不会再程序运行期间对永久代进行清理
- 永久代的内存会随着元数据加载的增加而增加,当超出内存时会遇到OOM错误(这种情况很少,因此不是内存设置的主要区域)
扩展
设置Survivor(幸存者)区的意义?
减少进入老年代的对象,如果不设置幸存者区,那么每一次MinorGC存活的对象都会进入老年代,当老年代内存满了之后会进行MajorGC,会大大的影响程序的性能
为什么要设置两个Survivor(幸存者)区?
防止内存碎片化。
上面我们了解了MinorGC的原理----将Eden和SurvivorFrom中存活下来的对象复制到SurvivorTo区,From区和To区交换;所以每一次存活的对象都会进入一个空的To区,然后依次分配,就保证了分配的内存是连续的,也就不会出现内存碎片化。
如果仅仅设置一个,那么每次MinorGC过后存活的对象进入Survivor区,都会有已经保存在其中的对象,就出现了内存不连续的情况,导致内存碎片化
SurvivorFrom和SurvivorTo区内存大小必须严格相等