JVM入门学习笔记——上篇:内存与垃圾回收(10-13)

文章目录

  • 10、对象的实例化内存布局与访问定位
      • 大厂面试题
    • 一、对象的实例化
      • 创建对象的方式
      • 创建对象的步骤
    • 二、对象的内存布局
    • 三、对象的访问定位
      • 对象访问的方式
        • 句柄访问
        • 直接指针(Hotspot采用的方式)
  • 11、直接内存(Direct Memory)
    • 一、直接内存概述
  • 12、执行引擎
    • 一、执行引擎概述
      • 执行引擎的工作过程
    • 二、Java代码编译和执行的过程
    • 三、机器码、指令、汇编语言
      • 机器码
      • 指令与指令集
      • 汇编语言
      • 高级语言
      • 字节码
    • 四、解释器
      • 解释器的工作机制(或工作任务)
      • 解释器分类
      • 现状
    • 五、JIT编译器
      • Java代码的执行分类
      • 问题
      • Hotspot JVM的执行方式
      • 案例
      • JIT编译器
      • 热点代码及探测方式
      • 方法调用计数器
      • 回边计数器
      • Hotspot VM可以设置程序执行的方式
      • Hotspot VM中JIT的分类
      • 分层编译策略
      • C1和C2编译器不同的优化策略
      • 总结
        • Graal编译器
        • AOT编译器
  • 13、StringTable
    • 一、String的基本特性
      • String在jdk1.9中存储结构变更
    • 二、String的内存分配
    • 三、String 的基本操作
    • 四、字符串拼接操作
      • 例1
      • 例2
      • 例3
      • 例4
      • 例5
      • 例6
    • 五、intern()的使用
      • jdk6 vs jdk7/8
        • 题1
        • 题2
        • 练习1
        • 练习2
      • intern()的效率测试:空间角度
    • 六、StringTable的垃圾回收
    • 七、G1中的String去重操作

10、对象的实例化内存布局与访问定位

大厂面试题

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第1张图片

一、对象的实例化

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第2张图片

创建对象的方式

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第3张图片

创建对象的步骤

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第4张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第5张图片

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第6张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第7张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第8张图片

二、对象的内存布局

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第9张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第10张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第11张图片

三、对象的访问定位

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第12张图片
图示为上图。
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第13张图片

对象访问的方式

句柄访问

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第14张图片
好处
在这里插入图片描述
但是,直接指针比句柄访问效率高。

直接指针(Hotspot采用的方式)

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第15张图片

11、直接内存(Direct Memory)

一、直接内存概述

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第16张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第17张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第18张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第19张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第20张图片
在这里插入图片描述
简单理解:进程中所占的内存空间 = 堆空间 + 本地内存

12、执行引擎

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第21张图片

一、执行引擎概述

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第22张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第23张图片

执行引擎的工作过程

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第24张图片
在这里插入图片描述

二、Java代码编译和执行的过程

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第25张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第26张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第27张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第28张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第29张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第30张图片

三、机器码、指令、汇编语言

机器码

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第31张图片

指令与指令集

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第32张图片

汇编语言

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第33张图片

高级语言

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第34张图片

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第35张图片

字节码

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第36张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第37张图片

四、解释器

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第38张图片

解释器的工作机制(或工作任务)

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第39张图片

解释器分类

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第40张图片

现状

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第41张图片

五、JIT编译器

Java代码的执行分类

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第42张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第43张图片

问题

Hotspot JVM的执行方式

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第44张图片

案例

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第45张图片

JIT编译器

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第46张图片

热点代码及探测方式

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第47张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第48张图片

方法调用计数器

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第49张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第50张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第51张图片

回边计数器

在这里插入图片描述
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第52张图片

Hotspot VM可以设置程序执行的方式

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第53张图片

Hotspot VM中JIT的分类

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第54张图片
C2编译器是用C++编写的。

分层编译策略

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第55张图片

C1和C2编译器不同的优化策略

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第56张图片

总结

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第57张图片

