JVM
jvm结构
类加载机制
-
加载:
-
通过一个类的全限定名来获取定义此类的二进制字节流。
-
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
-
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
存储在方法区中。
-
-
链接阶段:
- 验证: 目的是确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。
验证阶段主要包括四个检验过程:文件格式验证、元数据验证、字节码验证和符号引用验证。 - 准备: 准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。这个阶段中有两个容易产生混淆的知识点,首先是这时候进行内存分配的仅包括类变量(static 修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在java堆中。
- 解析: 解析阶段是虚拟机常量池内的符号引用替换为直接引用的过程。
- 验证: 目的是确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。
-
初始化:初始化阶段是执行类构造器< clinit >()方法的过程。
在以下四种情况下初始化过程会被触发执行:
- 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需先触发其初始化。生成这4条指令的最常见的java代码场景是:使用new关键字实例化对象、读取或设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)的时候,以及调用类的静态方法的时候。
- 使用java.lang.reflect包的方法对类进行反射调用的时候。
- 当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先出发其父类的初始。
- jvm启动时,用户指定一个执行的主类(包含main方法的那个类),虚拟机会先初始化这个类。
双亲委派机制
-
启动类加载器(Bootstrap ClassLoader):负责加载JAVA_HOME\lib目录中并且能被虚拟机识别的类库到JVM内存中,如果名称不符合的类库即使放在lib目录中也不会被加载。该类加载器无法被Java程序直接引用。
-
扩展类加载器(Extension ClassLoader):该加载器主要是负责加载JAVA_HOME\lib\,该加载器可以被开发者直接使用。
-
应用程序类加载器(Application ClassLoader):该类加载器也称为系统类加载器,它负责加载用户类路径(Classpath)上所指定的类库,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
-
自定义类加载器(必须继承 ClassLoader)。
打破双亲委派
- 自定义类加载器,重写loadClass方法;
- 使用线程上下文类加载器;
运行时数据区
栈:
栈帧(Stack Frame):
1.局部变量表
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。
2.操作数栈
当一个方法刚刚执行的时候,这个方法的操作数栈是空的,在方法执行的过程中,会有各种字节码指向操作数栈中写入和提取值,也就是入栈与出栈操作。
3.动态连接
每个栈帧都包含一个指向运行时常量池中该栈帧所属性方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。
4.方法返回地址
当一个方法被执行后,有两种方式退出这个方法。第一种方式是执行引擎遇到任意一个方法返回的字节码指令; 第二种是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理。
5. 附加信息
虚拟机规范允许具体的虚拟机实现增加一些规范里没有描述的信息到栈帧中。
堆:
常量池在堆的老年代
一个对象的生命周期:
在Eden被创建,当Eden区满了的时候s0(或者s1)为空,Eden区和s1(s0)进行垃圾回收,将幸存的对象放入空的s0(s1)区。当一个对象经过多次垃圾回收后幸存时(一般15次),进入老年代。若对象特别大则直接进入老年代。
当老年代也满了,jvm整体内存不够的时候就会进行fullGC,老年代的对象也会被回收。
执行引擎
Java解释器
Java解释器:JVM的一部分。Java解释器用来解释执行Java编译器编译后的程序。java.exe可以简单看成是Java解释器。
JIT
JIT编译(just-in-time compilation)狭义来说是当某段代码即将第一次被执行时进行编译,因而叫“即时编译”。JIT编译是动态编译的一种特例。JIT编译一词后来被泛化,时常与动态编译等价;但要注意广义与狭义的JIT编译所指的区别。
编译优化(栈上替换):
运行过程中会被即时编译器编译的“热点代码”有两类:
1、被多次调用的方法。
2、被多次执行的循环体。
两种情况,编译器都是以整个方法作为编译对象。 这种编译方法因为编译发生在方法执行过程之中,方法栈帧还在栈上,方法就被替换了。
垃圾回收器:
Serial收集器:Serial收集器是最基本、历史最悠久的收集器,是一个单线程的收集器 。
Serial Old收集器: Serial收集器的老年代版本,它同样是一个单线程收集器,使用”标记-整理”算法 。
Parallel Old收集器:是Parallel Scavenge收集器的老年代版本,使用多线程和”标记-整理”算法。
ParNew收集器: ParNew收集器其实就是Serial收集器的多线程版本 。
Parallel Scavenge收集器 :并行的多线程收集器 。
CMS收集器: 收集器是一种以获取最短回收停顿时间为目标的收集器。
- 优点: 并发收集、低停顿 。
- 缺点:
- CMS收集器对CPU资源非常敏感。
- CMS收集器无法处理浮动垃圾,可能出现Concurrent Mode Failure 失败而导致另一次Full GC的产生。
- CMS是一款基于标记-清除算法实现的收集器,所以会产生大量的空间碎片 。
垃圾回收流程
G1垃圾回收器
特点:
-
内存整理
-
并行且并发
-
分代收集
-
可预测的停顿时间模型
缺陷:占用额外内存空间
G1内存管理:依照年代 分Eden、Survivor、Old、Humongous。
内存物理上不在连续,逻辑上连续。每个region都是独立的管理对象,在GC后
G1设置Humongous区域存储巨型对象(大小超过region50%以上),如果一个Humongous装不下,会寻找连续的Humongous区来存储,找不到能存放巨型对象的连续Humougous区域会强制Full GC。
本地方法接口、本地方法仓库
怎么是本地方法?
简单地讲,一个Native Method就 是一个Java调用非Java代码的接口。一个Native Method是这样一个Java方法:该方法的实现由非Java语言实现,比如C。这个特征并非Java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern “C” 告知C++编译器去调用一个C的函数。