对于java程序员来说,在虚拟机自动内存管理的机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出的问题,由虚拟机管理内存,这一切看起来都很美好。不过,也正是因为java程序员把内存控制的权利交给了java虚拟机,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会成为一项艰难的工作。所以本篇博文主要介绍java虚拟机内存的各个区域。
运行时数据区域就是指虚拟机在运行java程序时,把虚拟机自己管理的内存划分为若干个不同的数据区,如下图:
程序计数器是线程私有;
程序计数器是较小的一块内存,用于记录当前线程执行到哪一步;
如果执行的是java方法,则程序计数器记录的是正在执行的虚拟机字节码指令的地址;
如果执行的是native方法,则程序计数器为空;
此内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域;
线程私有;
描述java方法执行的内存模型,每个方法在执行时,都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链表、方法出口等信息;
每一个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中的入栈出栈的过程;
此内存区域会出现两种异常状况:
如果线程请求的栈深度大于虚拟机栈允许的深度,则抛出StackOverflowError异常;
如果虚拟机栈可动态扩展,且在扩展时无法申请到足够的内存时,则抛出OutOfMemoryError;
线程私有;
与虚拟机栈基本一致,唯一不同的地方在于:虚拟机栈中执行的是java方法,而本地方法栈中执行的是native方法
线程共享;
java堆是虚拟机内存管理中最大的一块区域,虚拟机启动时创建;
几乎所有的对象实例都在堆内存中分配;
java堆可以处于物理上不连续的空间中,只要逻辑上是连续的即可;
如果堆中没有内存分配去完成实例化,且堆无法扩展时,将会抛出OutOfMemoryError;
线程共享;
用于存储虚拟机加载的类信息、常量、静态变量、字节码文件等;
当方法区无法分配内存时,则抛出OutOfMemoryError;
运行时常量池是方法区的一部分;
用于存储编译时期产生的字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放;
既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError;
直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域。但是这部分内存被频繁使用,而且也可能导致OutOfMemoryError;
JDK1.4中新加入的NIO(New Input/Output)类,引入一种基于通道(Channel)与缓冲区(buffer)的I/O方式,它可以使用native函数库直接分配堆外内存,然后通过一个存储在java堆中DirectByteBuffer对象作为这块内存的引用进行操作;
直接内存不会受java堆大小的限制,但是,受本机总内存大小以及处理器寻址空间的限制;
服务器管理员在配置虚拟机参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略直接内存,使得各区域内存总和大于物理内存限制,从而导致动态扩展时出现OutOfMemoryError;
原文:https://www.cnblogs.com/wly1-6/p/10438178.html