---------------------------------------------------------------------------------------------------------------
Java Exception 异常信息查询,查异常网 查找开发中遇到的各种异常问题:http://www.findexception.com/
---------------------------------------------------------------------------------------------------------------------------------
1.背景知识
1)JVM体系结构2)JVM运行时数据区
2.堆溢出(OutOfMemoryError:java heap space)
堆(Heap)是Java存放对象实例的地方。
堆溢出可以分为以下两种情况,这两种情况都会抛出OutOfMemoryError:java heap space异常:
1)内存泄漏
内存泄漏是指对象实例在新建和使用完毕后,仍然被引用,没能被垃圾回收释放,一直积累,直到没有剩余内存可用。
如果内存泄露,我们要找出泄露的对象是怎么被GC ROOT引用起来,然后通过引用链来具体分析泄露的原因。
分析内存泄漏的工具有:Jprofiler,visualvm等。
2)内存溢出
内存溢出是指当我们新建一个实力对象时,实例对象所需占用的内存空间大于堆的可用空间。
如果出现了内存溢出问题,这往往是程序本生需要的内存大于了我们给虚拟机配置的内存,这种情况下,我们可以采用调大-Xmx来解决这种问题。
示例:
packagecom.demo3;importjava.util.ArrayList;importjava.util.List;publicclassOOMTest
{publicstaticvoidmain(String[] args) {List<byte[]> buffer =
newArrayList<byte[]>();buffer.add(newbyte[10* 1024* 1024]);}}[GC
836K->568K(19456K), 0.0234380secs][GC 568K->536K(19456K),
0.0009309secs][Full GC 536K->463K(19456K), 0.0085383secs][GC
463K->463K(19456K), 0.0003160secs][Full GC 463K->452K(19456K),
0.0062013secs]Exception in thread "main"java.lang.OutOfMemoryError: Java
heap spaceat com.demo3.OOMTest.main(OOMTest.java:10)
3.持久带溢出(OutOfMemoryError: PermGen space)
持久带(PermGen space)是JVM实现方法区的地方,因此该异常主要设计到方法区和方法区中的常量池。
1).方法区
方法区(Method
Area)不仅包含常量池,而且还保存了所有已加载类的元信息。当加载的类过多,方法区放不下所有已加载的元信息时,就会抛出OutOfMemoryError: PermGen space异常。
主要有以下场景:
- 使用一些应用服务器的热部署的时候,我们就会遇到热部署几次以后发现内存溢出了,这种情况就是因为每次热部署的后,原来的class没有被卸载掉。
-如果应用程序本身比较大,涉及的类库比较多,但是我们分配给持久带的内存(通过-XX:PermSize和-XX:MaxPermSize来设置)比较小的时候也可能出现此种问题。
2).常量池
常量池(Runtime Constrant
Pool)专门放置源代码中的符号信息。常量池中除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值外,还包含一些以文本形式出现的符号引用,比如:类和接口的全限定名;字段的名称和描述符;方法的名称和描述符等。
当常量池需要的空间大于常量池的实际空间时,也会抛出OutOfMemoryError: PermGen space异常。
例如,Java中字符串常量是放在常量池中的,String.intern()这个方法运行的时候,会检查常量池中是否存和本字符串相等的对象,如果存在直接返回对常量池中对象的引用,不存在的话,先把此字符串加入常量池,然后再返回字符串的引用。那么可以通过String.intern方法来模拟一下运行时常量区的溢出.
示例:
packagecom.demo3;importjava.util.ArrayList;importjava.util.List;importjava.util.UUID;publicclassOOMTest
{publicstaticvoidmain(String[] args) {List<String> list =
newArrayList<String>();while(true)
{list.add(UUID.randomUUID().toString().intern());}}}
4.线程栈
栈(JVM Stack)存放主要是栈帧( 局部变量表, 操作数栈 , 动态链接 , 方法出口信息 )的地方。注意区分栈和栈帧:栈里包含栈帧。
与线程栈相关的内存异常有两个:
- StackOverflowError(方法调用层次太深,内存不够新建栈帧)
- OutOfMemoryError(线程太多,内存不够新建线程)
1).java.lang.StackOverflowError
栈溢出抛出java.lang.StackOverflowError错误,出现此种情况是因为方法运行的时候,请求新建栈帧时,栈所剩空间小于战帧所需空间。
例如,通过递归调用方法,不停的产生栈帧,一直把栈空间堆满,直到抛出异常 :
packagecom.demo3;publicclassOOMTest {publicvoidstackOverFlowMethod()
{stackOverFlowMethod();}publicstaticvoidmain(String... args) {OOMTest
oom = newOOMTest();oom.stackOverFlowMethod();}}Exception in thread
"main"java.lang.StackOverflowErrorat
com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)at
com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)at
com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)at
com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)at
com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)at
com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)at
com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)at
com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5).....
2).java.lang.OutOfMemoryError:unable to create new native thread
因为虚拟机会提供一些参数来保证堆以及方法区的分配,剩下的内存基本都由栈来占有,而且每个线程都有自己独立的栈空间(堆,方法区为线程共有)。所以:
- 如果你把虚拟机参数Xss调大了,每个线程的占用的栈空间也就变大了,那么可以建立的线程数量必然减少
- 公式:线程栈总可用内存=JVM总内存-(-Xmx的值)- (-XX:MaxPermSize的值)- 程序计数器占用的内存
如果-Xmx或者-XX:MaxPermSize太大,那么留给线程栈可用的空间就越小,在-Xss参数配置的栈容量不变的情况下,可以创建的线程数也就越小。
上述两种情况都会导致:当创建的线程数太多时,栈内存不够用来创建新的线程,那么就会抛出java.lang.OutOfMemoryError:unable
to create new native thread 异常。
PS:由于在window平台的虚拟机中,java的线程是隐射到操作系统的内核线程上的,所以运行一下产生该异常的代码时,可能会导致操作系统假死。