还原案发现场
先上一段程式来模拟可以触发OutOfMemoryError,此程式是无限循环地往Map里面塞东西
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class Test {
public static void main (String args[]) throws Exception {
Map<Integer, String> map = new HashMap<Integer, String>();
Random r = new Random();
while ( true ) {
map.put(r.nextInt(), "value" );
}
}
}
通过javac Test.java
编译成功后,再通过java -Xmx12m Test
去执行指令,这里的-Xmx12m
是一个关键,这里指定了这个java程式,能使用的heap memory的上限为12M,此时执行完指令的时候会喷出以下错误:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.HashMap.resize(HashMap.java:703)
at java.util.HashMap.putVal(HashMap.java:662)
at java.util. HashMap.put(HashMap.java:611)
at Test.main(Test.java:13)
这样可以看到喷出此错误讯息,这代表说假设你电脑本身内存空间8G,但你分配给java应用程式的记忆体只有12 MB的时候,它不会跨过这个12 MB限制,即使电脑还有将近8G的内存空间,它是不会超过12 MB,总归一句话,使用的内存超出了我们设定给他的限制会导致OOM (Out of Memory)
这里可以注意到叫做『Heap Space』,也就是程式运行时JVM可调配让程式使用的内存空间,Class实例化的Instance也是被放在这个区域,除了Heap之外,还有PermGen的设定,PermGen指的是Memory永久保存区,是存放Class, Meta Info的地方,如果太小可能就会在pre compile的阶段把PermGen弄爆
解决方法
通常内存不够,就是给他开大加下去!但万一你的程式刚好是无穷回圈地往某一个地方塞东西,这样加大内存就没有任何意义了,因为这属于程式上的Bug,要解决的不是内存,而是写出这程式的人解决程式逻辑的Bug 才对。
但如果是本身内存真的不够用,那就是加上内存试试看,如果加了好几XXG上去依旧不能用,就开始要分析出错的原因了。
至于要如何分析,虽然log会喷出Exception的讯息,但总不会一直蹲在log前面看Exception哪天喷出来,就算log以云端方式保存,Exception能分析的程度还是有限。
所以可以加上以下指令把当时喷出OOM的详细状况dump出来
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp
会把在OOM的时候,把整个heap等等当下执行详细的状况储存变成一个档案,以上述的范例来说,使用的完整指令为:java -Xmx12m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp Test
,这样出现OOM的时候,就会往/tmp底下放入一个副档名为.hprof可分析档案
java -Xmx12m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp Test
java.lang.OutOfMemoryError: Java heap space
Dumping heap to /tmp/java_pid57606.hprof ...
Heap dump file created [19050199 bytes in 0.163 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java. util.HashMap.resize(HashMap.java:703)
at java.util.HashMap.putVal(HashMap.java:662)
at java.util.HashMap.put(HashMap.java:611)
at Test.main(Test.java :13)
不过要注意的是,当应用程式越庞大的时候,产生出来的hprof就会越大,高达GB等级以上也是很常见的,所以服务器保留适当的空间就很重要
这时再透过java内建的一个分析程式jvisualvm去分析这个dump就可以找到出现OOM的地方,通常jvisualvm是位在java home里面bin底下的位置
以Mac来说是在这个路径底下:/Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home/bin/jvisualvm
Widnwos则是会在C:\Program Files\Java\jdk1.8.0_65
这前提是你没有自行更改安装的位置,有更改安装位置的话,那你自己应该就知道在哪了
打开后长这样,然后开启刚刚dump 出来的hprof 档案
在里面会看到一个『Thead casuing OutOfMemoryError exception: main』
点选main 后就可以看到错误的地方
点上面class 可以获得比较详细的资讯,包含使用内存多少的量都能够知道
以上是简单介绍针对OOM 排错的一点心得和介绍
Tomcat 设定方法
在tomcat预设的资料夹底下,进入到bin的资料夹,linux用户新增一行程式,新增一个setenv.sh的档案:
export JAVA_OPTS="-Xmx12m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp"
windows用户则是,新增一个setenv.bat的档案
JAVA_OPTS="-Xmx12m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp"
后记
实际上并没有一个银弹可以顺利地解决OOM的方法,必须找到是程式的逻辑Bug导致OOM,又或是本身程式就是需要比较大的内存,又或是第三方的Library写不好,又或是流量太大开太多Thread,原因有很多种,只能透过分析的方式找到引起的主因,否则,单纯加大内存不会解决根本原因,不然只是推迟内存溢出的时间点而已。。。
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/1028.html
暂无评论