简单了解JVM

JVM结构

类加载器ClassLoader:用来状态字节码文件

执行引擎

运行时数据区:堆、栈、方法区、程序计数器、本地方法栈

简单了解JVM_第1张图片

 

 

 

一、类加载

类加载机制:JVM把描述类的.class文件加载到内存,并对数据进行加载、连接、初始化,最终形成可以被虚拟机直接使用的java类型。

类加载器:类被加载到虚拟机中开始,到卸载出内存为止,它的生命周期包括:加载、连接(校验、准备、解析)、初始化、使用、卸载

1、   加载

  1.1         通过全类名获取二进制文件

  1.2         将字节流所代表的二进制文件转换成方法区的运行时数据结构

  1.3         在堆内存中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口

  类加载阶段是可控性最强的,因为这个阶段可以由系统提供的类加载器完成,也可以自定义类加载器

2、 连接

  a)    校验
    校验是连接阶段第一步,确保加载的二进制文件安全

  b)    准备
    准备阶段正式为类变量(静态变量,而不是成员变量,成员变量在类创建时候分配在堆中)分配内存并设置默认值的阶段,这些内存都在方法区中进行分配

  c)    解析

    解析阶段是虚拟机常量池中的符号引用替换为直接引用的过程。

3、 初始化
类加载的最后阶段,若该类有超类,则对其进行初始化。给静态成员变量赋值。

二、执行引擎

       执行引擎负责具体的代码执行过程。

       类加载器负责装载编译后的字节码,并加载到运行时数据区,然后字节码执行引擎以指令形式读取java字节码。字节码文件并不是JVM所认识的语言,所以执行引擎会将字节码转换成可以被JVM执行的语言,有以下两种方法:

  1、 解释器:一条一条地读取,解释并执行字节码指令。虽然解释很快,但是执行很慢

  2、 即时(JIT)编译器:JIT用来弥补解释器的缺点。

三、运行时数据区

       线程共享

              方法区

              堆内存(Heap)

       线程私有

              栈内存

              本地方法栈

              程序计数器

       JVM运行时会分配好方法区和堆内存,而JVM每遇到一个线程,就会为其分配一个程序计数器,栈和本地方法栈,当线程结束,三者所占用的内存会被释放,即程序计数器、栈、本地方法栈的生命周期和所属的线程相同,而方法区和堆的生命周期和Java程序生命周期相同,所以GC只会发生在共享线程上,大部分在堆里。

  1、 方法区
    有时候又称永久代。他存放类信息(类名称、修饰符等),运行时常量池,静态变量,常量,即时编译后的代码等。在GC时主要是对常量池和类型的卸载。当方法区所需要的内存超过其允许的大小时,就会抛出OutOfMemory异常。

  2、 堆
     堆内存是存放对象实例和数组的。堆是线程共享的,因此在进行内存分配时需要进行加锁,这也导致了new对象过程开销较大。HotSpot为了提升内存分配效率,对所创建的线程都会分配一个独立的缓冲空间(TLAB:Thread Local Allocation Buffer),在这个线程缓冲区不需要加锁。
       堆空间被分为年轻代和老年代,年轻代又分为Eden和两个Survivor区。新创建的对象分配到Eden区,Survivor区作为Eden区Old区的缓冲,经过多次GC,存活下来的对象被扔到Old区。如果对象大于Eden区而小于Old区,会被直接扔到Old区。如果对象大于Old区,会抛出OutOfMemoryError(OOM)。

  3、 栈
       Java栈是线程私有的,每个线程创建的同时都会创建自己的栈,互不干扰。
       栈内存是java方法执行的内存模型。每个方法的执行都会创建一个栈帧,栈帧中包括:局部变量表、操作数栈、指向当前方法所属的类的运行时常量池的引用、方法返回地址等。当调用一个方法时,就创建一个栈帧,并且建立栈帧的压栈,方法执行完毕后,便会将栈帧出栈。由此可知,线程当前运行的方法所对应的栈帧比定位于栈的顶部。
       局部变量表:存放局部变量。对于基本数据类型的变量,直接存值,对于引用类型的变量,存储的是执行对象的引用。大小在编译期已经确定,不会发生改变。
       操作数栈:程序用于计算的地方。
       方法返回地址:方法指向完毕后,返回到之前调用它的地方。
       可能产生异常:StackOverFlowError、OOM

  4、 程序计数器
       也称为PC寄存器。在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的,一个CPU内核只能执行一个线程中的指令,因此为了能是的每个线程在切换后都能回到切换之前的位置,每个线程都需要有自己的程序计数器,因此程序计数器是线程私有的。此区域是JVM中唯一一个不会出现OOM异常的地方。

你可能感兴趣的:(简单了解JVM)