java内存区域

 1. 程序计数器(线程私有)

  程序计数器,是一块较小的内存空间,是当前线程所执行的字节码行号指示器。在虚拟机的概念模型里,字节码解释器工作时时通过改变程序计数器的值来选取下一条需要执行的字节码指令。

  作用:分支、循环、异常处理、线程恢复等。

  正在执行 java 方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果还是 Native 方法,则为空。

  唯一一个在虚拟机中没有规定任何 OutOfMemoryError 情况的区域。

2. java虚拟机栈(线程私有)

  虚拟机栈,是描述java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

  栈帧:用户存储局部变量表、操作数栈、动态链接、方法出口等信息。

    1)局部变量表:是一组变量值存储空间,用户存放方法参数和方法内部定义的局部变量。

      基本数据类型(boolean、byte、char、short、int、long、float、double)、

      对象引用(reference类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)
      returnAddress类型(指向了一条字节码指令的地址)

      64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其余的数据类型只占用1个。

      局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

    2)操作数栈:也叫操作栈。先入后出的栈。元素可以是任意的java数据类型,包括double和long。32位数据类型占栈容量1、64位2。

      当一个方法刚开始执行时,操作数栈为空,在方法执行过程中,会有各种字节码指令往操作数栈中写入和提取内容(入栈/出栈操作)。

      作用:算术运算、调用其他方法时通过操作数栈进行参数传递。

    3)动态连接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。

      Class文件的常量池中有大量的符号引用,字节码中的方法调用指令就是以常量池中指向方法的符号引用作为参数。这些符号引用一部分在类加载阶段或者第一次使用的时候转化为直接引用,这种转化称为静态解析。

      另一部分将在每一次运行期间转化为直接引用,这部分称为动态连结。

    4)方法返回地址:在方法退出后,需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要再栈帧中保存一些信息,用来恢复它的上层方法的执行状态。

      两种方法可以退出方法:

        1.执行引擎遇到任意一个方法返回的字节指令。正常完成出口。

        2.在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理。异常完成出口,不会给他的上层调用者返回任何信息。

    一般把动态连接、方法返回地址和其他附加信息归为一类,称为栈帧信息。

3. 本地方法栈(线程私有)

  本地方法区和虚拟机栈作用类似, 区别是虚拟机栈为执行 Java 方法服务, 而本地方法栈则为 Native 方法服务。

4. java堆(线程共享)

  存放对象实例。

  堆是垃圾收集器的主要区域(GC堆)。现代垃圾回收器基本都采用分代回收,细分为:新生代(Eden空间、From Survivor空间、To Survivor空间)、老年代。

5. 方法区(线程共享)

  储存已经被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码的数据。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。  

  Class 在被加载的时候被放入永久区域,它和和存放实例的区域不同,GC 不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的 Class 的增多而胀满,最终抛出 OOM 异常。

  在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。元空间的本质和永久代类似,元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入 java 堆中,这样可以加载多少类的元数据就不再由MaxPermSize 控制, 而由系统的实际可用空间来控制。

  运行时常量池:方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。Java虚拟机对Class文件每一部分(自然也包括常量池)的格式都有严格规定,每一个字节用于存储哪种数据都必须符合规范上的要求才会被虚拟机认可、装载和执行。

  一般来说,除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性。并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。

  方法区回收

    主要回收两部分内容:废弃常量和无用的类。

    1) 回收废弃常量与堆中对象类似;

    2)回收无用的类必须同时满足以下三个条件:

      a、该类的所有实例都已经被回收,也就是Java堆中不存在该类的任何实例。

      b、加载该类的ClassLoader已经被回收。

      c、该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

posted @ 2019-11-30 22:05  king_wq_庆  阅读(144)  评论(0编辑  收藏  举报