Java虚拟机
内存模型
- 堆(Heap):线程共享,所有的对象实例以及数组都要在堆上分配,回收期主要管理和清理的内存空间。
- 方法区(Method Area):线程共享,存储已被虚拟机加载的
类信息
、静态变量
、常量
、即时编译器后的代码
、运行时常量池
。 - Java虚拟机栈(JVM Stack):线程私有,存储局部变量表、操作数栈、动态链接、方法出口,对象指针。
- 本地方法栈(Native Method Stack):线程私有,主要用于虚拟机使用到的Native方法。(比如Java使用C或者C++编写的接口服务时,代码在此区域运行)。
- 程序计数器(Program Counter Register):线程私有,记录当前线程执行的字节码的行号指示器,指向下一条要运行的指令。
JDK8之前HotSpot JVM是使用永久代去实现方法区,永久代是一个连续的堆空间。在JDK8以后,HotSpot JVM使用元空间(Meta Space)实现方法区,类的元信息(metadata)还在,只不过不再是存储在连续的对空间上,而是存储在元空间的本地内存(Native Memory)
动态链接 : 被调用的目标方法再编译期无法被确认,只有在程序运行期间将方法的符号引用转换为直接引用,这种引用转换的过程具备动态性,称为动态链接。
方法的绑定机制分为前期绑定和后期绑定。
堆
堆主要存放 对象实例
和 数组
,而从结构上来说,堆可以分为新生代
和 老年代
,新生代
里分为 Eden
区,From Survivor (S0)
和 To Survivor (S1)
所有新生成的对象首先都省分配存放在 新生代
,需要注意的是,两个Survivor区是对称的,没有先后关系,所以同一个区有可能存在从Eden
区复制过来的对象,和从前一个 Survivor
区复制过来的存活对象。而复制到老年代
的对象只可能是从第一个Survivor
区中复制过来的对象。
新生代(Young Gen)
Eden区
新生成(new)的对象都是存放在 Eden
区,当 Eden区 空间不足时,JVM会出发一次 Minor GC (Young GC)
,此时垃圾回收器会扫描Eden区和From区存活下来的对象,大部分对象会被回收(朝生夕死),存活下来的对象会被复制到Survivor To区中,年龄达到的对象会被直接复制到老年代 (Old Gen)
。
Survivor区
由 From Survivor
和 To Survivor
组成,Survivor区相当于是 Eden区 和 Old Gen的缓冲区,如果没有Survivor区的话,Old Gen(老年代)很快被填满,然后触发 Major GC
, 因为 Major GC(Full GC)
一般都伴随着Minor GC, Survivor区存在的意义就是减少对象进入老年代,从而减少Major GC(Full GC)的频率。
Survivor又分为 From区
和 To区
。JVM每次执行Minor GC的时候,都会将 Eden区
和 Survivor From (S0)
存活下来的对象复制到 Survivor To区
(当To区内存不足的时候,直接进入Old区),并且对对象的年龄值+1,到达一定年龄(年龄 > 15),会晋升到老年代。
15岁这个年龄可以通过虚拟机参数进行配置
-XX:MaxTenuringThreshold
,晋升老年代还有个动态年龄
Hotspot遍历所有对象时,按照年龄从小到大对其所占用的大小进行累积,当累积的 某个年龄大小超过了survivor区的 TargetSurvivorRatio
(一般是0.5 一半) 时,取这个年龄和MaxTenuringThreshold
中更小的一个值,作为新的晋升年龄阈值