内存溢出是指应用系统中存在不可回收的内存或者过度使用内存,最终使得程序运行所使用的内存大于虚拟机所能提供的最大内存.本文编译自《深入理解java虚拟机》。我以前见过阿里。1、内存溢出的原因内存溢出就是内存不足。内存溢出的原因有很多。常见的有以下几种:1、内存中加载的数据量过大,比如一次从数据库中取的数据太多;2.Collection类中有对对象的引用,使用后不清除,使JVM无法回收;3、代码中存在死循环或循环生成过多重复对象实体;4.使用的第三方软件中的BUG;5、启动参数内存值设置过小;当然,实际情况下内存溢出的原因太多了。下面把这些原因归纳一下:上图是基于java7.从上图中,我们可以得到如下信息:java虚拟机将内存划分为5个模块。(1)程序计数器:程序计数器是线程私有的,它的主要作用是通过改变这个计数器的值来选择下一条要执行的字节码指令。由于每个线程都有一个,所以这些线程的计数器是相互独立的。也不会抛出异常。(2)虚拟机栈和本地方法栈:虚拟机栈描述了java方法执行的内存模型。每个方法执行时都会创建一个栈帧来存放局部变量表、操作数栈、动态链接、方法导出等信息。本地方法栈和虚拟机栈的区别在于,虚拟机栈为虚拟机执行java方法服务,而本地方法栈为虚拟机提供native方法服务。在单线程运行中,无论是因为栈帧太大还是虚拟机栈空间太小,当无法分配栈空间时,虚拟机抛出的是StackOverflowError异常,而不是OutOfMemoryError异常。在多线程环境下,会抛出OutOfMemoryError异常。(3)java堆和方法区:java堆区主要存放对象实例和数组等,方法区存放类信息、常量、静态变量等,运行时常量池也是方法区的一部分。这两个区域是线程共享的,只会抛出OutOfMemoryError。不知道大家有没有看到B站经典的面试场景,在回答Java内存操作数据区结构的时候,上面的函数是一方面。如果在回答的时候加上内存溢出的问题,那将是一个很大的加分项。二、内存溢出示例1、堆溢出由于堆中存放的是实例对象,所以我们无线创建实例对象。堆迟早会满。publicclassHeapOOM{staticclassUser{}publicstaticvoidmain(String[]args){Listlist=newArrayList();while(true){list.add(newUser());}}}/*Exceptioninthread"main"java.lang.OutOfMemoryError:GCoverheadlimitexceededatcom.fdd.test.HeapOOM.main(HeapOOM.java:11)*/因为我提前设置了堆区内存,无限创建会抛出异常。2、虚拟机栈和本地方法栈溢出Java虚拟机规范中描述了两种异常:如果线程请求的栈深度大于虚拟机锁允许的最大深度,将抛出StackOverflowError异常。如果虚拟机在扩展栈时无法申请到足够的内存空间,则会抛出OutOfMemoryError异常。第一个我们只需要使用方法递归调用来模拟:}/*Exceptioninthread"main"java.lang.StackOverflowErroratsun.nio.cs.ext.DoubleByte$Encoder.encodeLoop(DoubleByte.java:617)atjava.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)atsun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)atsun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)atjava.io.OutputStreamWriter.write(OutputStreamWriter.java:207)atjava.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)atjava.io.PrintStream.write(PrintStream.java:526)atjava.io.PrintStream.print(PrintStream.java:597)atjava.io.PrintStream.println(PrintStream.java:736)atcom.fdd.test.StackOutOfMemoryError.go(StackOutOfMemoryError.java:11)atcom.fdd.test.StackOutOfMemoryError.go(StackOutOfMemoryError.java:13)*/第二种也可以递归调用simulation,而是使用类直接调用。publicclassJavaVMStackSOF{privateintstackLength=1;publicvoidstackLeak(){stackLength++;stackLeak();}publicstaticvoidmain(String[]args){JavaVMStackSOFoom=newJavaVMStackSOF();oom.stackLeak();}}/*Exceptioninthread"main"java.lang.StackOverflowErroratcom.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:18)atcom.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)atcom.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)atcom.lindaxuanxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)atcom.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)atcom.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)atcom.linmedaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)atcom.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)atcom.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)...*/3、方法区和运行时常量池涌出publicclassJavaMethodAreaOOM{publicstaticvoidmain(String[]args){while(true){Enhancerenhancer=newEnhancer();enhancer.setSuperclass(User.class);enhancer.setUseCache(false);enhancer.setCallback(newMethodInterceptor(){publicObjectintercept(Objectobj,Methodmethod),Object[]args,MethodProxyproxy)throwsThrowable{returnproxy.invokeSuper(obj,args);}});enhancer.create();}}staticclassUser{}}/*Exceptioninthread"main"Exception:java.lang.OutOfMemoryErrorthrownfromtheUncaughtExceptionHandlerinthread"主要“*/4。本机的DirectMemory容量可以通过-XX:MaxDirectMemorySize指定。如果不指定,则默认与Java堆的最大值相同(由-Xmx指定)0];unsafeField.setAccessible(true);Unsafeunsafe=(Unsafe)unsafeField.get(null);while(true){unsafe.allocateMemory(_1MB);}}}上面介绍了几个例子,那么如何排查这种问题?3、内存溢出排查其实排查代码最重要的就是检查代码,而内存溢出往往是代码问题。当然,需要注意以下几点:(1)内存中加载的数据量过大,比如一次从数据库中取出的数据过多;(2)集合类中有对象的引用,使用后没有清除,使得JVM无法回收;(3)代码中存在死循环或循环生成过多重复对象实体;(4)使用的第三方软件中的BUG;(5)启动参数内存值设置过小;最后解决了。第一步是修改JVM启动参数,直接增加内存。第二步是查看错误日志。第三步,遍历并分析代码,找出可能发生内存溢出的地方。一般来说,代码错误的概率较高。当然,不同的场景、不同的错误总是复杂多样的。本文转载自微信公众号“愚公要移山”,可通过以下二维码关注。转载本文请联系愚公想移山公众号。