天天看点

一文了解JVM内存分配

一、前言

关于JVM内存分配一直有想法想自己整理一篇文档,之前总是查询别的博客,对于概念的理解和系统的知识梳理一直没有仔细整理过。所以整理这样一篇文章,夯实基础,后续会查漏补缺,也希望多多指正。

二、概述

众所周知,Java虚拟机在执行Java程序时会把所管理的内存分为若干个不同的数据区域(也称为运行时数据区),大致也划分为方法区(Method Area)、虚拟机栈(VM Stack)、本地方法栈(Native )、堆(Heap)、程序技术器(Progra Counter Register)等五部分,不过随着JDK升级,内存模型也在发生变化。

一文了解JVM内存分配

JDK1.6及之前

线程间共享:堆、方法区

线程私有:虚拟机栈(也有被称为Java栈)、本地方法栈、程序计算器 

JDK1.7

在方法区中,包括已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,方法区包含运行时常量池和字符串常量池,不过在JDK以后,字符串常量池被放到了堆中,方法区仍然保留。

一文了解JVM内存分配

JDK1.8以后 

JDK1.8以后直接移除了方法区,用元数据区替代了方法区,存放于堆外内存中,字符串常量池仍在堆中。

一文了解JVM内存分配

 三、详细描述:

概念、分配内出容

堆(Heap):是虚拟机管理内存最大的一块,也是被所有线程共享的一块内存区域。对象实例及数组会在堆上进行内存分配,但也不是绝对。另外堆也是垃圾收集的主要区域,根据垃圾收集的分带收集算法,堆还可以细分为新生代和老年代。而在物理空间上堆处于不连续的内存空间中,只要是逻辑上连续即可,既可实现固态大小,也可以实现可扩展性(可配置-Xmx 和-Xms控制)。当内存不足无法完成实例分配,同时堆也无法进行扩展时会抛出OOM。

方法区(Method Area):也是各个程共享的一块区域,主要存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。也被称为“永久代”,不过在不同的虚 拟机中这两者还是有本质的区别。方法区同样支持固定大小及可扩展性,还可以选择不实现垃圾收集。同时也会出现OOM的异常。

运行时常量池:也是方法区的一部分,存放编译期产生的各种字面量和符号引用,常量池受方法区内存限制,当无法扩展时会抛出OOM。

字符串常量池:一个StringTable类,它是一个Hash表,默认值大小长度是1009;这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。字符串常量由一个一个字符组成,放在了StringTable上。在JDK1.7中,StringTable的长度可以通过参数指定:-XX:StringTableSize=6666

虚拟机栈,是线程私有的。在方法执行时会创建一个栈帧,每一个方法从调用到执行完成的过程也就是一个栈帧在虚拟机中出栈入栈的过程。虚拟机栈在编译器存放了基本数据类型,对象引用及局部变量。要注意的是在基本数据类型中long和double类型的数据会占用两个局部变量的空间,其余只占用一个。在异常情况中与堆相比除了因固定长度时(也支持动态扩展)无法扩展申请足够内存会抛出OOM还有因线程请求的栈深度大鱼虚拟机所允许的深度会抛出StackOverflowError异常。

本地方法栈 与虚拟机栈发挥作用相似,虚拟栈为执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用的Native服务。sun HOST POST 把二者合二为一,同虚拟机栈一样也会抛出OOM和StackOverflowError异常。

程序技术器:是唯一一个没有任何规定OOM情况的区域,是一块比较小的内存空间。每条线程都需要一个独立的程序计数器,来保证线程切换后能恢复到正确的执行位置,在字节码解释器工作就是通过改变这个技术器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等功能。线程正在执行一个Java方法,计数器记录的是正在执行的虚拟机字节码指令地址,而如果正在执行Native方法,则技术器为空。

直接内存:也称堆外内存,在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。直接内容使用频繁,也会产生OOM异常。引入NIO时为了避免Java堆和Native堆中来回复制数据,从而直接分配的堆外内存。本机直接内存不会受到Java堆大小的限制,受本机总内存影响。在服务器管理员在配置虚拟机参数时,会根据实际配置-Xmx等参数信息,但经常忽略直接内存,会出现各个内存区域总和大于物理内存限制,从而在动态扩容时出现OOM的情况。