Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进来,墙里面的人却想出来。

不知道其他人想出来没,反正我是没想出来,为什么这个JVM的运行时内存要这样设计?。

以下内容中,Java虚拟机(JVM)特指HotSpot虚拟机。

JVM启动后,整体来看,会把向操作系统申请到的内存空间分成这样几个部分:

  1. 程序计数器区
  2. 虚拟机栈
  3. 本地方法栈
  4. 方法区

它们在《Java虚拟机规范》中都有相关规定和说明,只是有的区域具体实现细节根据Java虚拟机实现者来决定,《Java虚拟机规范》不做强行要求,例如方法区。下面对这几个部分进行详细叙述,在叙述过程中,需要不断参考下面这个示意图来帮助理解。

Java运行时内存布局

文中示例程序来自
《深入理解Java虚拟机》
周志明·著
如有雷同,不是巧合 ?

1. 程序计数器区(Program Counter Register)

这个区域内容指向当前线程所执行字节码行号,是线程私有区域。和操作系统里面的PC指针类似,只不过这个PC指针指向当前执行的字节码行号,操作系统的指针指向下一条执行的指令地址。

需要额外注意的是,如果当前线程在执行Java程序,那么如上所述指向字节码行号,如果执行的是Native方法,那么这个区域是空(Undefined)的。

该区域也是唯一一个《Java虚拟机规范》里面没有 OutOfMemoryError 错误的定义的区域。

2. Java虚拟机栈(VM Stack)

该区域的内容是Java方法执行的线程内存模型,具体来说,存放的是栈帧,在栈帧中,存放了局部变量表、操作数栈等信息,当一个方法被调用时,就会创建一个栈帧来记录信息,方法从被调用到返回的过程,也就对应了栈帧从入栈(虚拟机栈)到出栈的过程。显而易见,这个区域必须是线程私有的,否则就混乱了呀?。

这个区域在《Java虚拟机规范》中,详细定义了以下内容:

  1. 如果线程所创建的栈帧个数大于虚拟机所允许的个数,那么抛出 StackOverflowError 异常。
  2. 如果在创建栈帧的过程中,因为内存不足所失败了,则会抛出 OutOfMemoryError 异常。

2.1 虚拟机栈异常示例程序

/** * VM Args: -Xss128k * -Xss: 减小栈内存容量 * 虚拟机栈和本地方法栈测试,抛出 StackOverflow 异常 */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;        }    }}

对于 OutOfMemoryError 就需要格外小心了,程序如下:

/** * !! 需要先保存当前系统的工作,因为这个程序比较危险。 * 可能会造成系统假死或者其他异常(甚至强制重启才可以使用)。 * 


 * VM Args: -Xss2m
 * -Xss: 减少栈内存容量
 * 使用线程来制造内存溢出异常
 */


public class JavaVMStackOOM {
    private void dontStop() { while (true) {} }

public void stackLeakByThread() {
        while (true) {
            Thread thread = new Thread(new Runnable() {
                public void run() { dontStop(); }
            });
            thread.start();
        }
    }

public static void main(String[] args) {
        JavaVMStackOOM oom = new JavaVMStackOOM();
        oom.stackLeakByThread();
    }
}

3. 本地方法栈(Native Method Stack)

本地方法栈“雷同”虚拟机栈的功能,唯一的区别在于前者记录的是本地方法的执行记录,后者是Java程序的执行记录。

因为这个区域在《Java虚拟机规范》中并没有详细规定本地方法的实现语言和数据结构,所以虚拟机可以根据自己需要来进行扩展。只不过,在HotSpot虚拟机中,是把虚拟机栈和本地方法栈统一放到一起了的。

在可能抛出的异常类型上,和虚拟机栈一样样:

  1. 如果线程所创建的栈帧个数大于虚拟机所允许的个数,那么抛出 StackOverflowError 异常。
  2. 如果在创建栈帧的过程中,因为内存不足所失败了,则会抛出 OutOfMemoryError 异常。

4. 堆(Heap)

此堆非彼堆,不是同一堆?。上篇文章说到的《探索STL:堆数据结构及算法》,和这里的堆完全不是同一个概念。更何况,这个堆是Java虚拟机的东西,那个是C++的东西,两个混淆了不大好吧?。

Java堆可以说是整个Java虚拟机中最灵活的地方了,也是最值得研究的地方了,目前好多研究都是围绕这个区域的垃圾收集(Garbage Collection, GC)展开的。这个区域在Java虚拟机启动之初就创建好了,给所有线程共享使用,这里存放的唯一内容就是对象实例,拿《Java虚拟机规范》中的描述来说就是:

The heap is the runtime data area from which memory for all class instances and arrays is allocated.

这一块内存是设计成可扩展的,使用参数 -Xmx-Xms 来设定允许的最大内存和允许的最小内存。当内存不够完成实例的创建也不能完成内存扩展时,将会抛出 OutOfMemoryError 异常。