Graal编译器

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第58张图片

AOT编译器

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第59张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第60张图片

13、StringTable

一、String的基本特性

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第61张图片

String在jdk1.9中存储结构变更

JDK1.9前使用char数组,JDK1.9使用byte数组。

机翻:
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第62张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第63张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第64张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第65张图片

二、String的内存分配

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第66张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第67张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第68张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第69张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第70张图片

三、String 的基本操作

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第71张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第72张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第73张图片

四、字符串拼接操作

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第74张图片

例1

String s1 = "a"+"b"+"c";	//等同于"abc"
String s2 = "abc";			//"abc"一定是放在字符常量池中,将此地址赋给s2

/*最终.java编泽成.class,再执行.class
 * String s1 = "abc";
 * String s2 = "abc";
 */
 
System.out.println(s1 == s2);		//true
System.out.println(sl.equals(s2));	//true

例2

String s1 = "JavaEE";	//变量
String s2 = "hadoop";	//变量
String s3 = "JavaEEhadoop";
String s4 = "JavaEE" + "hadoop";	//编泽优化
//如果拼接符号的前后出现了变量,则相当于在堆空间中new String(),具体的内容为拼按的结果: JavaEEhadoop
String s5 = s1 + "hadoop";	//有变量,所以为new对象,在堆空间
String s6 = "JavaEE" + s2;	//有变量,所以为new对象,在堆空间
String s7 = s1 + s2;		//有变量,所以为new对象,在堆空间

System.out.println(s3 == s4);	//true
System.out.println(s3 == s5);	//false
System.out.println(s3 == s6); 	//false
System.out.println(s3 == s7);	//false
System.out.println(s5 == s6); 	//false
System.out.println(s5 == s7); 	//false
System.out.println(s6 == S7);	//false

// intern():判断字符审常量池中是否存在JavaEEhadoop值,如果存在,则返回常量池JavaEEhadoop的地址;
//如果字符常量池中不存在JavaEEhadoop,则在常量池中加载一份 JavaEEhadoop,并返回此对象的地址
String s8 = s6.intern();

System.out.println(s3 = s8); //true

例3

String s1 = "a";	//变量
String s2 = "b";	//变量
String s3 = "ab";
/*
 * 如下的s1+s2的执行细节:
 * ① StringBuilder s = new StringBuilder();	//匿名的形式
 * ② s.append("a")
 * ③ s.append("b")
 * ④s.toString() -> 约等于 new String("ab")
 * 
 * 补充:在jdk5.0之后使用的是StringBuilder,在jdk5.0之前使用的是StringBuffer
*/
String s4 = s1 + s2;	//有变量,所以为new对象,在堆空间
System.out.println(s3 == s4);	//false

例4

final string s1 = "a";	//常量
final String s2 = "b";	//常量
String s3 = "ab";
/*
 * 1. 字符拼按操作不一定使用的是StringBuilder!
 * 如果拼按符号左右两边都是字符串常量或常量引用,则仍然使用编泽期优化,即非StringBuilder的方式。
 * 2. 针对于final修饰类、方法、基本数据类型、引用数据类型的量的结构时,能使用上final的时候建议使用上。
*/
String s4 = s1 + s2;	//都是常量,编译期优化,所以在堆空间的字符串常量池
System.out.println(s3 == s4);	//true

例5

String s1 = "JavaEEhadoop";
String s2 = "JavaEE";
String s3 = s2 + "hadoop";
System.out.println(s1 == s3);	//false

final String s4 = "JavaEE"; 	//s4为常量
String s5 = s4 + "hadoop";
System.out.println(s1 == s5);	//true

例6

