分析工具: Eclipse Memory Analyzer
图解jvm内存结构:
Java堆溢出:
package jvm;
import java.util.ArrayList;
import java.util.List;
/**
* -verbose:gc -Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails
*
* @author xlj
*/
public class HeapOOM {
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
while (true) {
list.add(new OOMObject());
}
}
}
class OOMObject {
}
1、-Xms :表示java虚拟机堆区内存初始内存分配的大小
2、-Xmx: 表示java虚拟机堆区内存可被分配的最大上限
1、-XX:newSize:表示新生代初始内存的大小,应该小于 -Xms的值;
2、-XX:MaxnewSize:表示新生代可被分配的内存的最大上限;当然这个值应该小于 -Xmx的值;
2、 -XX:MaxPermSize:表示对非堆区分配的内存的最大上限。
[GC [PSYoungGen: 5416K->504K(6656K)] 5416K->3681K(20480K), 0.0066120 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC [PSYoungGen: 6043K->504K(6656K)] 9220K->8232K(20480K), 0.0060092 secs] [Times: user=0.05 sys=0.02, real=0.01 secs][Full GC [PSYoungGen: 6648K->0K(6656K)] [ParOldGen: 10893K->13330K(13824K)] 17541K->13330K(20480K) [PSPermGen: 2614K->2614K(21504K)], 0.1963102 secs] [Times: user=0.41 sys=0.00, real=0.20 secs]
[Full GC [PSYoungGen: 3031K->3001K(6656K)] [ParOldGen: 13330K->13330K(13824K)] 16361K->16331K(20480K) [PSPermGen: 2614K->2614K(21504K)], 0.1339469 secs] [Times: user=0.38 sys=0.00, real=0.13 secs]
[Full GC [PSYoungGen: 3001K->3001K(6656K)] [ParOldGen: 13330K->13318K(13824K)] 16331K->16320K(20480K) [PSPermGen: 2614K->2614K(21504K)], 0.1357866 secs] [Times: user=0.42 sys=0.00, real=0.13 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid17220.hprof ...
Heap dump file created [27972051 bytes in 0.106 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2245)
at java.util.Arrays.copyOf(Arrays.java:2219)
at java.util.ArrayList.grow(ArrayList.java:242)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
at java.util.ArrayList.add(ArrayList.java:440)
at jvm.HeapOOM.main(HeapOOM.java:16)
Heap
PSYoungGen total 6656K, used 3208K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
eden space 6144K, 52% used [0x00000000ff900000,0x00000000ffc220e8,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 13824K, used 13318K [0x00000000feb80000, 0x00000000ff900000, 0x00000000ff900000)
object space 13824K, 96% used [0x00000000feb80000,0x00000000ff881bd8,0x00000000ff900000)
PSPermGen total 21504K, used 2645K [0x00000000f9980000, 0x00000000fae80000, 0x00000000feb80000)
object space 21504K, 12% used [0x00000000f9980000,0x00000000f9c156f8,0x00000000fae80000)
虚拟机栈和本地方法栈溢出:
由于在HotSpot虚拟机中并不区分虚拟机栈和本地方法栈,因此,对于HotSpot来说,虽然-Xoss参数(设置本地方法栈大小)存在,但实际上是无效的,栈容量只由-Xss参数设定。关于虚拟机栈和本地方法栈,在Java虚拟机规范中描述了两种异常:如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
package jvm;
/**
* VM Args:-Xss128k
* @author xlj
*/
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Throwable {
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLeak();
} catch (Throwable e) {
System.out.println("stack length:" + oom.stackLength);
throw e;
}
}
}
stack length:988
Exception in thread "main" java.lang.StackOverflowError
at jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:11)
at jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
………………
package jvm;
/**
* VM Args:-Xss128k
*
* @author xlj 虚拟机栈和本地方法栈溢出
*/
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Throwable {
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLeak();
} catch (Throwable e) {
System.out.println("stack length:" + oom.stackLength);
throw e;
}
}
}
Exception in thread"main"java.lang.OutOfMemoryError:PermGen spaceat java.lang.String.intern(Native Method)at org.fenixsoft.oom.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:18)
2.4.3 方法区和运行时常量池溢出
package jvm;
import java.util.ArrayList;
import java.util.List;
/**
* VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M
* @author xlj 运行时常量池导致的内存溢出异常
*/
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
// 使用List保持着常量池引用,避免Full GC回收常量池行为
List<String> list = new ArrayList<String>();
// 10MB的PermSize在integer范围内足够产生OOM了
int i = 0;
while (true) {
list.add(String.valueOf(i++).intern());
}
}
}
Error occurred during initialization of VM
java.lang.OutOfMemoryError: PermGen space
at sun.misc.Launcher.<init>(Launcher.java:71)
at sun.misc.Launcher.<clinit>(Launcher.java:57)
at java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1489)
at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1474)
package jvm;
/**
* jdk6 都是false
* jdk7 true false
* @author xlj
*这段代码在JDK 1.6中运行,会得到两个false,而在JDK 1.7中运行,会得到一个true和一
个false。产生差异的原因是:在JDK 1.6中,intern()方法会把首次遇到的字符串实例复制
到永久代中,返回的也是永久代中这个字符串实例的引用,而由StringBuilder创建的字符串
实例在Java堆上,所以必然不是同一个引用,将返回false。而JDK 1.7(以及部分其他虚拟
机,例如JRockit)的intern()实现不会再复制实例,只是在常量池中记录首次出现的实例
引用,因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。对str2比
较返回false是因为“java”这个字符串在执行StringBuilder.toString()之前已经出现过,字符串
常量池中已经有它的引用了,不符合“首次出现”的原则,而“计算机软件”这个字符串则是首
次出现的,因此返回true。
*/
public class RuntimeConstantPoolOOM1 {
public static void main(String[] args) {
String str1 = new StringBuilder("中国").append("钓鱼岛").toString();
System.out.println(str1.intern() == str1);
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2);
}
}