4.1 堆内存溢出程序示例

import java.util.ArrayList;import java.util.List;/** * -Xms 设置最小值 * -Xmx 设置最大值 * -XX:+HeapDumpOnOutOfMemoryError  当出现内存溢出异常时,Dump出当前的内存转储快照 * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * 


 * 堆内存溢出异常测试
 */


class HeapOOM {
    static class OOMObject {}
    public static void main(String[] args) {
        List list = new ArrayList();while (true) {
            list.add(new OOMObject());
        }
    }
}

5. 方法区(Method Area)

这个区域也是线程共享区域,里面存放了被虚拟机加载进来的:

  • 类信息
  • 常量、静态变量
  • 即时编译后的程序

需要注意的是在这里有一个版本分歧,JDK6及以前的版本,方法区使用分代设计来实现该区域,但是JDK7就开始不一样了,首先是在JDK7吧放在永久代的字符串常量池、静态变量移出到了本地内存中,到了JDK8,彻底将方法区的分代设计抛弃了,换成了使用本地内存实现的“元空间”代替。

这个空间在不能满足新的内存分配需求时,抛出 OutOfMemoryError 异常。

4.1 方法区内存溢出示例程序

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

import java.lang.reflect.Method;

/** * VM Args: -XX:PermSize=10m -XX:MaxPermSize=10m -XX:+HeapDumpOnOutOfMemoryError * 上面这个设置是没用的,因为从JDK8开始,HotSpot虚拟机就没有了永久代这么一说,变成了元空间可通过这样设置来实现: * VM Args: -XX:MaxMetaspaceSize=10m * -XX:MaxMetaspaceSize=10m 设置最大元空间,默认是-1,即不设置大小(受限于本地) * -XX:MetaspaceSize=10m    设置元空间的初始空间大小,以字节为单位,如果达到了该值,就会触发垃圾收集,并且会调整元空间的大小 * -XX:MinMetaspaceFreeRatio * 报错为:Exception in thread "main" java.lang.OutOfMemoryError: Metaspace */public class JVMMethodAreaOOM {    static class OOMObject {}

    public static void main(String[] args) {        while (true) {            Enhancer enhancer = new Enhancer();            enhancer.setSuperclass(OOMObject.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();        }    }}

5.1 运行时常量池(Runtime Constant Pool)

这是一个神奇的区域?

首先它是方法区的一个部分,在类文件中有一部分内容是常量池表,这部分内容存放了在编译器生成的字面量和符号引用,在类加载后放到方法区的运行时常量中。

运行时常量池具备动态特性,也就是说可以将运行期产生的常量放到池子中,例如 String.intern() 方法。

同方法区异常一样,在内存申请不足的时候,会抛出 OutOfMemoryError 异常。

5.1.1 运行时常量池异常示例程序

import java.util.HashSet;import java.util.Set;/** * VM Args: -Xmx2m * 因为JDK8已经把原本放在永久代的字符串常量池移到了 Java堆 中, * 所以限制了堆大小之后,会模拟出来堆内存溢出异常 */public class RuntimeConstantPoolOOM {    public static void main(String[] args) {        Set set = new HashSet();short i = 0;while (true) {            set.add(String.valueOf(i++).intern());        }    }}

6. 直接内存(Direct Memory)

如果查《Java虚拟机规范》,并不能够找到这部分内容,也就是说它并不是虚拟机的一部分,但是这部分内容经常被使用到,举个例子:JDK1.4之后出现的NIO(New Input/Output),记起来了吧??这个基于缓冲区(Buffer)和通道(Channel)的I/O模型。使用本地函数直接分配堆外内存,然后通过 DirectByteBuffer 对象作为申请到的堆外内存引用,可以省去在Java堆和本地内存中来回复制数据。

很明显,既然不属于Java虚拟机的范畴,自然也就不会被Java虚拟机大小所限制,不过,仍然要受到本机内存的限制。当内存申请失败时,会抛出 OutOfMemoryError 异常。

6.1 直接内存异常示例程序

import sun.misc.Unsafe;import java.lang.reflect.Field;/** * VM Args: -Xmx20m -XX:MaxDirectMemorySize=10m * -XX:MaxDirectMemorySize  设置最大直接内存,默认是和Java堆保持一致 */public class DirectMemoryOOM {    private static final int _1MB = 1024 * 1024;    public static void main(String[] args) throws IllegalAccessException {        Field unsafeField = Unsafe.class.getDeclaredFields()[0];        unsafeField.setAccessible(true);        Unsafe unsafe = (Unsafe) unsafeField.get(null);        while (true) {            unsafe.allocateMemory(_1MB);        }    }}

好了,图里面出现的各个区域所能出现的问题,已经差不多都遇到了(这种主动犯错的机会可不多?),也该休息休息然后去复习考试了呜呜呜呜呜?……

java ppt转图片 内存溢出_Java虚拟机内存及内存溢出异常相关推荐

  1. java 虚拟机内存类_java 虚拟机类加载 及内存结构

    在面试java工程师的时候,这道题经常被问到,故需特别注意. 1.JVM 简介 JVM 是我们Javaer 的最基本功底了,刚开始学Java 的时候,一般都是从"Hello World &q ...

  2. java内存 海子_Java虚拟机:JVM内存模型和volatile详解

    JVM内存模型和volatile详解 Java内存模型 随着计算机的CPU的飞速发展,CPU的运算能力已经远远超出了从主内存(运行内存)中读取的数据的能力,为了解决这个问题,CPU厂商设计出了CPU内 ...

  3. java虚拟机内存告警_java虚拟机内存溢出各种场景总结

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

  4. java 虚拟机内存管理_java虚拟机内存管理

    程序计数器: 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器. 程序计数器处于线程独占区 如果线程执行的是Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地 ...

  5. java虚拟机内存告警_Java虚拟机总结

    JVM体系四大块: 类的加载 JVM内存结构 GC算法 垃圾回收 GC分析 性能调优 执行引擎 JVM架构图 jvm.arch 一.类的加载机制 类的加载 类的加载,将class文件读入方法区,然后在 ...

  6. 记录java对象修改过的字段_Java垃圾回收器与内存回收策略

    Java中,内存由虚拟机管理,控制着回收什么,什么时候回收,怎么回收. 在栈中内存的随线程产生和分配,销毁而回收,在堆中,需要制定一系列策略来判断该回收哪些区域,以及何时回收. 可达性分析 主流的做法 ...

  7. java jvm内存模型_Java(JVM)内存模型– Java中的内存管理

    java jvm内存模型 Understanding JVM Memory Model, Java Memory Management are very important if you want t ...

  8. java远程桌面图片压缩问题_java用Socket实现的远程桌面浏览 内存溢出问题

    用socket做长连接时,出现了内存溢出的错误.搞了4天的时间总算是搞定了. 现总结下: 1.socket一般分为短连接和长连接. 长连接是一旦一个客户端登陆上服务器,其与服务器之间的连接就不关闭,不 ...

  9. java虚拟机内存监控_java虚拟机内存监控工具jps,jinfo,Jstack,jstat,jmap,jhat使用...

    将会打印出很多jvm运行时参数信息,由于比较长这里不再打印出来,可以自己试试,内容一目了然 Jstack(Stack Trace for Java):JVM堆栈跟踪工具 jstack用于打印出给定的j ...

最新文章

  1. C++调用Python
  2. 解决ViewPager嵌套后子ViewPager不能滑动的方法
  3. 3d正方体旋转相册代码_3d旋转正方体的多种html和css制作方法和相关知识复习讲解
  4. 丰胸神器?网友曝椰树椰汁新广告低俗 疑似虚假宣传被调查...
  5. Numpy中的时间类型
  6. mysql创建用户navicat_14MYSQL创建用户和授权、15Navicat的使用、16-pymysql模块的使用、17-索引...
  7. Java分代垃圾回收机制:年轻代/年老代/持久代(转)
  8. wifi信号桥怎么设置_手机设置路由器WDS桥连方法
  9. 吞剑!喷火!这种江湖卖艺套路能吸引观众吗?
  10. 视频横竖屏模式切换,如何将多个视频任意转换
  11. 在Vmware14中安装Linux系统教程(图文教程)
  12. kindle出现电池感叹号,充电黄灯亮,怎么解决?按AWZ客服的回复弄好了。
  13. ZYNQ有两个CPU?(二)——OCM共享内存
  14. 泸州职业技术学院计算机单招试题,泸州职业技术学院普通类9+3单招考试技能测试考纲...
  15. -Dmaven.multiModuleProjectDirectory system propert
  16. 2022-2028年中国桌面云产业发展动态及市场需求预测报告
  17. 删除u盘插拔记录linux,电脑u盘插拔记录_电脑u盘插拔时间记录
  18. java csv 复杂表头_java读csv 和excel
  19. Python王牌加速库:奇异期权定价的利器!
  20. Python基于BeautifulSoup4库爬取亚马逊网页

热门文章

  1. 《飞鸽传书》已经不算陌生
  2. 即时通讯的企业应用和个人应用的区别
  3. Gmail有充分理由启用SSL加密会话
  4. C#中is vs as
  5. 程序员每天少吃 能活120岁
  6. 新手关于如何看编程经典书的一些疑惑?
  7. HTML5 API详解(18):IndexedDB 本地存储
  8. python-opencv图像处理之哈里斯角检测
  9. 序列的卷积运算与相关运算——MATLAB
  10. 第十届 蓝桥杯大赛 青少年创意编程 C++组