Java虚拟机之内存区域与内存溢出异常总结

来源:互联网 发布:淘宝直播怎么做起来 编辑:程序博客网 时间:2024/06/03 16:51

一、Java虚拟机运行时数据区

   Java虚拟机运行时数据区一共分为以下几块:

  线程共享:

     (1)方法区:存储被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据,这部分有一个运行时常量池,用于存放编译期生成的各种字面量和符号引用,较常见的是String的intern()方法。

       (2)Java堆:Java堆是虚拟机管理内存最大的一块。几乎所有的对象实例都在这里分配内存,是垃圾收集器管理的主要区域。

   线程独有:

       (1)程序计数器:当前线程所执行字节码的行号指示器。为了线程切换后能恢复到正确的位置,每个线程都需要一个独立的计数器,彼此互不影响。是唯一一个Java虚拟机中规范中没有规定任何OutOfMemoryError情况的区域。

         (2)虚拟机栈:虚拟机栈是为虚拟机执行Java方法(即字节码)服务的,也就是在方法执行的时候,会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机中入栈到出栈的过程。

          (3)本地 方法栈:和虚拟机栈差不多,是Java虚拟机为native方法服务的。

二、溢出

 1. Java堆溢出

原因:Java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制来清除这些对象,那么对象达,到最大堆的容量限制后,就会产生内存溢出异常,即OutOfMemoryError。

在Myeclipse中,解决方法是:通过内存映像分析工具对Dump出来的堆转储快照进行分析,重点是确定内存中的对象是否是需要的,也就是分清楚到底是出现内存泄漏还是内存溢出。

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

  原因:String.intern()是一个Native方法,在JDK1.6中,intern()方法将首次遇到的字符串实例复制到永久代中,返回的是永久代中这个实例字符串的引用,而JDK1.7中,只是在常量池中记录首次出现的实例引用。

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

原因:(1)如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverFlowError异常。

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

  如使用-Xss参数减少栈内存容量。结果:抛出StackOverFlowError异常,异常时出现的堆栈深度相应减小。

  定义大量的本地变量,增大此方法帧中本地变量表的长度。

  在单线程的情况下,无论由于栈帧太大还是虚拟机栈帧容量太小,当内存无法分配时,虚拟机抛出的都是StackOverFlowError异常。

  在建立过多线程导致的内存溢出,在不能减少最大堆和减少栈容量来换取更多的线程,此时通过减少内存的手段解决内存溢出问题。

4. 本机直接内存溢出

直接通过反射获取Unsafe实例进行内存分配。即Unsafe.allocateMemory().

三、 HotSpot虚拟机在java堆中进行对象分配、布局和访问的全过程

 1. 对象的创建

 (1) 虚拟机遇到一条new指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用所代表的类是否已经被加载、解析、初始化过。如果没有,那必须先执行相应的类加载全过程。在类加载检查通过后,接下来虚拟机将对新生对象分配内存。对象所需大小在类加载完成后便可完全确定(如(2)中所述),为对象分配空间的任务等同于在Java堆中将一块确定大小的内存划分出来。有两种方式:指针碰撞和空闲列表。选择哪种方式有Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩收集功能决定。同时也要考虑并发情况下的线程安全问题,解决此问题有两种方案。(1)、分配内存空间的动作进行同步处理—实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性;(2)、把内存分配的动作按照线程在不同空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB).

  (2)内存分配完成后,虚拟机将分配好的内存空间都初始化为零值(不包括对象头)。

 (3) 接下来,虚拟机对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据、对象的哈希吗、对象的GC分代年龄等信息。这些信息保存在对象头里。

 (4)执行new指令之后会接着执行<init>方法,将对象按照程序员的意愿进行初始化。

由此,一个可用的对象被创建出来。

2、对象的内存布局

   在虚拟机中,对象在内存中存储布局分为3块区域:对象头、实例数据和对其填充。

 (1)HotSpot虚拟机对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希吗、GC分代年龄持有的、锁状态标志、线程持有的锁等。另外一部分是类型指针,虚拟机通过这个指针来确定这个对象是那个类的实例。

(2)实例数据部分是对象真正存储的有效信息,也是程序代码中定义的各种类型字段的内容。

(3)对齐填充并不是必然存在的,起着占位符的作用。

3、对象的访问定位

 创建对象是为了使用,Java程序需要通过栈上的reference数据来操作堆上的具体对象。对象的访问方式由虚拟机的具体实现决定的。主流的有两种访问方式:使用句柄和直接指针。

1 0