JVM学习之路(一)

JVM学习之路(一)


基本定义
    JVM是编译后的java程序(.class文件)和硬件系统之间的接口.将后缀名为.java的源文件编译为后缀名为.class的可以运行于java虚拟机的字节码。
    JVM=类加载器classloader+执行引擎execution engine+运行时数据区域runtime data area
JVM结构

JVM结构图

    JVM = 类加载器Classloader + 执行引擎execution engine + 运行时数据区域runtime area
类加载器classloader
- classloader把硬盘上的class文件加载到JVM中的运行时数据区域,但是他不负责这个类文件能否执行,而这个时执行引擎负责的。
- 装载.class文件
    1.隐式:运行过程中,new方式产生对象时,隐式的调用classloader到JVM
    2.显示:通过class.forname()方式动态加载

模型:类加载的过程采用双亲委托机制(Parent Delegation Model),这种机制能更好的保证java平台的安全。该模型要求除了顶层的Bootstrap classloader 启动类加载器外,其余的类加载器都应该有自己的父类加载器。子类加载器和父类加载器不是以继承的关系来实现,而是通过组合关系来复用父类加载器的代码。每个类加载器都有自己的命名空间(由该加载器及所有父类加载器所加载的类组成,在同一个命名空间中,不会出现类的完整名字相同的两个类)

工作过程:
- 当前classloader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。
- 当前classloader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrap classloader。
- 当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便有加载请求的时候直接返回。

加载器的层次结构:
类加载器

BootStrap classloader
    java虚拟机运行时,这个类加载器被创建,它负责加载虚拟机的核心类库。它不是ClassLoader的子类,而是由JVM自身实现的。
Extension classloader
    加载除了基本API外的拓展类。
System classloader
    系统类加载器,它负责JVM被启动时,加载来自系统属性所指定的JAR类包河类路径。加载应用程序和程序员自定义的类。
User-defined classloader
    用户定制的自己的类加载器,都应该继承ClassLoader类。
执行引擎Execution engine

作用:执行字节码,或者执行本地方法

运行时数据区域runtime dataarea
    JVM运行时数据区,对JVM内存空间的划分和分配。分别存放在heap,java stack,native method stack,PC register,method area.
PC程序计数器 PC Register
    较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成。
    由于java虚拟机的多线程时通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器只会执行一条线程中的指令。因此为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为"线程私有"的内存。
    如果线程正在执行的是java方法,这个计数器纪录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是native方法,这个计数器则为空。此内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
java stack栈
    java虚拟机栈也是线程私有的。每一个JVM线程都有自己的java虚拟机栈,这个栈与线程同时创建,它的生命周期与线程相同。虚拟机描述的是java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(stack frame)用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法被调用直至执行完成的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。
    局部变量表存放了编译期可知的各种基本数据类型、对象引用和returnAddress类型。局部变量表所需的内存空间在编译期完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小
本地方法栈
    与虚拟机栈的作用相似,虚拟机栈为虚拟机执行java方法服务,而本地方法栈则为虚拟机使用本地方法native服务。
java堆 Heap
    被所有线程共享的的一块存储区域,在虚拟机启动时创建,它是JVM用来存储对象实例以及数组值的区域,在JVM启动时就被创建,堆中存储了各种对象,这些对象被自动管理内存系统所管理,这些对象无需,也无法显示地被销毁。
    如果从内存分配的角度看,线程共享的java堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)
    java堆可以处于物理上不连续的内存空间上,只要逻辑上时连续的即可。
    JVM将Heap分为两块:新生代和旧生代。
    堆在JVM是所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也是new开销比较大的原因。
    为了提高效率,创建的线程都会分配一块独立的空间TLAB。
    TLAB仅作用于新生代的Eden space,故小对象比大对象分配起来更加高效。
方法区
    方法区和堆区域一样,是各个线程共享的内存区域,它用于存储每一个类的结构信息,例如运行时常量池,静态变量,成员变量和方法数据,构造函数和普通函数的字节码内容,还包括一些在类,实列,接口初始化时用到的特殊方法。
    方法区也是全局共享的,在JVM启动时创建。在一定的条件下它也会被GC,这块区域对应永久代。
    一般较少GC,这个区域的回收目标主要是针对常量池的回收和对类型的卸载。
运行时常量池
    其空间从方法区中分配,存放类中固定的常量信息,方法和域的引用信息。
    用于存放编译期生成的各种字面量和符号引用,这部分内容在类加载后存放。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值