Java虚拟机内存区域及可能发生的内存相关异常

1、运行时数据区域
     1.1、程序计数器
        该区域只占用内存的很小一块,该区域是线程私有的。即每个线程都有一块属于自己的程序计数器。该区域的作用相当于一个代码执行的指示器,字节码解释器在工作的时候通过改变该数值来选取下一条要执行的字节码,是程序控制流的指示器。分支、循环、跳转、异常处理、线程恢复等基础功能都靠程序计数器来完成。该区域不会抛出OutOfMemoryError。
     1.2、虚拟机栈
        该区域也是线程私有的。栈内存中存储的是Java方法执行的内存模型–栈帧。每个方法被执行的时候,虚拟机都会同步创建一个栈帧,方法执行完成后,就会将对应的栈帧弹出。所以一次方法的执行,对应着一个栈帧的入栈和出栈的过程。
        栈帧内存储的是局部变量表、操作数栈、动态链接、方法出口等。其中局部变量表中又存放了编译期可知的基本数据类型(八大基本数据类型)、对象引用(并非对象本身,对象本身存储在堆中,reference可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型。
        局部变量中存储空间是局部变量槽,其中64位的double和long会占用两个局部变量槽,其余只占据一个。局部变量表所需的空间大小(局部变量槽的多少,局部变量槽的大小不同的虚拟机可能不一样)在编译器已经确定,运行期间局部变量表大小不会改变。
       该区域可能抛出两种异常:1、StackOverflowError 2、OutOfMemoryError。当线程请求的栈深度大于虚拟机所允许的深度时抛出1,如果栈空间可以动态扩展的话,当栈扩展时无法申请到足够的空间时会抛出2(目前主流的OracleJDK、OpenJDK使用的HotSpot虚拟机栈空间无法扩展,所以只要线程的栈空间申请成功就不会抛出OOM。但是如果线程申请栈空间失败时也会抛出OOM,这种情况可以通过不断创建线程,并在线程中运行方法的方式来展示)。

//该方法可以使栈内存抛出OOM,请在32位系统下运行,否则你的电脑大概率会崩
//需要输入虚拟机参数 -Xss2m
public class JavaVMStackOOM {
     

    private void dontStop(){
     
        while (true){
     
        }
    }

    public void stackLeakByThread(){
     
        while (true){
     
            Thread thread = new Thread(new Runnable() {
     
                public void run() {
     
                    dontStop();
                }
            });
            thread.start();
        }
    }

    public static void main(String[] args) {
     
        JavaVMStackOOM javaVMStackOOM = new JavaVMStackOOM();
        javaVMStackOOM.stackLeakByThread();
    }

}

     1.3、本地方法栈
        该区域与虚拟机栈相似,区别是虚拟机栈为Java方法服务,本地方法栈为本地方法栈服务。HotSpot直接将两个栈合并。
     1.4、Java堆
        该区域是虚拟机管理的内存区域中最大的一块。几乎所有的对象都在这里分配内存,同时也是垃圾收集器管理的区域,所以也被称为“GC堆”。堆内存由所有线程共享,同时这块堆内存内部又可以划分出多个线程私有的缓冲区,但这不影响堆内存是被线程共享的,缓冲区只是用来提升对象分配时效率的。堆内存在逻辑上是连续的,在物理上并不要求连续,可以是不连续的。但是对于大对象(如数组),多数虚拟机出于实现简单,存储高效的考虑可能会要求连续的内存空间。堆既可以被实现为可扩展的,也可以实现为固定内存的。大多数虚拟机实现为可扩展的(通过虚拟机参数-Xmx设置虚拟机最大堆内存大小,和-Xms设置虚拟机初始堆内存大小)。当没有内存可供对象分配时且没有可扩展空间时会抛出OOM。
        1.4.1、分代收集理论
              十年前HotSpot虚拟机的垃圾收集器都是根据分代收集理论来进行设计的,也就是以前的文章中看到的,将堆分为新生代(其中又划分为一个Eden空间、两个Survivor空间(一个From Survivor,一个To Survivor))、老年代、永久代(JDK8后被元空间代替)的设计理论。但是目前HotSpot已经出现不采用分代设计的垃圾收集器了,但是主流的垃圾回收器还是使用分代设计。
     1.5、方法区
        该区域和堆一样,也是内存共享的。用于存储被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存。《Java虚拟机规范》中将方法区描述为堆的一个逻辑部分,但却有个别名“非堆”,用于区别堆。
        1.5.1、永久代
           永久代是早期HotSpot虚拟机的方法区的实现方式,早期HotSpot虚拟机设计团队将分代设计扩展至方法区,使得HotSpot的垃圾回收器能够像管理堆内存一样管理方法区,省去开发专门的方法区垃圾回收器。其他虚拟机并不存在方法区的概念。并且永久代有很大的缺陷,所以在JDK8后使用和JRockit、J9虚拟机一样的在本地内存中实现的元空间来代替永久代。在使用CGLib这类动态类生成工具、大量JSP、大量动态JSP生成应用、基于OSGi的应用(即使用同一个文件,被不同的加载器加载时也视为不通的类)时容易出现OOM。
        1.5.2、运行时常量池
           该区域是方法区的一部分。Class文件中有除了类的版本、字段、方法、接口外,还有一个常量池表,这个常量池表在类加载后会存放到运行时常量池中。运行时常量池具有动态性,Class中的常量池没有动态性。因为Java可以在运行时产生常量,该常量也会进入到常量池中,如String.intern()方法。
2、直接内存
    直接内存并非Java运行时数据区域,该区域不受Java堆大小限制,但是受到本机总内存限制。在直接或间接使用DirectByteBuffer(间接使用如NIO)也可能出现OOM。

你可能感兴趣的:(Java虚拟机学习,java,jdk,编程语言)