堆的简介
堆是JVM虚拟机中最大的一块内存区域,它在JVM启动时被创建,主要用于存储实例对象的地址,堆的空间大小是可以在启动JVM前设置,是GC(垃圾回收机制)重点照顾的一块区域,会通过GC将堆中不需要的数据进行垃圾回收,以减少空间使用.
堆的体系结构图
堆空间逻辑上主要分为三大部分:1.新生代 2.老年代 3.方法区(持久代,元空间)
新生代(Young generation)
新生代占堆空间的1/3,而新手代又包括三个部分:1.Eden伊甸园 2.S0幸存者0区 3.S1幸存者1区
这三个部分在新生代中的空间占比分别为,
E
d
e
n
:
S
0
:
S
1
=
8
:
1
:
1
Eden:S0:S1=8:1:1
Eden:S0:S1=8:1:1
老年代(Old generation)
老年代占堆空间的2/3,是新手代空间的2倍
既然都是堆空间的部分,都是用来存储实例对象,那为什么要分新手代和老年代呢?
这就涉及到了实例对象的创建到销毁,以及垃圾回收机制GC,~~~请继续往下看
方法区(Method Area)
关于方法区的作用,可以看下面这篇博客JVM内存模型中的方法区
1.在Java8之前方法区的实现是永久代,永久代是堆空间的一部分,占用这堆分配的内存
2.在Java8之后方法区的实现是元空间,元空间是在本地内存中
堆中的GC机制
刚new出来的实例对象一开始是存放在伊甸园区中的,但是实例对象不可能一直保存在堆中,当不需要这个对象的时候就会出发GC,将该对象作为垃圾进行回收
GC分为两种:1.Minor GC(发生在新手代的GC) 2.Major GC也称Full GC(发生在老年代的GC)
流程大致如下:
1.Eden区满了之后触发Minor GC,存活的对象放到Survivor幸存者区
2.Survivor满了之后触发Minor GC,存活的对象放到Old老年区
3.Old老年区满了之后触发MajorGC,当多次触发Major GC后,老年区以及腾不出空间了,此时Major
GC也没有用,堆就会报OOM异常
堆内存调优
查看自己的堆内存大小
public class OOM {
public static void main(String[] args) {
long mx = Runtime.getRuntime().maxMemory();//单位是字节
long ms = Runtime.getRuntime().totalMemory();//单位是字节
System.out.println("堆内存初始大小为:"+(ms/1024.0/1024.0)+"MB");
System.out.println("堆内存最大为大小为:"+(mx/1024.0/1024.0)+"MB");
}
}
堆内存发生OOM
首先设置堆内存大小
public class OOM {
public static void main(String[] args) {
String s="";
while(true){
s+="1233333";
}
}
}
GC回收日志
从GC回收日志可以看出,分别触发了GC和Full GC,并且可以看出堆空间的组成充分,分别是
PSYoungGen,ParOldGen和Metaspace,分别是新手代,老年代,元空间