首先说一下JAVA虚拟机运行时会管理的内存区域:

  1. 程序计数器:当前线程执行的字节码的行号指示器,线程私有.
  2. JAVA虚拟机栈:Java方法执行的内存模型,每个Java方法的执行对应着一个栈帧的进栈和出栈的操作。
  3. 本地方法栈:类似“ JAVA虚拟机栈 ”,但是为native方法的运行提供内存环境。
  4. JAVA堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分为新生代,老生代。
  5. 方法区:用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot中的“永久代”。
  6. 运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。
  7. 直接内存:并不是JVM运行时数据区的一部分, 可直接访问的内存, 比如NIO会用到这部分。

所以除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。

常见OOM情况

java.lang.OutOfMemoryError: Java heap space ------>java堆内存溢出,
此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。
对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。

java.lang.OutOfMemoryError: PermGen space ------>java永久代溢出,即方法区溢出了,
一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。
当出现此种情况时可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。
注意,过多的常量尤其是字符串也会导致方法区溢出。

java.lang.StackOverflowError ------> 不会抛OOM error,但也是比较常见的Java内存溢出。
JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。
可以通过虚拟机参数-Xss来设置栈的大小。