1.Java程序被编译器编译成Class文件

2.任何一个class文件都对应着唯一一个类或接口得定义信息,但是类或接口不一定都得在Class文件里面(类或接口页可以动态生成,之间送入类加载器中)。

3.class文件是一组以8个自己为单位得二进制流,类似于C语言得伪结构来存储数据,只有两种数据无符号数和表。

4.无符号有u1,u2,u4,u8来表示,可以描述数字,索引引用,数量值,或者UTF-8构成得字符串值。表是有多个无符号数或者其他表作为数据项构成的复合数据类型。

6.无论是无符号数还是表,当需要描述同一类型但是数量不定的多个数据时,经常使用一个计数器跟随连续的数据项来表示。

7.class开头是魔数+版本号。紧接着是常量池入口,它是class文件结构中与其他项目关联最多的数据。

8.常量池=U2(计数器)+U2数量的常量,常量池主要字面量和符号引用。字面量就是java语言层面的常量,而符号引用包括:被模块导出或开发的包,类和接口的全限定名,字段的名称和描述符,方法的名称和描述符,方法句柄和方法类型,动态调用点和动态常量。

9.class文件不会保存方法,字段,这些不经过虚拟机在运行期转换的化是无法得到真正的内存入口地址。当虚拟机做类加载时,会从常量池获得对应得符号引用,再在类创建时或运行时解析和翻译到具体的内存地址中。

10.常量池的每一项都是一个表,截至JDK13,常量表中有17种不同类型的常量有(1-17)的标志位tag,比如1就是CONSTANT_UTF8_ibfo(utf8编码的字符串),7就是类或接口的标志。

11.常量表是完全独立的数据结构,而它们都是以标志为开头 比如代表的是类或接口常量表=tag+ name_index(常量池的tag标志索引)

12 常量池结束后,紧接着是两个字节代表访问标志(比如public)

13. 表示一个类是通过类索引(this_class)和父类索引(super_class)和接口索引(interface)组成,数据表示就是u2+u2+u2集合,u2集合就是因为接口可以多实现。

14. 字段表用于描述接口或类中声明的变量包括类级变量和实例变量,但不包括方法内部声明的局部变量。

15. 方法表则描述方法。

16.类 ,字段表,方法表都包含自己的属性表,比如方法表的属性表code里面可以包含exceptions属性,表示throws关键字后面列举的异常,类可以包含ConstantValue属性表示static修饰的变量

17.  指令应该是操作码+操作数(此操作需要的参数)组成,而因为Java虚拟机面向操作数栈而不是寄存器,所以操作码只有一个字节(8位),意味着指令集的数量不能超过256条。

18.Java虚拟机的指令集中,大多数指令就包含了其操作所对应的数据类型,比如iload用于从局部变量表中加载int型的数据到操作数栈中。

19. 加载和存储指令是用于将数据在栈帧中局部变量表和操作数栈中来回传输。运输指令,类型转换指令,对象创建以及访问指令

20 同步指令,方法级和方法内部的同步,都是通过管程monitor来实现,

21 方法级的同步是隐式的,虚拟机从方法表中的拿到ACC_SYNCHRONIZED访问标志得知是否需要同步,如果设置了,执行线程就要求先成功持有monitor才能执行方法。

22.同步一段代码,则是通过monitorenter和monitorexit指令来支持的,并且需要Java虚拟机和编译器协同支持,方法调用中,每个monitorenter都必须都对应的monitorexit。当发生异常的时候,编译器会自动产生一个异常处理程序,它的目的就是来执行monitorexit的。

23 类加载,由于类型的加载,连接和初始化都是在运行期完成的,所以,用户可以自定义类加载器,让某个应用在运行时加载一个外部二级制流文件位程序代码的一部分。

24类加载声明周期,加载,连接,初始化,使用,卸载。

25 连接包括 验证,准备,解析。加载 验证,准备,初始化,卸载

26.Java虚拟机规定,有且只有六种情况,如果类型没有初始化则必须触发其初始化(而加载,验证,准备自然要在初始化之前开始)。

1) 使用new 关机子实例化对象,读取或者设置一个类型的static字段(被final、已经把结果让入常量池的字段除外),调用静态方法时。

2) 使用反射时

3) 初始化类时,发现其父类还没有初始化,先初始化父类

4) 执行main的类

5)加入动态语言支持,java.lang.invoke,MethodHandle实例最后解析结果方法句柄为REF_putStatic等,方法句柄对应的类没有初始化

