具体解决异常的办法,查看jvm运行时各个部分的内存状态,包括垃圾回收时,内存状态,包括修改了,jvm参数后,参数是否修改成功,可以使用jdk bin下的一些性能监控工具。详情可参考这篇文章
首先:JVM运行时区域包括:程序计数器,虚拟机栈,本地方法栈,java堆,方法区(包含运行时常量池),直接内存。
1.程序计数器是当前线程所执行的字节码的行号指示器,线程私有,并且是唯一不会报OutofMemoryError异常的区域。当运行native方法时,计数器的内容为空,运行字节码指令时,保存运行的zi'j字节码指令的地址。
2.虚拟机栈和本地方法栈这两部分作用类似,运行native方法时,分配到本地方法栈中,该部分也是线程私有。一个对象对象在创建时,占用多少栈内存,是明确大小的。栈的内存是可以动态申请的,当申请不到对应的内存时,报OutofMemoryError。如果线程请求的栈的深度大于虚拟机suo所允许的最大深度,则抛出stackoverfolwError。在虚拟机默认参数,多线程情况下,栈的深度达到1000-2000,对应正常的方法调用,包括递归,完全够用,如果建立的线程数过多,导致内存溢出,在不减少线程数量的情况下,只能通过,减少最大堆和减少栈容量来换取更多的线程,来解决栈内存的溢出。
3.方法区。在jdk1.7中,永久带是方法区的实现,永久带不再堆中。jdk1.8之后,永久带被移除,元空间用来实现方法区。元空间被放到了java堆中,和堆共享内存大小。逻辑上不互联,物理上互联。方法区主要包含常量池,静态变量,类的信息。jdk1.7之前,方法区内存溢出,会报永久带内存溢出,即:OutOfMemoryError: PermGen space。而1.8之后,报元空间内存溢出即:OutOfMemoryError: Metaspace。
4.堆内存溢出,当对象无法创建的时候,会触发垃圾收集回收,还是无法创建,且没办法申请堆内存,则报堆内存溢出。即:OutOfMemoryError: java heap space。
5.直接内存,并不是虚拟机运行时数据区域。jdk1.4之后,引入niogai概念,加入了通道,缓冲区。可以直接使用native方法库直接分配堆外内存。在某些场景下,可以避免,在堆中来回复制对象,而是直接通过DirectByteBuffer直接操作该ne内存区域,从而提高性能。直接内存溢出:是在程序使用了NIO后发生OutOfMemoryError 异常。