zoukankan      html  css  js  c++  java
  • 深入理解JVM(1)——JVM内存模型

    Java虚拟机的内存空间分为五个部分,分别是:

    1. 程序计数器;
    2. Java虚拟机栈
    3. 本地方法栈
    4. 方法区

    接下来对这五部分分别进行详细的介绍

    1、程序计数器:

      a)什么是程序计数器:程序计数器是内存中的一个很小的空间,可以看作是当前线程正在执行的字节码的行号指示器。也就是说,程序计数器里面记录的是当前线程正在执行的字节码指令的地址。需要注意的是:如果当前线程正在执行的是一个本地方法,那么此时程序计数器为空。

        b)  程序计数器的作用:字节码解释器通过改变程序计数器依次读取指令,实现程序的流程控制;在多线程的情况下,程序计数器用来记录当前线程的执行位置,以便于当线程被切回来的时候能够记住该线程上次运行到那里了。

    c) 程序计数器的特点:是一块较小的内存空间;线程私有,每一个线程都有一个程序计数器;是唯一一个不会出现OutOfMemoryError的区域;生命周期随着线程的创建而创建,线程的结束而死亡。

    2、Java虚拟机栈

    a) 什么是Java虚拟机栈:Java虚拟机栈是描述Java方法运行过程的内存模型。Java虚拟机会为每一个即将运行的java方法创建一个叫“栈帧”的区域,该区域用来存放Java方法运行过程中需要的一些信息,这些信息主要包括:局部变量表、操作数栈、动态链接、方法出口信息等。

    注意:当方法在运行过程中需要创建局部变量的时,就会将变量的值存入局部变量表中。人们常说的Java内存空间分为“栈”和“堆”,栈中存放的是局部变量,堆中存放的是对象,这句话是不完全正确的,几乎所有的对象都是存放在堆中,但是Java虚拟机会为每一个Java方法创建一个“栈帧”,其中栈帧包括局部变量表,但还包括其他的如操作数栈、动态链接和方法出口信息等。

    b)Java虚拟机栈的主要特点:

      1) 局部变量表的创建是在Java方法即将运行的时候随着栈帧的创建而创建,但是局部变量表的大小是在编译阶段就已经确定的,创建的时候只是按照实现分配好的大小进行创建,而且在Java方法运行的过程中局部变量表的大小不会改变。

      2)Java虚拟机栈是线程私有的,每个线程都有自己的Java虚拟机栈,并且随着线程的创建而创建,随着线程的结束而死亡。

      3) Java虚拟机栈一般会出现两种错误:StackOverFloeError和OutOfMemoryError。

    若Java虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前Java虚拟机栈的最大深度的时候,就抛出StackOverFlowError异常。StackOverFlowError表示当前线程申请的栈超过了事先定好的栈的最大深度,但内存空间可能还有很多。

    若Java虚拟机栈的内存大小允许动态扩展,且当线程请求栈时内存用完了,无法再动态扩展了,此时抛出OutOfMemoryError异常。而OutOfMemoryError是指当线程申请栈时发现栈已经满了,而且内存也全都用光了。

    3、本地方法栈

      a)什么是本地方法栈:

        1) 本地方法栈和Java虚拟机栈实现的功能类似,只不过本地方法栈是描述本地方法运行过程的内存模型。

        2)本地方法栈也是在方法即将运行被执行的时候,本地方方法栈也会为本地方法创建一个栈帧,用于存放该本地方法在运行过程中需要用到的一些信息。包括:局部变量表、操作数栈、动态链接、方法出口信息等。

        3) 本地方法在执行完毕也会将相应 的栈帧出栈并释放内存空间。

        4) 本地方法栈也会抛出StackOverFlowError和OutOfMemoryError异常。

    4、堆

       a) 什么是堆

    堆是用来存放对象的内存空间,几乎所有的对象都存储在堆中。

     b)堆的特点是什么

      1)线程共享:整个虚拟机只有一个堆,所有的线程都访问同一个堆。

      2)在虚拟机启动的时候创建堆,在虚拟机关闭的时候销毁堆

      3)堆是垃圾回收的主要场所

      4) 堆可以进一步分为新生代和老年代,其中新生代有可以进一步分为Eden、From Survior、To Survior.不同的区域存放不同声明周期的对象,这样可以根据不同的区域使用不同的垃圾收集算法,从而是对象回收而更具有针对性,也会变的更加高效。

      5)  堆的大小即可以固定也可以动态的扩展,但是主流的虚拟机堆的大小都是可以动态扩展的。

    5、方法区

      a)什么是方法区

    在Java虚拟机规范中定义方法区是堆的一个逻辑部分,方法区中存放已经被虚拟机加载的类信息、常量、静态常量、即时编译器编译后的代码等

      b) 方法区的特点

            1)  线程共享:方法区是堆的一个逻辑部分,因此和堆一样是线程共享的,整个虚拟机中只有一个堆区域。

        2)永久代:方法区中存放的都是需要永久保存的信息,而且它还是堆空间的一个逻辑部分,因此用堆的方法进行划分把方法区分为老年代。

        3) 内存回收效率低:方法区中的信息一般需要长期存在,内存回收一遍只有少量的信息是无效的,在方法区中垃圾回收的主要对象是对常量池的回收和对类型的卸载。

        4)Java虚拟机规范对方法区的要求比较宽松:和堆一样允许固定大小,也允许动态扩展,还允许不进行垃圾回收。

      c)  什么是运行时常量池

      一般在一个类中通过public static final来声明一个常量,这个类被编译之后会生成一个Class文件,这个类的全部信息都会包含在这个Class文件中,当这个类被虚拟机加载之后,Class文件中的常量就会被放在方法区的运行时常量池中。在程序运行期间,也可以向常量池中添加常量。如果运行时常量池中的常量长时间没有被对象引用,也没有被变量引用,那么就会被垃圾收集器进行回收。

    6、直接内存

    直接内存是除了Java虚拟机之外的内存,但也能被Java利用。在NIO中引入了一种基于通道和缓冲的IO方式,它可以通过调用本地方法直接分配Java虚拟机之外的内存,然后通过一个存储在Java堆中的DirectByteBuffer对象直接操作该内存,从而提升了数据操作的效率。

    直接内存的大小不受Java虚拟机的控制,但既然是内存,当内存不足的时候也会出现抛出OOM异常。

    综上所述:

    a)Java虚拟机的内存模型中一共有两个“栈”,分别是:Java虚拟机栈和本地方法栈。两个“栈”的功能类似,都是方法运行过程的内存模型。并且两个“栈”内部构造相同,都是线程私有。只不过Java虚拟机栈描述的是Java方法运行过程的内存模型,而本地方法栈是描述Java本地方法运行过程的内存模型。

    b)Java虚拟机的内存模型中一共有两个“堆”,一个是原本的堆,一个是方法区。方法区本质上是属于堆的一个逻辑部分。堆中存放对象,方法区中存放类信息、常量、静态变量、即时编译器编译的代码。

    c)堆是Java虚拟机中最大的一块内存区域,也是垃圾收集器主要的工作区域。

    d) 程序计数器、Java虚拟机栈、本地方法栈是线程私有的,即每个线程都拥有各自的程序计数器、Java虚拟机栈、本地方法区。并且他们的生命周期和所属的线程一样。

    e)而堆、方法区是线程共享的,在Java虚拟机中只有一个堆、一个方法栈。并在JVM启动的时候就创建,JVM停止才销毁。

  • 相关阅读:
    kubernetes---Init-Container
    kubernetes---affinity--taint
    kubernetes---pod调度
    kubernetes---项目部署
    StatefulSet
    kubernetes---存储--PV--PVC
    pod-pod控制器-service-ingress
    jenkins
    django中的验证码
    django中的缓存
  • 原文地址:https://www.cnblogs.com/BaoZiY/p/10632204.html
Copyright © 2011-2022 走看看