欢迎光临散文网 会员登陆 & 注册

Java虚拟机-内存区域

2020-10-01 17:13 作者:bili_93709487  | 我要投稿

Java虚拟机在执行程序时会把它所管理的内存划分为若干个区域。分别为线程共享的方法区和线程隔离的程序计数器虚拟机栈本地方法栈

Java虚拟机运行时数据区域
  • 程序计数器

    程序计数器可以看作是当前线程所执行的字节码的行号指示器。每个线程都拥有自己的程序计数器(指示该线程执行到的位置),这部分内存为线程“私有”的内存。

    如果线程正在执行java方法,这个计数器记录的是正在执行的java指令地址。如果正在执行的是Native方法,这个计数器的值为空(Undefined)。

    这个内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

  • Java虚拟机栈

    Java虚拟机栈的生命周期与线程相同

    虚拟机栈描述的是Java方法执行的内存模型。每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表操作数栈动态链接方法出口等信息。每个方法的执行,就对应一个栈帧的入栈到出栈。

    局部变量表存放了编译期可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double)、对象引用(reference类型)和returnAddress类型(指向了一条字节码指令的类型)。这些数据类型在局部变量表中的存储空间以局部变量槽(Slot)来表示,其中64位长度的long和double的数据会占用两个局变量槽,其他类型只占用一个。局部变量表所需的内存空间在编译期间完成分配,方法运行期间不会改变局部变量表的大小(变量槽的数量)。

    在Java虚拟机规范中,对这个区域规定了两个异常状态。如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。如果虚拟机栈可以动态扩展,扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常(HotSpot虚拟机的栈容量是不可以动态扩展的,只要线程申请栈空间成功了就不会有OutOfMemoryError,但如果申请失败还是会抛出OutOfMemoryError)。

  • 本地方法栈

    本地方法栈和虚拟机栈所发挥的作用是相似的,他们之间的区别不过是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。

    本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

  • Java堆

    Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建。

    Java堆的唯一目的就是存放对象实例,几乎所有对象实例都在这里分配。(所有的对象实例一季数组都要在堆上分配)但是随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有对象都分配在堆上也渐渐变得不是那么“绝对”。

    Java堆是垃圾收集器管理的主要区域,因此也被称作“GC堆”(Garbage Collected Heap)。由于现在收集器都采用分代收集算法,所以Java堆还可以细分为新生代老年代Eden空间From Survivor空间To Survivor空间等。从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的本地线程分配缓冲区(Thread Local Allocation Buffer, TLAB)。

    根据Java虚拟机规范规定,Java堆可以处于物理上不连续的空间中,大小通过-Xms和-Xmx控制。如果Java堆中没有内存完成实例分配,堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。

  • 方法区(Method Area、Non-Heap)

    方法区用于存储已被虚拟机加载的类型信息常量静态变量即时编译后的代码缓存等数据。

    在HotSpot虚拟机上,尤其是在JDK8以前,方法区又被称为“永久代”,仅仅是因为HotSpot虚拟机将GC分代收集扩展至方法区。但这样设计更容易遇到内存溢出问题,永久代有-XX:MaxPermSize的上限,而J9和JRocket只要不触碰到进程可用内存上限就不会出现问题。(JDK1.7的HotSpot中,已经将字符串常量池移出“永久代”, 到了JDK8, 终于完全废弃了永久代的概念,改用与JRocket、J9一样在本地内存中实现的元空间来代替)。

    根据Java虚拟机规范的规定,当方法区无法满足内存分配的需求时,将抛出OutOfMemoryError异常.

    运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中,出了类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

  • 直接内存

    直接内存(Direct Memory)不是虚拟机运行时数据区域的一部分,也不是Java虚拟机规范中定义的内存区域。

    直接内存使用Native函数库直接分配堆外内存,然后使用存储在堆中的DirectByteBuffer对象作为这块内存的引用进行操作。

    忽略直接内存也可能导致OutOfMemoryError异常。(各大内存区域加上直接内存超过物理内存,导致其他区域无法扩展)。



Java虚拟机-内存区域的评论 (共 条)

分享到微博请遵守国家法律