自动内存管理机制--Java内存区域与内存溢出异常

对C,C++程序员来说,在内存管理领域,他们既是用友最高权力的“皇帝”,又是从事最基础工作的“劳动人民”。

对Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码。但是,交出权力给Java虚拟机,一旦出现内存泄露和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会非常困难。

程序计数器(program Counter Register):

一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

如果线程正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

Java虚拟机栈(Java Virtual Machine Stacks):

Java虚拟机栈也是线程私有的,它的生命周期与线程相同。

每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量名,操作数栈,动态丽娜姐,方法出口等信息。每个方法从调用直至执行完成的过程,就对应着一个战阵在虚拟机栈中入栈到出栈的过程。

Java虚拟机规范中,对这个区域规定了两种异常状况:

如果线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverflowError异常。

如果虚拟机栈可以i动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutMemoryError异常。

本地方法栈(Native Method Stack):

虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

Java堆(Java Heap):

Java堆是Java虚拟机所管理的内存中的最大一块,Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

Java堆是垃圾收集器管理的主要区域,被称作“GC堆”(Garbage Collected Heap),Java堆还可以细分为:新生代和老生代,再细致一点的有Eden空间,From Survivor空间,To Survivor空间等。进一步划分的目的是为了更好地回收内存,或者更快地分配内存。

Java虚拟机规范规定,Java堆可以处于物理上不连续的内存空间,只要逻辑上是i按需的即可,就像我们的磁盘空间一样。

方法区(Mehod Area):

与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。有一个别名(Non-Heap(非堆))。

很多人愿意把方法去称为“永久代”(Permanent Generation),本质上两者并不等价。

运行时常量池(Runtime Constant Pool)

是方法区的一部分。用于存放编译器生成的各种字面量和富豪引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

直接内存(Direct Memory)

并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。但这部分内存也被频繁的使用,也可能导致OutOfMemoryError异常出现。

在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方法,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。避免在Java堆和Native堆中来回复制数据,显著提高性能。

HotSpot虚拟机对象探秘:

对象的创建

虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载,解析和初始化过。如果没有,那必须先执行相应的类加载过程。

在类加载检查通过后,接下来,虚拟机将为新生对象分配内存(对象所需内存的大小在类加载完后便可完全确定)

对象的内存布局

在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header),实例数据(Instance Data)和对齐填充(Padding)

HotSpot虚拟机的对象头包含两部分,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode),GC分代年龄,锁状态标志,线程持有的锁,偏向线程id,偏向时间戳等。

第二部分,类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

实例数据:对象真正存储的有效信息,也是在层序代码中所定义的各种类型的字段内容。

对齐填充:并不是必然存在,也没有特别的含义,它仅仅起着占位符的作用。

对象的访问定位:

OutOfMemoryError异常:

能根据异常的信息快速判断是哪个区域的内存溢出,知道什么样的代码可能会导致这些区域内存溢出,以及出现这些一场后该如何处理。

Java堆溢出

虚拟机栈和本地方法栈溢出

方法区和运行时常量池溢出

本机直接内存溢出

你可能感兴趣的:(自动内存管理机制--Java内存区域与内存溢出异常)