JVM将内存区域划分为方法区,堆,虚拟机栈,本地方法栈,程序计数器五块区域。
如下图所示:
JVM内存结构
比较常见的是堆内存和栈内存(虚拟机栈)。
方法区和堆,是线程共享的内存区域,其余三块是线程独享区域,至于为什么分为线程共享和非线程共享,在后续章节中会提及到,请加关注。
堆内存大小可以通过-Xms和-Xmx来进行控制。
其中-Xms表示启动时申请的最小内存,默认为物理内存的1/64,
-Xmx表示可申请的最大内存,默认为物理内存的1/4。
如果剩余的堆内存小于40%时,jvm会自动扩大内存到-Xmx值。
可以通过-XX:MinHeapFreeRation选项来定义比例。
如果剩余堆内存大于70%时,jvm对减小堆内存到-Xms值。
可以通过XX:MaxHeapFreeRation选项来定义比例。
在实际项目中,为了避免JVM频繁调整堆内存大小,一般来讲,-Xms与-Xmx的值设成一样。
堆内存 = 新生代+老生代+持久代
新生代
主要是用来存放新生的对象。一般占据堆的1/3空间,新生代又分为 Eden区、ServivorFrom、ServivorTo三个区。新生代可能会频繁触发MinorGC进行垃圾回收。关于MinorGC的过程,在后续章节中会提及到,请加关注。
老年代
老年代的对象比较稳定,所以MajorGC不会频繁执行,MajorGC采用标记—清除算法,详细过程也会在后续章节中提到,请加关注。
永久代
指内存的永久保存区域,主要存放Class和Meta(元数据)的信息。GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。在Java8之前,通常会出现这种情况。
Java8永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代.
元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,因此不会出现OOM异常。