6)被default修饰的接口,如果这个接口实现类初始化,那么这个接口要在其之前被初始化。

27 加载,通过类的全限定名获取类并完成加载

28  验证,文件格式验证,元数据验证,字节码验证(验证程序语义是否符合逻辑等等)

29.准备,为类中定义的变量(static变量),分配内存并设置初始值(0)的阶段,jdk7被分配到方法区,jdk8被分配的队中。

30解析,将常量池中的符号引用(各种常量池表),变成直接引用(能够定位到目标的指针,或者间接定位到目标的句柄)。这里主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点等

31 初始化,jvm真正执行 类中的代码,对所有变量赋值。

32 类加载器,被同一个类加载器加载的同一个类,才能确定类相等。

33. 三层类加载器,bootstrap  extensionapplication。

34.双亲委派模型,如果一个类收到了类加载请求,它首先不会自己去舱室加载这个类,而是把请求委派给父类加载器,因此加载请求最终都会传到最顶层的类加载器中,如果父类加载不了,子加载器才会加载,这样的好处是,java类随着它的类加载器一起具备了一个带有优先级的层次关系,因此可以保证类是唯一的,比如Object,如果用户定义一个Object,如果没有使用双亲委派,系统中就会出现多个Object。 避免重复加载+避免核心类篡改

35.破坏双亲委派模型

1) jdk1.2引入的双亲委派模型,而类加载器之前就存在,为了兼容已有代码,避免loadClass被子类覆盖,jdk1.2加入了findClass,并引导用户编写类加载尽可能使用findClass,而不是修改loadClass.

2)由于自身模型的缺陷,双亲委派模型解决了各个类的写作基础类型一致性问题(越基础月上层的加载器加载),而基础类型又总是被继承和调用,但是基础类型又要调回用户代码。比如JIND(对资源查找和集中管理),需要器加载用户classPath的SpI(Sevice Provider interface SPI)。问题来了,启动类加载器绝不可能认识加载这些代码的,为了解决这个问题,设置了thread Context ClassLoader线程上下文加载器。JNDI服务,就是通过父亲加载器去请求子加载器完成类加载,这就是逆向。 当SPI服务提供者多于1个时,代码只能根据具体提供者的类型来硬编码判断。 为了消除这种方式,JDK6提供了 META-INF/services的配置,通过责任链模式,才是给SPI提供相对合理的方案

3)由于热部署,比如OSGI,每个bundle都有自己的类加载器,当需要更换bundle,连同类加载器一起换掉,在OSGI中,不是双亲委派,而是复杂的网状结构。

36 .虚拟机执行引擎,jvm解释执行和编译执行一起。

37.Java虚拟机以方法作为最基本的执行单元,栈帧存储了方法的局部变量表、操作数栈、动态链接和方法返回地址等信息。

38.局部变量表,存放参数和方法内部定义的局部变量。局部变量表的0位索引是对象实例的引用this.局部变量表的重用(重叠)是值,PC计数器的值已经超过了某个变量的作用域,那么这个变量的槽就可以给其他变量重用

39 Java虚拟机解释执行引擎,就是基于栈的执行引擎,

40  一个完善的web服务器,需要解决如下几个问题

1) 资源隔离,两个不同的应用程序也许需要依赖同一个第三方库的不同版本。

2)资源共享,两个相同的应用程序依赖的同一个三方库,不需要加载两次。

3) 支持jsp的服务器,需要支持热替换。

41 tomcat为了支持,则提供了不同的目录 common ,Web。然后为了支持这套目录结构 ,对类进行加载和隔离,Tomcat就自定了多个类加载器,按照双亲委派模型,比如shared类加载器,jsp类加载器,jsp加载器,就是每个jsp文件对应一个类加载器,在热部署的时候,卸载类加载,重新生成一个类加载器实例即可。

前端编译

42 jvm的前端编译,javac编译器把*.java变成*.class过程

即时编译器hotspot 虚拟中的c1/c2/graal,运行期间把字节码转变为本地码的过程。

43. javac前端编译的过程,大致为1个准备过程和3个处理过程。处理过程中,又解语法糖和字节码生成。

44. 解语法糖就是泛型,变长参数,自动装箱,拆箱等,把它们还原成最基础的语法结构。

45.  泛型,在编译时会自动进行类型擦除,然后再元素访问的时候,进行强制类型转换。在这个过程中,int、long,Object之前不支持强制转换,所以解决方案就通过自动装箱和拆箱解决。

