0%

JVM 堆内存 heap 详解

JAVA堆内存管理是影响性能主要因素之一


堆划分

  • JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、老年代(Old Generation),非堆内存就一个永久代(Permanent Generation)
  • 年轻代又分为 Eden 和 Survivor 区。Survivor区由 FromSpace 和 ToSpace 组成。Eden区占大容量,Survivor 两个区占小容量,默认比例是8:1:1
  • 堆内存用途: 存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收
  • 非堆内存用途: 永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等

年轻代(New): 年轻代用来存放JVM刚分配的Java对象
年老代(Tenured): 年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代
永久代(Perm): 永久代存放Class、Method元信息,其大小跟项目的规模、类、方法的量有关,一般设置为128M就足够,设置原则是预留30%的空间

在JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。

永久代有两个参数

  • -XX:PermSize: 永久代初始大小
  • -XX:MaxPermSize: 永久代最大允许大小

元空间也有两个参数:

  • -XX:MetaspaceSize: 初始化元空间大小,控制发生GC阈值
  • -XX:MaxMetaspaceSize: 限制元空间大小上限,防止异常占用过多物理内存

堆内存溢出

在年轻代中经过GC后还存活的对象会被复制到老年代中。当老年代空间不足时,JVM会对老年代进行完全的垃圾回收(Full GC)

如果GC后,还是无法存放从Survivor区复制过来的对象,就会出现OOM(Out of Memory)

OOM(Out of Memory)异常常见有以下几个原因

  • 老年代内存不足: java.lang.OutOfMemoryError:Javaheapspace
  • 永久代内存不足: java.lang.OutOfMemoryError:PermGenspace
  • 代码bug,占用内存无法及时回收

OOM在这几个内存区都有可能出现,实际遇到OOM时,能根据异常信息定位到哪个区的内存溢出

可以通过添加个参数-XX:+HeapDumpOnOutMemoryError,让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后分析。 -XX:HeapDumpPath=${目录} 表示生成DUMP文件的路径,也可以指定文件名称,例-XX:HeapDumpPath=${目录}/java_heapdump.hprof


参考链接