知识点总结于b站宋红康老师,视频链接
文章目录
- 1.执行引擎
- 1.1.执行引擎概述
- 1.2.执行引擎的工作流程
- 1.3.jvm的解释器和即时编译器(JIT)
- 1.3.1.解释器,即时编译器概述
- 1.3.2.常见即时编译器
- 1.3.3.热点代码探测
- 1.String Table
- 1.1 string的基本特性
- 1.2.string的不可变性
- 1.3.string对象创建的理解
1.执行引擎
- 执行引擎是Java虚拟机核心的组成部分之一。
- “虚拟机”是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器、缓存、指令集和操作系统层面上的,而虚拟机的执行引擎则是由软件自行实现的,因此可以不受物理条件制约地定制指令集与执行引擎的结构体系,能够执行那些不被硬件直接支持的指令集格式。
1.1.执行引擎概述
- JVM的主要任务是负责装载字节码到其内部,但字节码并不能够直接运行在操作系统之上,因为字节码指令并非等价于本地机器指令,它内部包含的仅仅只是一些能够被TVM所识别的字节码指令、符号表,以及其他辅助信息。
- 那么,如果想要让一个Java程序运行起来,执行引擎(Execution Engine)的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。简单来说,JVM中的执行引擎充当了将高级语言翻译为机器语言的译者。
1.2.执行引擎的工作流程
- 执行引擎在执行的过程中究竟需要执行什么样的字节码指令完全依赖于Pc寄存器。
- 每当执行完一项指令操作后,Pc寄存器就会更新下一条需要被执行的指令地址。
- 当然方法在执行的过程中,执行引擎有可能会通过存储在局部变量表中的对象引用准确定位到存储在Java堆区中的对象实例信息,B以及通过对象头中的元数据指针定位到目标对象的类型信息。
1.3.jvm的解释器和即时编译器(JIT)
1.3.1.解释器,即时编译器概述
-
解释器:当Java虚拟机启动时会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容“翻译”为对应平台的本地机器指令执行。
-
JIT (Just In Time Compiler)编译器:就是虚拟机将源代码直接编译成和本地机器平台相关的机器语言。
说人话就是解释器是用一行就解释一行,而jit则是把程序转换为底层的机器码后再用
毫无疑问,由于即时编译器翻译了更多的底层语言,那么运行速度是要明显高于解释器的,但是它也需要花费更长时间进行编译,并且使用更多的内存,因此即时编译器在硬件充裕的服务器上是大量使用的
1.3.2.常见即时编译器
- Java 语言的“编译期”其实是一段“不确定”的操作过程,因为它可能是指一个
前端编译器(其实叫“编译器的前端”更准确一些)把.java文件转变成.class文件的过程; - 也可能是指虚拟机的
后端
运行期编译器(JIT编译器,Just In Time Compiler)
把字节码转变成机器码的过程。 - 还可能是指使用静态
提前
编译器(AOT编译器,Ahead of Time Compiler)直接
把.java文件编译成本地机器代码的过程。
前端编译器: sun的 Javac、Eclipse JDT中的增量式编译器(ECJ)。JIT编译器:HotSpot VM的c1、c2编译器。
AOT编译器:GNU Compiler for the Java (GcJ) 、Excelsior JET。
1.3.3.热点代码探测
- 一个被多次调用的方法,或者是一个方法体内部循环次数较多的循环体都可以被称之为“热点代码”,因此都可以通过JIT编译器编译为本地机器指令。由于这种编译方式发生在方法的执行过程中,因此也被称之为栈上替换,或简称为oSR (on StackReplacement)编译。
- 一个方法究竟要被调用多少次,或者一个循环体究竟需要执行多少次循环才可以达到这个标准?必然需要一个明确的阈值,JIT编译器才会将这些“热点代码”编译为本地机器指令执行。这里主要依靠热点探测功能。
目前HotSpotVM所采用的热点探测方式是基于计数器的热点探测。
- 采用基于计数器的热点探测,HotSpot VM将会为每一个方法都建立2个不同类型的
- 计数器,分别为方法调用计数器(Invocation Counter)和回边计数器(Back
Edge Counter) 。
- 方法调用计数器用于统计方法的调用次数
- 回边计数器则用于统计循环体执行的循环次数
1.String Table
1.1 string的基本特性
- string声明为
final
的,不可被继承 - string实现了
serializable
接口:表示字符串是支持序列化的。 - 实现了
comparable
接口:表示string可以比较大小 - string在jdk8及以前内部定义了final char[ ] value用于存储字符串数据。jdk9时改为
byte[ ]
(更省内存) - 字符串常量池中是不会存储相同内容的字符串的。
string的string Pool是一个固定大小的Hashtable,默认值大小长度
是1009。如果放进string Pool的string非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用string.intern时性能会大幅下降。 - 使用-xx :StringTablesize可设置stringTable的长度
- 在jdk6中stringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。stringTablesize设置没有要求在jdk7中,stringTable的长度默认值是60013,1009是可设置的最小值。
1.2.string的不可变性
- string:代表不可变的字符序列。简称:不可变性。
- 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有
的value进行赋值。 - 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
- 当调用string的replace ()方法修改指定字符或字符串时,也需要
重新指定内存区域赋值,不能使用原有的value进行赋值。
- 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
1.3.string对象创建的理解
1. 直接引用创建
String a = "123";
String b = "123";
第一次
会把“123”放置到字符串常量池,因此a,b引用都指向同一个字符串对象。
2. ”+“拼接字符串
String a = "123";
String b = "345";
String c = a + b;
String d = "123" + "345";
System.out.println(a + b == "123345"); //输出为false
System.out.println(d == "123345");//输出为true
当没有出现变量进行拼接时拼接完的字符串会放置到字符串常量池,一旦对象中出现变量,则会通过StringBuilder的append拼接两个字符串,然后通过toString方法再new出新的对象。
3. ”new String“拼接字符串
String a = new String("a");
new String创建两个对象,一个在常量池,一个在堆里。
String c = new String("a") + new String("b");
每个new String创建两个对象,最后通过 StringBuilder拼接,然后再通过toString取得新的字符串。(StringBuilder拼接得到的字符串不在常量池)
4. ”a.intern()“方法要点
a.intern()方法是如果常量池中有字符串则返回改字符串引用,反之则创建字符串放进常量池
注意:在jdk6中,该方法会在常量池没有该字符串的情况下,完全创建一个全新的字符串。而在jdk7以后,则常量池引用指向该对象,因此6的时候会多创建一个对象。