自动内存管理机制与内存溢出异常

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

  1. 程序计数器
    1. 较小的内存空间,记录着当前线程执行的字节码指令、分支、循环、跳转、异常处理、线程恢复等基础功能。
    2. 唯一一个没有规定那个OutOfMemoryError的内存区域
  2. Java虚拟机栈
    1. 线程私有
    2. 每个方法在执行的时候都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息
    3. 两种异常:如果线程请求的栈深度超过Java虚拟机允许的深度将抛出StackOverFlow;对于可以动态增长的Java虚拟机,如果申请时没有足够内存将抛出OutOfMemoryError异常
  3. 本地方法栈
    1. 与虚拟机栈的区别是本地方法栈为执行Javanative方法服务
    2. 与虚拟机栈一样,本地方法栈也会抛出同样的两个异常
    1. Java Heap是Java虚拟机管理的最大内存区域,所有对象实例和数组都在堆上进行分配
    2. Java Heap是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”
    3. 从内存回收角度看,由于现在收集器基本都采用分代收集算法,所以Java堆中还可以细分为“新生代”和“老年代”
    4. 如果从内存分配角度看,Java Heap还可划分由多个线程私有的分配缓冲区。不过,无论如何,都与存放内容无关,因为Java Heap上存放的都是对象实例,进一步划分的目的只是为了GC更好的回收或者更快的分配内存
    5. Java Heap可以是物理上不连续的内存空间,主要逻辑上是连续的即可,就像磁盘一样。在实现时可以是固定的也可以是可扩展的,通过-Xmx和-Xms控制
    6. 如果没有内存可以再分配将抛出OutOfMemoryError异常
  4. 方法区
    1. 线程共享的内存区域
    2. 存储类信息、常量、静态变量、即时编译器编译的代码等
    3. 平常所说的永生代其实根本不存在,只不过常用的HotSpot设计人员只把GC扩展至方法区,或者说用永生代实现的方法区,很多其他的jvm不存在永生代这个概念
    4. 并非数据进入了方法区就永久了,而是GC对这块的回收较少,收集目标主要是方法区的常量池和对类型的卸载,一般来说对这块数据的回收很难达到令人满意的效果
    5. 当方法区无法满足内存分配需求时将抛出OutOfMemoryError异
  5. 运行时常量池
    1. 存放编译器生成的各种字面常量、符号引用、直接引用
    2. 可以动态内存扩增
    3. 并不是只有编译期生成的数据才放到常量池,运行期间也可以把新的常量放入池中,典型的例如String.intern()方法
    4. 也会抛出OutOfMemoryError异常
  6. 直接内存
    1. 有native函数库直接分配的对外内存,然后通过在堆中保存的DirectByteBuffer对象作为这块而内存的引用。这样能在一些场景中显著提高性能,避免了在Java堆和native堆中来回复制数据
    2. 例如:java的nio,提供了通道(channel)和缓冲区(buffer)的i/o方式
    3. 虚拟机管理员在分配内存时经常只是分配堆内存忽略了直接内存,导致对内存和直接内存总和大于实际内存或超出了服务器cpu的寻址空间而抛出OutOfMemoryError异常
  7. 对象访问
    1. 例如简单的代码Object object = new Object();
      1. 直接指针访问对象
        1. object存在栈中
        2. new Object()保存在堆中
        3. 而该对象类型例如类信息等对象类型数据保存在方法区中
      2. 句柄访问方式
        1. 在堆中存储new Object()和句柄池,句柄池中保存了对象的指针
        2. 栈中保存句柄池地址
        3. 方法区中保存对象类型数据
      3. 两种方式对比
        1. 第一种访问时节省了一次寻址,访问更快;第二种在垃圾收集对堆中对象地址更改时,只影响句柄池中的映射关系,不会影响栈中引用关系(GC回收时,对象地址改变很正常)
        2. 现在使用的SUN HotSpot采用第一种方式实现
  • ****windows平台中的虚拟机中,Java的线程是映射到操作系统的内核线程上的****

posted @ 2013-07-29 08:49  王 庆  阅读(334)  评论(0编辑  收藏  举报