46.便利循环则是把代码变成了迭代器的实现,所以被便利的类要实现了iterabel接口的原因。

后端编译(即时编译)

hotspot为何要使用解释器与编译器并存

为何hotspot要实现3个不同的即时编译器

程序何时使用解释器执行,合适使用编译器执行

哪些程序会被编译成本地代码

如何从外部观察到即时编译器的编译过程和编译结果

解释器:当程序需要迅速启动和执行时,解释器可以省去编译时间,立即运行,随着时间的推移,编译器逐渐发挥重用。解释器还可以作为编译器编译出错的时候,还可以退回到解释状态。

编译器:C1 客户端编译器,C2服务端编译器。

47. 由于编译本地代码需要占用程序运行时间,所以要编译出优化程度搞得代码,就需要分层编译。 大概就是最开始是纯解释行为,然后客户端编译执行,然后开启方法及回边次数统计等性能监控,然后再服务端编译,其中服务端编译更耗时,所以优化更好。

48. 通过分层编译后,解释器,客户端,和服务端编译器就同时工作。

49. 热点代码:多次被调用得方法和多次执行的循环体,虚拟机通过为每个方法(甚至代码块)建立计数器,统计方法执行次数。

50 编译器会做的一些优化,方法内敛,冗余消除,无用代码消除,通过逃逸分析,来进行栈上分配,标量替换,同步消除等,。

51逃逸分析,判断一个对象是否能被外部访问,其中有从不逃逸,方法逃逸,线程逃逸。

52 如果确定一个对象不会逃逸出线程之外,那么就栈上分配,这样大量的对象就随着方法的结束而自动销毁,这样不会产生垃圾回收损耗,这叫方法逃逸,栈上分配只支持方法逃逸。

标量替换,若一个数据已经无法再被分解,那么这个数据被称为标量。如果把一个Java 对象拆散,根据程序访问情况,将其成员变量恢复为原始变量,这个过程就称为标量,并且不会创建这个对象。

同步消除,线程同步非常耗时,如果逃逸分析确定一个变量不会逃逸出线程,那么这个变量实施的同步措施就可以安全消除掉。

53.Java 内存模型与线程

线程-》工作内存-》save和load操作,主内存。
 主内存和工作内存数据如何传输, java 内存模型定义了8种原子操作,lock,unlock,read,load,write,store,use assign。

lock 和unlock,read和load,write和store必须成对出现。

54 volatile,具备两项特性,1)当一条线程修改了这个变量的值,新值对于其他线程来说是立刻可以得知的。2) 禁止指令重排。

55. 那volatile i++这种形式,为啥再多线程也会出现问题。因为volatile,仅保证修改值后,另外线程立刻可知。当线程操作栈顶i最新值时,其他线程又修改了i的值,由于i++不是原子的,此时栈顶i的值已经过期。

56 .禁止指令重排,指令重排的意思是,普通变量尽在线程内表现为串行的语义,而不能保证变量赋值操作的顺序和执行顺序一致,就又可能导致并发执行。

57 .而volatile变量A,是如何实现着两个特性呢,在即时编译的时候,会在赋值操作后,多执行一个lock add 0 esp 的空操作,这个操作相当于让esp加了一个0,esp是什么是当前线程的栈顶指针,相当于把当前线程的工作内存中的变量i,进行了一次store和write操作,重写回主存当中。而其他线程操作时,由于主存中A过期,必须重新从主存中read和load。

58.而lock add 0 esp ,是如何禁止重排序呢,就像刚刚说的,在同一个处理器中,变量表现为串行语义, 当进行了loak add 0 esp 后,就达到了指令重拍寻无法越过内存屏障的效果。

59. happens-before,它是判断数据是否存在竞争,线程是否安全非常有用的手段,它是定义亮相操作直接的偏序关系。如果A 先行发生于b,那么A操作的影响能被B观察到。在Java 内存模型下,有一些天然的先行发生关系,这些关系无需同步器写作就是安全的。

1)程序次序规则:在一个线程内,书写在前面的肯定先行发生于后面的操作。

2)管程锁定规则:对于一个unlock操作先行发生于后面对同一个锁的lock操作(也就是同一个锁只能由一个线程持有)

3)volatile:一个变量的写操作先行发生后面对这个变量的读操作

4)线程启动:Thread对象的start先行发生于这个线程每一个动作。

5)线程终止:线程中所有操作都先行发生于线程的终止检测,Thread.join(),Thread.isAlive的返回值手段来检查线程是否已经终止。