/*
 * 体会执行效率:通过StringBuilder的append()的方式添加字符串的效率要远高于使用String的字符审拼接方式!
详情:
① StringBuilder的append()的方式;自始至终中只创建过一个StringBuilder的对象
使用String的字符拼接方式:创建过多个StringBuilder和String的对象
② 使用String的字符审拼接方式:内存中于创建了较多的 StringBuilder和String的对象,内存占用更大;如果进行GC,需要花费额外的时间。

因为StringBuilder的空参构造器在底层默认创建了一个长度为16的数组,当数组长度不够时,会进行扩容,创建更大容量的数组,并把原数组的值copy到新创建的数组里,那些长度不够的数组也会占有内存,所以有改进的空间。

改进空间:在实际开发中,如果基本确定要前前后后添加的字符串长度不高于某个限定值highLevel的情况下建议使用构造器
StringBuilder s = new StringBuilder(highLevel);	//new char[lhighLevel]
*/
public void method01(int highLevel){
	String src = "";
	for(int i = 0; i < highLevel; i++){
		src += "a";	//每次循环都会创建一个StringBuilder对象和一个String对象
	}
}

public void method02(int highLevel){
	//StringBuilder src = new StringBuilder();
	StringBuilder src = new StringBuilder(highLevel);	//指定底层创建的数组的长度
	for(int i = 0; i < highLevel; i++){
		src.append("a");
	}
}

五、intern()的使用

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第75张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第76张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第77张图片

jdk6 vs jdk7/8

题1

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第78张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第79张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第80张图片

题2

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第81张图片

//例1
String s1 = new String("1");     //此时会去字符串常量池中创建"1"
s1.intern();     //调用此方法前,字符串常量池中已经存在"1",所以调用此方法没有什么意义
String s2 = "1";
//s1是堆空间的String对象,而s2是堆空间字符串常量池中的"1"
System.out.println(s1 == s2);    //jdk6: false   jdk7/8: false

//----------------------------------

//例2
String s3 = new String("1") + new String("1");  //s3变量记录的地址为:new String("11")
//执行完上一行代码后,此时字符串常量池中是不存在"11"的
s3.intern();    //在字符串常量池中生成"11"
/* s3.intern()的理解:
    jdk6:在字符串常量池中创建了一个新的对象"11",也就有了新地址(字符串常量池在方法区(永久代)中)
    jdk7/8:此时并不会在字符串常量池中创建"11",而是创建一个指向堆空间new String("11")的地址,
            即字符串常量池中"11"其实只是一个引用,指向堆空间s3的地址(字符串常量池在堆中)
 */

String s4 = "11";   //s4变量记录的地址:为上一行代码执行后生成的"11"的地址
System.out.println(s3 == s4);   //jdk6: false   jdk7/8: true

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第82张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第83张图片

//例2拓展
String s3 = new String("1") + new String("1");  
String s4 = "11";   //位置向上提一行,因为此时字符串常量池中没有"11",所以会去创建"11"对象
String s5 = s3.intern();    //字符串常量池中已经有"11"了,所以返回已存在的"11"的地址
System.out.println(s3 == s4);  	//false
System.out.println(s5 == s4);  	//true

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第84张图片

练习1

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第85张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第86张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第87张图片

练习2

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第88张图片

intern()的效率测试:空间角度

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第89张图片
在这里插入图片描述
第一种会在堆空间new一千万个String对象,在字符串常量池中new"1"到"10"十个对象,数组引用的是这一千万的String对象。
而第二种虽然也会在堆空间new一千万个String对象,在字符串常量池中new"1"到"10"十个对象,但是,数组里是引用字符串常量池中对象的地址的,new出来的一千万个String对象也就没被引用,有些对象就会被GC,从而节省内存空间。
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第90张图片

六、StringTable的垃圾回收

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第91张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第92张图片
在这里插入图片描述

七、G1中的String去重操作

JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第93张图片
http://openjdk.java.net/jeps/192

去重是对堆空间的String对象而言的,字符串常量池中没有重复的字符串。
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第94张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第95张图片
JVM入门学习笔记——上篇:内存与垃圾回收(10-13)_第96张图片

下一篇笔记:待更新

学习视频(p102-p133):https://www.bilibili.com/video/BV1PJ411n7xZ?p=102

你可能感兴趣的:(JVM)