您的位置:首页 > 编程语言 > Java开发

笔记:深入理解JVM 第2章 Java内存区域与内存溢出

2014-12-21 16:53 981 查看
1、JVM 运行时数据区

所有线程共享的数据区:方法区(持久代)、堆区

线程隔离的数据区:程序计数器、Java虚拟机栈区

堆区构成:新生代 ( 由Eden, From Survivor, To Survivor 构成)、老生代

运行时常量池:方法区一部分,用于存放编译期生成的各种字面量和符号引用

直接内存:不是JVM 运行时数据区的一部分,不受Java堆大小限制,但是受物理内存限制,也会抛出 OutOfMemoryError。Java NIO 中的DirectByteBuffer 使用了直接内存。

2、OutOfMemoryError 案例

(1)、堆溢出

在对象数量达到最大堆容量限制后,发生堆内存溢出异常 OutOfMemoryError: Java heap space 。

JVM配置:

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError


代码:

public static void main(String[] args) {
List<Object>  list = new LinkedList<Object>();
for(int i=0;i<Long.MAX_VALUE;i++)
{
list.add(new XX());
}
}


输出:

java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to java_pid4192.hprof ...
Heap dump file created [37582435 bytes in 0.265 secs]
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.LinkedList.linkLast(Unknown Source)
at java.util.LinkedList.add(Unknown Source)
at chapter2.HeapOver.main(HeapOver.java:14)


使用Eclipse Memory Analyzer 分析生成的Dump文件:java_pid4192.hprof
快照






(2).栈溢出

线程的请求栈深度大于虚拟机所允许最大深度,将抛出StackOverflowError

如果虚拟机在扩展栈时候无法申请得到足够的内存空间,将抛出OutOfMemoryError

JVM配置:

-Xss128k


单线程代码:

public class StackOverExam1 {

public int stackLength = 1;

public void methed1() {
System.out.println("Stack Length:" + stackLength++);
if(stackLength < Long.MAX_VALUE)
{
methed1();
}
}

public static void main(String[] args) {
StackOverExam1 exam = new StackOverExam1();
exam.methed1();
}

}


抛出异常:

Stack Length:987
Stack Length:988
Exception in thread "main" java.lang.StackOverflowError
at java.nio.Buffer.<init>(Unknown Source)
at java.nio.CharBuffer.<init>(Unknown Source)
at java.nio.HeapCharBuffer.<init>(Unknown Source)
at java.nio.CharBuffer.wrap(Unknown Source)
at sun.nio.cs.StreamEncoder.implWrite(Unknown Source)
at sun.nio.cs.StreamEncoder.write(Unknown Source)


在单线程下,无论是栈帧太大还是虚拟机容量太小,当内存无法分配时候,虚拟机抛出的都是StackOverflowError。

多线程情况下,-Xss设置得越大,即为每个线程栈分配内存越大,越容易抛出 OutOfMemoryError: unable to create new native thread

多线程时候JVM配置:
-Xss2m


多线程代码:

public static void main(String[] args) {

for(long i=0;i<Long.MAX_VALUE;i++)
{
System.out.println("Thread Num:"+ (++i));
new Thread(new Runnable()
{

@Override
public void run() {
while(true)
{

}
}

}).start();
}
}


抛出异常:

ava.lang.OutOfMemoryError: unable to create new native thread


(3). 对象常量池和方法区溢出

抛出 OutOfMemory: PermGen space

常量池溢出例子:

String.intern() 是一个Native方法,其作用是:若字符串常量池包含此String对象,则返回该对象;否则将其加入到常量池中,再返回。

JVM配置:

-XX:PermSize=10m -XX:MaxPermSize=10m
代码:

public class RuntimeConstPoolOver {

public static void main(String[] args) {
List<String> list = new LinkedList<String>();
for (int i = 0; i < Long.MAX_VALUE; i++) {
//System.out.println(i);
list.add(String.valueOf(i).intern());
}
}

}


JDK1.6 输出:

java.lang.OutOfMemoryError: PermGen space
JDK 1.7 不会有错误

方法区溢出例子:

Spring、Hibernate 等框架会使用CGLib技术,将动态生成的Class载入方法区,当方法区太小时,引发

java.lang.OutOfMemoryError: PermGen space


(4).直接内存溢出

DirectMemory 容量可通过 -XX: MaxDirectMemorySize 制定,若无制定则与JVM堆的最大值(-Xmx) 一样。

判断是否由DirectMemory的溢出条件:

a. Heap Dump 文件不会看到明显异常,文件很小

b. 程序中直接或间接使用了NIO

例子JVM配置:

-Xmx20M -XX:MaxDirectMemorySize=20M


代码:

import java.lang.reflect.Field;
import sun.misc.Unsafe;

public class DirectMemoryOver {

public static void main(String[] args) throws IllegalArgumentException,
IllegalAccessException {

long _100M = 100 * 1024 * 1024;
Field f = Unsafe.class.getDeclaredFields()[0];
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
for (int i = 0; i < Long.MAX_VALUE; i++) {
System.out.println(i);
unsafe.allocateMemory(_100M);
}
}

}


输出:

126
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at chapter2.DirectMemoryOver.main(DirectMemoryOver.java:17)



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: