JVM
JVM分三部分:
类装载子系统
- 加载
类加载过程的一个阶段,ClassLoader通过一个类的完全限定名查找此类字节码文件,并利用字节码文件创建一个class对象。 - 验证
目的在于确保class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身的安全,主要包括四种验证:文件格式的验证,元数据的验证,字节码验证,符号引用验证。 - 准备
为类变量(static修饰的字段变量)分配内存并且设置该类变量的初始值,(如static int i = 5 这里只是将 i 赋值为0,在初始化的阶段再把 i 赋值为5),这里不包含final修饰的static ,因为final在编译的时候就已经分配了。这里不会为实例变量分配初始化,类变量会分配在方法区中,实例变量会随着对象分配到Java堆中。 - 解析
这里主要的任务是把常量池中的符号引用替换成直接引用 - 初始化
这里是类记载的最后阶段,如果该类具有父类就进行对父类进行初始化,执行其静态初始化器(静态代码块)和静态初始化成员变量。(前面已经对static 初始化了默认值,这里我们对它进行赋值,成员变量也将被初始化)
字节码执行引擎
- 执行字节码文件,
- 并且修改程序计数器,推动程序运行,
- 还会启动GC垃圾收集线程
GC
- GC回收算法是可达性分析算法,他会从GC ROOT对象向下查找引用的对象,直到找不到引用的对象,在最后一个对象和ROOT之间的对象都是非垃圾对象
- 在第一次minor GC回收后,垃圾对象会在eden区被回收,非垃圾对象会被放进S0区,此时非垃圾对象就是存活者
- 当eden快满的时候,再次触发GC回收,此时重复第一次GC回收操作,s0中的非垃圾对象会被放进s1,垃圾对象被回收
- 每回收一次,都会在对象头里面记下次数,简称年龄,当到达15岁时,对象将被移入老年区, 进入老年代
- 当老年代满了的时候,执行引擎会开一个full gc线程,对整个堆进行一次回收,如果此时老年代区满了,则会OOM
老年代GC:FullGC 是老年代的GC,在新生代如果说存在的对象或者说新创建 出来的对象由于某些原因需要移动到老年代中,但是老年代中压根就没有这么大的内存空间去容纳这个对象, 那么就会引发一次FullGC,如果在执行完FullGC之后,还是没有办法给这些对象分配内存,那么凉了,该抛出异常了,异常类型就是OutOfMemoryError。
FullGC危害:在发生FULL GC的时候,意味着JVM会安全的暂停所有正在执行的线程(Stop The World),来回收内存空间,在这个时间段内,所有除了回收垃圾的线程外,其他有关JAVA的程序,代码都会静止,反映到系统上,就会出现系统响应大幅度变慢,卡机等状态。
运行时数据区(内存模型)
- 虚拟机栈
- 每个线程都会在其中划分一块内存空间,各自独立
- 每个线程栈的数据结构都是先进后出FILO的
- 每个方法都是一个栈帧,main方法最先入栈,因为他是程序最后返回的值,毫无疑问,他是第一个入栈的
- 各个方法按照main方法调用次序,依次入栈,每个方法栈帧里面都有4个部分
- 局部变量表
里面存放着方法中局部变量 - 操作数栈
用来进行变量运算的一个栈空间,如加法,会将2个值相加后返回 - 动态链接
里面存放着对象在堆中的内存空间地址 - 方法出口
存放方法最后的返回值,将其返回给调用的方法栈帧
- 局部变量表
- 本地方法栈
同虚拟机栈,也会有对应的局部变量等等,但他是C/C++实现 - 堆
存放new的对象,方法区和栈都会引用他的内存地址 - 方法区(元空间)
又称永久代,JDK1.8后称为元空间,里面存放着常量+静态变量+类信息,其中静态变量存的内存空间地址,引用的堆. - 程序计数器
就是字节码文件中的开头行标识,由执行引擎修改,来推动程序运行