• JVM虚拟机结构


    JVM的主要结构例如以下图所看到的。图片引用自舒の随想日记

    JVM结构

    JVM结构

    方法区和堆由全部线程共享。其它区域都是线程私有的

    程序计数器(Program Counter Register)

    类似于PC寄存器。是一块较小的内存区域。通过程序计数器中的值寻找要运行的指令的字节码,因为多线程间切换时要恢复每个线程的当前运行位置,所以每个线程都有自己的程序计算器。这一个区域不会有OutOfMemeryError。当运行Java方法时,这里存储的运行的指令的地址,假设运行的是本地方法。这里的值是Undefined。

    虚拟机栈(Java Stack)

    虚拟机栈也是线程私有的,每创建一个线程,虚拟机就会为这个线程创建一个虚拟机栈。虚拟机栈表示Java方法运行的内存模型,每调用一个方法,就会生成一个栈帧(Stack Frame)用于存储方法的本地变量表、操作栈、方法出口等信息,当这种方法运行完后,就会弹出对应的栈帧。

    假设请求的栈的深度过大,虚拟机可能会抛出StackOverflowError异常,假设虚拟机的实现中同意虚拟机栈动态扩展,当内存不足以扩展栈的时候,会抛出OutOfMemoryError异常。

    栈帧(Stack Frame)

    栈帧分为三部分:局部变量区(Local Variables)、操作数栈(Operand Stack)和帧数据区(Frame Data)。

    局部变量区(Loca Variables)

    局部变量区被组织一个一个从0開始的字数组。byte、short、char在存储前被转换为int,boolean也被转换为int。0表示false,非0表示true。long和double占领两个字长。

    操作数栈(Operand Stack)

    操作数栈也被组织为一个字数组。但不同于局部变量区。它不是通过数组下标訪问的,而是能过栈的Push和Pop操作,前一个操作Push进的数据能够被下一个操作Pop出来使用。

    帧数据区(Frame Data)

    这部分的作用主要有三部分:

    • 常量池中数据的解析
    • 方法运行完后处理方法返回,恢复调用方现场
    • 方法运行过程中抛出异常时的异常处理,存储有一个异常表,当出现异常时虚拟机查找相应的异常表看是否有相应的Catch语句,假设没有就抛出异常终止这种方法调用

    本地方法栈(Native Method Stack)

    与虚拟机栈类似,仅仅是是运行本地方法时使用的。

    方法区(Method Area)

    用于存储已被虚拟机载入的类型信息、常量、静态变量、即时编译后的代码等信息。

    方法区是线程间共享的。当两个线程同一时候须要载入一个类型时。仅仅有一个类会请求ClassLoader载入,还有一个线程会等待。

    对于每个载入的类型。会在方法区中保存下面信息:

    • 类及其父类的全限定名(java.lang.Object没有父类)
    • 类的类型(Class or Interface)
    • 訪问修饰符(public, abstract, final)
    • 实现的接口的全限定名的列表
    • 常量池
    • 字段信息
    • 方法信息
    • 除常量外的静态变量
    • ClassLoader引用
    • Class引用

    对于每个字段。会在方法区中保存下面信息(字段声明顺序也会保存):

    • 字段名
    • 字段的类型
    • 字段的修饰符(public, private , protected, static, final, volatile, transient)

    对于每个方法,会在方法区中保存下面信息(方法声明顺序也会保存):

    • 方法名
    • 方法返回类型(或void)
    • 參数信息
    • 方法修饰符(public, private, protected , static, final, synchronized, native, abstract)

    假设方法不是抽象方法并非本地方法(Native Method),还会保存下面信息:

    • 方法的字节码
    • 本地变量表及操作数栈的大小
    • 异常表

    虚拟机须要存储一些数据,用来高速地訪问一个类对象中的方法,一般实现为一个方法表。

    方法区中另一部分是执行时常量池,主要用来存储编译时生成的字面量和符号引用,常量也能够在执行时产生,如String的intern方法。

    方法区中也可能存在GC,但虚拟机规范对此不做要求。主要是回收一些常量和卸载一些不用的类型信息,只是要卸载一个类的条件非常难达到,并且些处GC事实上也回收不了多少内存。

    堆(Heap)

    虚拟机中用于存放对象与数组实例的地方。垃圾回收的主要区域就是这里(还可能有方法区)。

    假设垃圾收集算法採用按代收集(眼下大都是这样),这部分还能够细分为新生代和老年代。

    新生代又可能分为Eden区,From Survivor区和To Survivor区。主要是为了垃圾回收。全部的线程共享Java堆。在这里还能够划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)。

    Java堆仅仅要求逻辑上是连续的,在物理空间上能够不连续。

    直接内存

    JDK1.4中引用了NIO,并引用了Channel与Buffer,能够使用Native函数库直接分配堆外内存,并通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。

    对象訪问

    当新建一个对象时,会在堆中为这个对象分配内存。并在栈中有一个对这个对象引用,除此之外,在Java堆中还要能通过这个对象找到它的类型信息(对象类型。父类,实现的接口,包括的字段与方法等)。

    Reference在Java虚拟机中定义为指向对象的引用,但未定义这个Reference应该有怎么实现。

    一种实现是Reference直接存储对象在堆内的地址。对象的类型信息能够在对象在堆中的内存布局中存储,如存储在对象内存的开头等。

    还有一种实现是Reference指向一个句柄表中的一个位置,句柄中保存了对象的实际位置及它相应的类型信息。

    使用句柄的优点是当在内存中移动对象的位置时。仅仅须要更新句柄表中的内容。不须要改变引用值,但会多一次内存訪问开销,直接引用的优缺点与此相反。



    參考资料:

    1. 深入Java虚拟机
    2. 深入理解Java虚拟机-JVM高级特性与最佳实践
    3. 浅析Java虚拟机结构与机制
  • 相关阅读:
    ViewState
    jar包签名
    Eclipse打JAR包引用的第三方JAR包找不到 问题解决
    java项目打jar包
    像VS一样在Eclipse中使用(拖拉)控件
    Myeclipse buildpath 加server library
    nativeswing的关闭问题 当出现Socket连接未断开错误
    Windows 7 配置jdk 1.7环境变量
    myeclipse添加server library
    RichFaces 大概
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/6972008.html
Copyright © 2020-2023  润新知