JVM(完成度95%,不断更新) (2)

类初始化时机 主动引用
右边几种方式会触发类的初始化 使用new关键字实例化对象时
  调用一个类的静态方法时  
  使用反射获取对象时,如果对象没有初始化,会先进行初始化  
  当初始化一个类时,如果父类还没初始化,会先初始化父类  
  虚拟机启动时,会先初始化主类(main方法)  
被动引用
右边几种方式不会触发类的初始化
  通过子类调用父类的静态字段,子类不会初始化  
  通过数组引用类,不会触发此类的初始化  
  调用类的常量,不会触发类初始化,因为常量是存在类的常量池中,会转为调用常量的那个类中  

实例初始化过程

父类的类构造器clinit

子类的类构造器clinit

父类的实例变量和实例代码块

父类的构造函数

子类的实例变量和实例代码块

子类的构造函数

如果一个类的成员变量用static修饰,则它被称为类变量(静态变量),否则它被称为实例变量。
Java的实例变量和实例代码块都是按照顺序执行的,并且实例变量和实例代码块优先于构造函数执行。
java的静态变量和静态代码块是按照顺序执行的。
实例初始化不一定要在类初始化结束之后才开始初始化,比如static StaticDemo st=new StaticDemo(); 实际上是把实例初始化嵌入到了静态初始化流程中。

双亲委派机制 概念 好处
当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。  
采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个 Object对象。
  沙箱机制:
保证类的唯一性,防止自己写的代码污染Java原生的方法。
比如自定义一个java.lang,String类,JVM从Bootstrap启动器开始寻找String类,但是其中找不到我们写的方法,这就会抛出ClassNotFoundException
 

三、JVM内存结构图

运行流程图(精简)

image.png


运行流程图(详细)

image.png

JDK7、8内存空间区别

image.png


四、程序计数器

线程私有
程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器 的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处 理、线程恢复等基础功能都需要依赖这个计数器来完成。
由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因 此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程 之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;
如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)。
此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。

可以使用javap 来查看类的字节码文件
程序计数器在当前指令执行时记录下一个指令的标记,方便当前指令完成后快速切换

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwsdps.html