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

Java学习(一):JVM内存区域与内存溢出异常

2018-02-10 00:00 459 查看
摘要: 实践是检验整理的唯一标准

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。根据《Java虚拟机规范(Jave SE 7 版)》的规定,Java虚拟机所管理的内存将会包括一下几个运行时数据区域,如下图所示:



目录大纲:

(1) 程序计数器

(2) Java虚拟机栈

(3) 本地方法栈

(4) Java堆

(5) 方法区

(6) 运行时常量池

(7) PermGen (永久代) 与 Metaspace (元空间)

(8) Java堆溢出

(9) 虚拟机栈和本地方法栈溢出

(10) 方法区与常量池溢出

1、程序计数器

程序计数器是一块较小的内存空间,它可以看成是当前线程所执行的字节码的行号指示器。程序计数器记录线程当前要执行的下一条字节码指令的地址。由于Java是多线程的,所以为了多线程之间的切换与恢复,每一个线程都需要单独的程序计数器,各线程之间互不影响。这类内存区域被称为“线程私有”的内存区域。

2、Java虚拟机栈

Java虚拟机栈是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。虚拟机栈可能出现两种异常:由线程请求的栈深度过大超出虚拟机所允许的深度而引起的StackOverflowError异常;以及由虚拟机栈无法提供足够的内存而引起的OutOfMemoryError异常。

3、本地虚拟机栈

本地方法栈与虚拟机栈类似,他们的区别在于:本地方法栈用于执行本地方法(Native方法);虚拟机栈用于执行普通的Java方法。在HotSpot虚拟机中,就将本地方法栈与虚拟机栈做在了一起。 本地方法栈可能抛出的异常同虚拟机栈一样。

4、Java堆

Java堆是被所有线程共享的一块内存区域,此内存区域的唯一目的就是存放对象实例。Java堆是垃圾收集器管理的主要区域,从内存回收的角度来看,Java堆中可以细分为:新生代(Eden空间、From Survivor空间、To Survivor空间)、老年代、永久代(JDK8移除)。

5、方法区

方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。

6、运行时常量池

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

7、永久代与元空间

在JDK1.8中使用元空间代替永久代,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。



8、Java堆溢出

Java堆用来存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象到达最大堆的容量限制后就会产生内存溢出异常。

参数控制:-Xms20m -Xmx20m -XX:+PrintGCDetails

package com.xusg.study.jvm.oom;

import java.util.ArrayList;
import java.util.List;

/**
* 堆内存溢出
* VM Args:-Xms20m -Xmx20m -XX:+PrintGCDetails
*
* @Author Xusg
* @Date 18/2/7 14:46
*/
public class HeapOOM {

static class OOMObject {
}

public static void main(String[] args) {
List<OOMObject> oomObjects = new ArrayList<>();

while (true) {
oomObjects.add(new OOMObject());
}
}
}

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at com.xusg.study.jvm.oom.HeapOOM.main(HeapOOM.java:24)


9、虚拟机栈和本地方法栈溢出

HotSpot 虚拟机中并不区分虚拟机栈和本地方法栈。如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。如果虚拟机在扩展栈时,无法申请到足够的内存空间,则将抛出OutOfMemoryError异常。

参数控制:-Xss128k -XX:+PrintGCDetails

package com.xusg.study.jvm.oom;

/**
* 虚拟机栈异常
* VM Args:-Xss128k
*
* @Author Xusg
* @Date 18/2/10 11:48
*/
public class StackOOM {

public void stackLeak() {
stackLeak();
}

public static void main(String[] args) {
StackOOM stackOOM = new StackOOM();

try {
stackOOM.stackLeak();
} catch (Exception e) {
e.printStackTrace();
}
}

}

Exception in thread "main" java.lang.StackOverflowError
at com.xusg.study.jvm.oom.StackOOM.stackLeak(StackOOM.java:15)
at com.xusg.study.jvm.oom.StackOOM.stackLeak(StackOOM.java:15)
at com.xusg.study.jvm.oom.StackOOM.stackLeak(StackOOM.java:15)


10、方法区与常量池溢出

方法区存放的类信息,通过CGLib动态生成Class,在Class信息到达最大的容量限制后就会产生内存溢出异常。

参数控制:

JDK1.7:-XX:PermSize=10m -XX:MaxPermSize=10m -XX:+PrintGCDetails

JDK1.8:-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m -XX:+PrintGCDetails

package com.xusg.study.jvm.oom;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
*
* @Author Xusg
* @Date 18/2/7 16:37
*/
public class MetaSpaceOOM {

public static void main(String[] args) {
int total = 0;
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MetaSpaceOOM.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o, objects);
}
});

enhancer.create();

System.out.println("代理对象数:" + (++total));
}
}
}

Exception in thread "main"
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)
at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
at com.xusg.study.jvm.oom.MetaSpaceOOM.main(MetaSpaceOOM.java:32)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Java JVM