深入理解Java 虚拟机 之自动内存管理 Eclipse Memory Analyzer,内存泄漏插件,安装使用一条龙

Java 内存区域与内存溢出异常

1. 运行时数据区域

Java 虚拟机在执行Java 程序的过程中慧把它所管理的内存划分为若干个不同的数据区域。如下图所示:

(1).程序计数器:较小的内存空间,可以看作时当前线程所执行的字节码的行号指示器。(是唯一一个不会OOM 的区域)

(2).Java 虚拟栈:存储局部变量、操作数栈、动态链接、方法出口等信息。每一个方法从调用到执行完成,对应一个栈帧在虚拟机栈中入栈和出栈的过程。

(3).本地方法栈:类似与虚拟机栈,只不过两者执行的服务不一样,前者时虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,后者为虚拟机使用到的Native 方法服务。

(4).Java 堆(也叫GC 堆):虚拟机管理的内存中最大的一块,唯一目的就是存放实例对象。

(5).方法区:存储常量、静态变量等。

(6).运行时常量池:属于方法区的一部分

(7).直接内存:既不属于虚拟机也不是Java 虚拟机规范中定义的内存区域。NIO 的时候会用到

2. HotSpot 虚拟机对象

进一步了解这些虚拟机内存中的数据的其他细节。包括创建----布局----访问

(1).对象的创建

在语言层面上,创建对象(例如:克隆、反序列化)通常仅仅是一个new 关键字而已,而在虚拟机中对象创建要复杂的多,如下图所示:

 

 (2).对象的内存布局:三块区域----对象头、实例数据和对齐填充

  • 对象头

    ①用于存储对象自身的运行时数据:哈希码、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等;

    ②对象指向它的类元数据的类型指针(但是不一定每个对象都有),如果对象时数组那么对象头中还包括一块用于记录数组长度的数据;

  • 实例数据:是对象真正存储的有效信息,也是程序代码中所定义的哥中类型的字段内容;
  • 对齐填充:不是必然存在,也没有特别的含义,仅仅起着占位符的作用;

(3).对象的访问定位:原理:栈上的reference 数据来操作堆上的具体对象,有两种方式----句柄和直接指针

  • 句柄:Java 堆中划分一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息,如下图所示

   

  • 直接指针:Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference 中存储的直接就是对象地址,如下图所示

   

  • 两种访问的区别:句柄:reference记录的是句柄地址,对象改变时只需要改变句柄中实例数据的指针,不影响reference指向,更稳定;直接指针:直接访问对象,相对句柄访问中间少了一层句柄池,因此更快,但是开销会积少成多,增加执行成本。
3. OutOfMemoryError 异常

(1).Java 堆溢出:使用Eclipse Memory Analyzer 分析堆转储快照文件,详细见Eclipse Memory Analyzer,内存泄漏插件,安装使用一条龙

(2).虚拟机栈和本地方法栈溢出:

①如果线程请求的栈深度大于虚拟机所运行的最大深度,将抛出StackOverflowError 异常(内存泄漏)

②如果虚拟机在扩展时无法申请到足够的内存空间,则抛出OutOfMemoryError 异常(内存溢出)

(3).方法区和运行时常量池溢出

(4).本机直接内存溢出

4. 总结

1. 虚拟机中的内存是如何划分

2. 异常产生的原因

  

猜你喜欢

转载自www.cnblogs.com/hellovoyager1/p/9153434.html
今日推荐