6)线程中断:对线程interrupt()方法的调用先行发生于被中断线程代码检测到中断事件的发生。

7 )对象终结 8)传递性

60  hotspot 通过用户线程实现,每个java线程都是直接映射到一个操作系统原生线程实现的,全权交给底下的操作系统去处理

61. 互斥是实现同步的一种手段,互斥是因,同步是果。临界区、互斥量、信号量都是常见的互斥是实现方式。

62 synchronized,在经过javaC编译后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令,这两个字节码指令都需要指定一个reference的参数来指明锁定和解锁的对象,可能是对象的reference,也可以是类方法或者实例方法

63.在执行monitorenter指令时,首先尝试获取锁对象,如果没有获取 ,或者获取到,锁的计数器+1.在monitorexit时将锁计数器-1.一旦计数器的值为零,锁随机就被释放。

64. 两个synchronized结论:可重入,无条件阻塞没有获得锁的其他线程(不可强制获得锁的线程释放锁,不可中断正在等待锁的线程,或者超时退出)

65. 由于synchronized的局限性,一如了current.locks.Lock接口,ReentrantLock是lock接口最常用的实现,它比synchronized相比增加了一些高级功能

等待可中断,公平锁(公平锁影响性能),锁绑定多个条件(synchnied锁对象的wait()跟它的nofit可以配合实现一个隐含的条件)z

66. 非阻塞同步,CAS。默认不会冲突,先尝试,失败后轮询

67 锁优化,共享数据在大部分情况下,都只会持续很短一段时间,为了这段时间去挂起和恢复线程不值得,我们可以让后面请求锁的那个线程“稍等一会儿”,但是不放弃处理器执行时间,为了让线程等待,我们只需让线程执行一个循环,这就是自旋锁。自旋锁要占用处理器时间,如果锁被占用的时间很短,自旋就非常好,自旋超过一定次数,就会使用传统方式挂起线程。

jdk6 后引入了自适应自旋,就是通过同一个锁的自旋时间以及锁拥有着的状态决定。如果某个锁,自旋很少成功获得锁,直接省略自旋过程。

68.锁消除,是指编译器,根据逃逸分析。

69.轻量级锁,虚拟机对象头分为两个部分,一个部分存储对象自身运行时数据(hash码,GC分代年龄等),称为MARK WORD,另外一个部分是用于存储指向方法区对象类型的数据,如果是数组对象,还会额外存储数组长度。

70 mark word中,存储hash码+分代年龄+偏向模式+2个bit存储锁标志。锁标志包括,未锁定01,轻量级锁定00,重量级锁定10 GC标记,可偏向等几种状态。

71  轻量级锁,代码即将进入同步块,如果同步对象没有被锁定,虚拟机会在当前线程栈帧中建立一个锁记录(lockRecord),用于存储锁对象目前的MarkWord。

虚拟机通过CAS操作尝试把对象的Mark Word更新为指向所记录的指针,如果更新成功,即代表该线程拥有了这个对象的锁,并且把锁标志改成轻量级锁定

如果更新失败,就意味着,至少存在一条线程和当前线程竞争获取该锁,会检查MarkWord是否指向当前线程的栈帧,如果是,说明当前线程以及拥有,直接进入同步块。否则说明被其他线程抢占,然后膨胀为重量级锁,MarkWord中存储的就是重量级锁的指针。

72 .解锁,就是对象的MarkWord指向线程记录,那么就用CAS把对象之前的MarkWord和线程赋值的DisplacedMarkword替换回来,如果替换成功,就解锁成功。

如果替换失败,则有其他线程尝试获取该锁,在释放锁的时候,唤醒被挂起的线程。

72.偏向锁,jdk6引入的一项锁优化,轻量级锁通过CAS操作消除同步使用的互斥量,那么偏向锁就是消除整个同步。偏向锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他线程获取,那么持有锁的线程将永远也不用同步。一旦出现另外的线程尝试获取这个锁,偏向模式立马结束。在偏向状态时,MARKWORD大部分空间都用了存储持有锁的线程id了。

