堆的核心概念
-
Java堆区在JVM启动的时候即被创建。是JVM管理的最大一块内存空间,是GC执行垃圾回收的重点区域。
-
一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域,GC和OOM都存在。
-
《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。
-
所有的线程共享Java堆,但并不是共享堆的全部,内部同样存在每个线程私有的缓冲区(如:ThreadLocal Allocation Buffer,TLAB)。
-
《Java虚拟机规范》中对Java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上。因为栈帧中保存的是引用,这个引用指向对象或者数组在堆中的位置。我要说的是:"几乎"所有的对象实例都在这里分配内存,有逃逸现象。
-
在方法结束后,堆中的对象不会马上被移除,只有在垃圾收集的时候才会被移除。
堆空间大小的设置
-
-Xms
:设置堆区的起始内存,等价于-XX:InitialHeapsize
-
-Xmx
:设置堆区的最大内存,等价于-XX:MaxHeapSize
-
一旦堆区中的内存大小超过
-Xmx
所指定的最大内存时,将会抛出OutOfMemoryError
错误。 -
通常会将
-Xms
和-Xmx
两个参数配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新计算分配堆区的大小,从而提高性能。 -
默认情况下,堆空间初始内存大小: 物理电脑内存大小 / 64,最大内存大小: 物理电脑内存大小 / 4
堆内存结构细分
现在的垃圾收集器大部分都基于分代收集理论设计,JVM堆空间可细分为:
-
JDK7及以前:堆内存逻辑上分为:新生代 + 老年代 + 永久代、TLAB
- Young Generation Space 新生代 Young/New:内部又划分为Eden、Survivor0、Survivor1三个区域
- Tenure Generation Space 老年代 Old/Tenure
- Permanent Space 永久代 Perm
- TLAB
-
JDK8之后:堆内存逻辑上分为:新生代 + 老年代 + 元空间、TLAB
- Young Generation Space 新生代 Young/New:内部又划分为Eden、Survivor0、Survivor1三个区域
- Tenure Generation Space 老年代 Old/Tenure
- Mete Space 元空间 Meta
- TLAB
约定:
- 新生代 <–> 新生区 <–> 年轻区 --> 新生代
- 老年代 <–> 老年区 <–> 养老区
- 永久区 <–> 永久代
新生代与老年代
-
Java堆区进一步细分的话,可以划分为年轻代(YoungGen)和老年代(oldGen),默认占比为:1:2
-
几乎所有的Java对象都是在Eden区被new出来的。GC对绝大部分的Java对象的回收也是在新生代进行的。
-
其中年轻代又可以划分为Eden空间、Survivor0空间和survivor1空间(有时也叫做from区、to区) ,默认占比为:8:1:1(官网上说是8:1:1,但实际查看是6:1:1,因为存在自适应内存分配策略)。
设置新生代与老年代在堆结构的占比(这个参数在开发中一般不会调)
-XX:NewRatio=X
,默认为2,表示新生代占1,老年代占2,新生代占整个堆的1/3,可以修改-XX:NewRatio=4
,表示新生代占1,老年代占4,新生代占整个堆的1/5XX:SurvivorRatio=X
:设置新生代中Eden区和Survivor区的比例。-XX:-UseAdaptiveSizePolicy
: 关闭自适应内存分配策略,-XX:+UseAdaptiveSizePolicy
:表示打开-Xmn
:新生代最大内存大小,这个设置的优先级大于设置比例。