根据Java虚拟机规范的定义,JVM的运行时内存区域主要由Java堆、虚拟机栈、本地方法栈、方法区和程序计数器以及运行时常量池组成。其中堆、方法区以及运行时常量池是线程之间共享的区域,而栈(本地方法栈+虚拟机栈)、程序计数器都是线程独享的。
思路: 详细来讲的话,openJDK 默认的虚拟机是hotspot , 不同虚拟机版本,实现上有差异,jdk1.8以后 栈空间定义都差不多,方法去变成了元空间;
jvm虚拟机启动后,tomcat(管理 servlet filter listener)启动时加载contextLoaderListener,初始化spring容器; dispatcherServlet是servlet,由tomcat容器管理,但是需要从spring容器加载;
spring管理的单例对象,会被扫描创建,首先会将类信息 通过类加载器(双亲委派机制),类的信息加载到元空间,
符合引用 (类全限定名 , 字段名成和描述符 方法名称和描述符 , 方法类型和注解) 和 字面量(基本类型值,布尔常量,空引用) 会被放到运行时常量池;
字符串--字面量 加载到字符串常量池(在堆中)
通过反射 创建单例对象,对象被实例化 和 初始化后 ,放在年轻代的 Eden区, 当Eden区内存满了时,会触发yangGC,通过标记--复制算法,复制对象到幸存者0区;
如果Survivor的内存容量也用完,那么存活对象会被移动到老年代
如果老年代内存也满了时,会触发fullGC,使用 标记--整理算法(G1使用此算法 ,CMS垃圾回收器 为了降低STW的时长,也会采用标记清除算法)
java对象不一定都在堆上分配内存,由于有jit优化,根据逃逸分析,方法内创建的对象,没有作为返回值,即是无逃逸,会在栈上分配内存
- 该类所有的实例都已被GC回收。
- 该类的ClassLoader已经被GC回收。 出现这两种情况时,类信息会被卸载;
方法执行时,程序计数器,会保存记录Java虚拟机正在执行的字节码指令的地址, 本地方法栈用来执行native代码(一般是C等语言实现的), 虚拟机栈 相比于堆 比较下,一般几百到几千字节,是先进后出,每执行一个方法会创建一个栈针,被称为压栈,最后调用的方法最先被弹出;
栈上分配的效率很高,可以适合分配局部变量等,可以非常的高效。而堆上的内存分配及回收都会相对复杂