JVM知识点整理2——(类加载)相关推荐

  1. JVM超神之路:年后跳槽需要的JVM知识点,周末给你整理了一份!!!

    四期目录 一.ClassLoader类加载器 1.类加载过程 2.类加载器种类以及加载范围 3.双亲委派是什么 4.为啥要有双亲委派 5.为什么需要破坏双亲委派模型 6.如何破坏双亲委派模型 7.如何 ...

  2. 高级 Java 面试通关知识点整理

    转载自 高级 Java 面试通关知识点整理 1.常用设计模式 单例模式:懒汉式.饿汉式.双重校验锁.静态加载,内部类加载.枚举类加载.保证一个类仅有一个实例,并提供一个访问它的全局访问点. 代理模式: ...

  3. 06-JAVA面试核心知识点整理(时间较多的同学全面复习)

    JVM (1) 基本概念: JVM是可运行Java代码的假想计算机 ,包括一套字节码指令集.一组寄存器.一个栈.一个垃圾回收,堆 和 一个存储方法域.JVM 是运行在操作系统之上的,它与硬件没有直接的 ...

  4. 带你全面了解高级 Java 面试中需要掌握的 JVM 知识点

    对于一名高级 Java 工程师来说,JVM 可以说是面试必问的一个知识点,而大多数人可能没有对 JVM 的实际开发和使用经验,接下来这一系列文章将带你深入了解 JVM 需要掌握的各个知识点.这也将帮助 ...

  5. Java基础知识点整理(2022年最新版)

    看了网上很多关于Java基础知识点整理的文章,但是感觉都不是很好,要么不全面,要么不准确,要么排版太乱了,所以今天整理了一份Java基础知识点整理(2022年最新版),希望对大家有帮助哈~ 由于本文篇 ...

  6. 万字吐血总结JVM知识点

    前言:这一年前前后后把<深入理解JVM>看了好几遍,最近把里面所有的知识点整理如下,供大家一起学习. 第二章 Java内存区域与内存溢出异常 一.运行时数据区域 1.程序计数器:线程私有, ...

  7. 知识点整理,Java基础面试题(一)

    写在前面 整理了一些互联网大厂的面试题,这些面试题经常会被问到,也是作为Java工程师需要掌握的一些知识点,毕竟理论和实践的结合,才是王道,分片整理,每天嗑些知识点,快乐每一天,如果对你有帮助,记得点 ...

  8. java基础知识点整理一

    java基础知识点整理一 引言 '''突然发觉任何一门语言的基础知识部分,都比较杂一些.如果个人经过梳理之后,知识体系系统化,可以让基础更加牢靠一些.但是还是会有一些遗忘.所以,我想把一些比较重要但是 ...

  9. 【2022持续更新】大数据最全知识点整理-HBase篇

    大数据最全知识点整理-HBase篇 基础问题: 1.Hbase是什么 2.Hbase架构 3.Hbase数据模型 4.Hbase和hive的区别 5.Hbase特点 6.数据同样存在HDFS,为什么H ...

最新文章

  1. 你应该知道的25道Javascript面试题
  2. python偶数分解成两个素数之和_偶数 2021218918 ,有多少种方法分解成两个素数之和?...
  3. 新三国 雷人台词----雷死一群程序员
  4. cpu时间片 linux,能讲一下在Linux系统中时间片是怎么分配的还有优先级的具体算法是...
  5. Django--QuerySet--基础查询
  6. ALGO-147_蓝桥杯_算法训练_4-3水仙花数
  7. # 笔记2021-11
  8. 基于单片机的十字路口交通灯设计(带左转带紧急按钮带调时间)
  9. 免费建站网站分享,最好的自助建站
  10. go decimal 使用方法
  11. ScreenToGif
  12. linux关闭云锁服务器,安全软件之linux系统下云锁简单的安装方法
  13. u检验和t检验区别与联系
  14. 【图文并茂】一文讲透Dubbo负载均衡之最小活跃数算法
  15. 如何查看html的字体,如何检测网页中使用了哪种定义的字体?
  16. php 虚拟机速度慢,[译]PHP虚拟机(PHP Virtual Machine)
  17. 21世纪东方美女标准[男士参考女士学习]
  18. docker容器内的attached 和detached模式
  19. 深度学习论文: Multi-modal Sensor Fusion for Auto Driving Perception: A Survey
  20. 《C++性能优化指南》 linux版代码及原理解读 第四章

热门文章

  1. u盘安装grub linux,U盘安装的引导搞错了
  2. JS组合函数(Composition):原来如此!
  3. 详解各种布隆过滤器原理及使用场景
  4. 网络安全学习路线-超详细
  5. Office办公 如何设置WPS的默认背景大小
  6. 原生JS获取body
  7. Mil代码编程的基本概述
  8. 物联网的2018:这是百花齐放的一年
  9. Windows设置本地DNS域名解析Hosts文件的方法
  10. 网络七层协议具体是什么?