一、五块区域
-
程序计数器
线程私有
用于记录每个线程将程序执行到哪了,下一步该执行哪条字节码指令。
注:如果执行的是一个java方法,计数器记录的是字节码指令的地址,如果是native方法,记录的值为空。 -
本地方法栈
线程私有
本地方法栈存储一些navtive方法的信息,类似于虚拟机栈,虚拟机栈存储java方法的一些信息。 -
虚拟机栈
线程私有
虚拟机栈中存储一个个栈帧,而栈帧存储着方法中的局部变量、操作数栈、动态链接、方法出口等信息。当线程调用一个方法时,就相当于一个栈帧入栈,可以理解为一个方法对应一个栈帧,当方法调用结束,这个栈帧就出栈了。
注:当使用递归方法或调用的方法过多,会报栈溢出异常,因为栈帧数量过多,栈已经存不了了。 -
方法区
线程共享
这里存储了类信息、常量、静态变量、即时编译器编译后的代码等数据 。
这里要着重讲一些持久代、元空间!
持久代和元空间都是对方法区的实现,就像Hibernate和JPA的关系一样。持久代是在Java8之前的,持久代是存储在虚拟机中的,Java8之后元空间就代替了持久代,但是元空间是存储在本地内存中的。经过多方参考,元空间存储着类信息、常量、静态变量等数据。但是字符串常量由方法区移到了堆空间中了。 -
堆
线程共享
当new了一个对象,这个对象都放在这个堆空间中。
堆空间分年轻代和老年代。
年轻代里有三块区域
Eden以及2个Surviver。这两个Surviver作用是为了减少对象存到老年代导致频繁的对老年代进行垃圾清理。
至于为什么是两个Surviver,而不是一个Surviver呢?。
为了防止内存的碎片化
假如只有一个Surviver,Eden区域满了触发Minor GC,然后将对象存到Surviver,当第二次Eden区域满了,触发Minor GC,此时将对象再存到Surviver,但是Surviver已经有了一些对象,再把Eden的对象存到里面时,对象的存储就不是连续的了。
注:MinorGC会将Eden、Survivor区进行垃圾回收。2个Survivor中总有一个Survivor是空的,因为Eden满了,GC过后,会把Eden以及其中一个Survivor存的对象一起移到另一个Survivor区。在Survivor经过15次的GC都没被回收,那么就会将该对象移到老年代。