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

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

                22163938_PQjq.png

目录大纲:

  • (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规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。

                163925_cmTJ_2299686.png

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)

 

转载于:https://my.oschina.net/u/2299686/blog/1620975

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值