java ppt转图片 内存溢出_Java虚拟机内存及内存溢出异常
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进来,墙里面的人却想出来。
不知道其他人想出来没,反正我是没想出来,为什么这个JVM的运行时内存要这样设计?。
以下内容中,Java虚拟机(JVM)特指HotSpot虚拟机。
JVM启动后,整体来看,会把向操作系统申请到的内存空间分成这样几个部分:
- 程序计数器区
- 虚拟机栈
- 本地方法栈
- 堆
- 方法区
它们在《Java虚拟机规范》中都有相关规定和说明,只是有的区域具体实现细节根据Java虚拟机实现者来决定,《Java虚拟机规范》不做强行要求,例如方法区。下面对这几个部分进行详细叙述,在叙述过程中,需要不断参考下面这个示意图来帮助理解。
文中示例程序来自
《深入理解Java虚拟机》
周志明·著
如有雷同,不是巧合 ?
1. 程序计数器区(Program Counter Register)
这个区域内容指向当前线程所执行字节码行号,是线程私有区域。和操作系统里面的PC指针类似,只不过这个PC指针指向当前执行的字节码行号,操作系统的指针指向下一条执行的指令地址。
需要额外注意的是,如果当前线程在执行Java程序,那么如上所述指向字节码行号,如果执行的是Native方法,那么这个区域是空(Undefined)的。
该区域也是唯一一个《Java虚拟机规范》里面没有 OutOfMemoryError
错误的定义的区域。
2. Java虚拟机栈(VM Stack)
该区域的内容是Java方法执行的线程内存模型,具体来说,存放的是栈帧,在栈帧中,存放了局部变量表、操作数栈等信息,当一个方法被调用时,就会创建一个栈帧来记录信息,方法从被调用到返回的过程,也就对应了栈帧从入栈(虚拟机栈)到出栈的过程。显而易见,这个区域必须是线程私有的,否则就混乱了呀?。
这个区域在《Java虚拟机规范》中,详细定义了以下内容:
- 如果线程所创建的栈帧个数大于虚拟机所允许的个数,那么抛出
StackOverflowError
异常。 - 如果在创建栈帧的过程中,因为内存不足所失败了,则会抛出
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虚拟机中,是把虚拟机栈和本地方法栈统一放到一起了的。
在可能抛出的异常类型上,和虚拟机栈一样样:
- 如果线程所创建的栈帧个数大于虚拟机所允许的个数,那么抛出
StackOverflowError
异常。 - 如果在创建栈帧的过程中,因为内存不足所失败了,则会抛出
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虚拟机内存及内存溢出异常相关推荐
- java 虚拟机内存类_java 虚拟机类加载 及内存结构
在面试java工程师的时候,这道题经常被问到,故需特别注意. 1.JVM 简介 JVM 是我们Javaer 的最基本功底了,刚开始学Java 的时候,一般都是从"Hello World &q ...
- java内存 海子_Java虚拟机:JVM内存模型和volatile详解
JVM内存模型和volatile详解 Java内存模型 随着计算机的CPU的飞速发展,CPU的运算能力已经远远超出了从主内存(运行内存)中读取的数据的能力,为了解决这个问题,CPU厂商设计出了CPU内 ...
- java虚拟机内存告警_java虚拟机内存溢出各种场景总结
java堆溢出 java堆用于存储对象实例,只要不断地创建对象,并且保证gc roots到对象之间有可达路径来避免垃圾回收机制来清楚这些对象,那么在 对象到达最大堆的容量限制后就会产生内存溢出溢出. ...
- java 虚拟机内存管理_java虚拟机内存管理
程序计数器: 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器. 程序计数器处于线程独占区 如果线程执行的是Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地 ...
- java虚拟机内存告警_Java虚拟机总结
JVM体系四大块: 类的加载 JVM内存结构 GC算法 垃圾回收 GC分析 性能调优 执行引擎 JVM架构图 jvm.arch 一.类的加载机制 类的加载 类的加载,将class文件读入方法区,然后在 ...
- 记录java对象修改过的字段_Java垃圾回收器与内存回收策略
Java中,内存由虚拟机管理,控制着回收什么,什么时候回收,怎么回收. 在栈中内存的随线程产生和分配,销毁而回收,在堆中,需要制定一系列策略来判断该回收哪些区域,以及何时回收. 可达性分析 主流的做法 ...
- java jvm内存模型_Java(JVM)内存模型– Java中的内存管理
java jvm内存模型 Understanding JVM Memory Model, Java Memory Management are very important if you want t ...
- java远程桌面图片压缩问题_java用Socket实现的远程桌面浏览 内存溢出问题
用socket做长连接时,出现了内存溢出的错误.搞了4天的时间总算是搞定了. 现总结下: 1.socket一般分为短连接和长连接. 长连接是一旦一个客户端登陆上服务器,其与服务器之间的连接就不关闭,不 ...
- java虚拟机内存监控_java虚拟机内存监控工具jps,jinfo,Jstack,jstat,jmap,jhat使用...
将会打印出很多jvm运行时参数信息,由于比较长这里不再打印出来,可以自己试试,内容一目了然 Jstack(Stack Trace for Java):JVM堆栈跟踪工具 jstack用于打印出给定的j ...
最新文章
- C++调用Python
- 解决ViewPager嵌套后子ViewPager不能滑动的方法
- 3d正方体旋转相册代码_3d旋转正方体的多种html和css制作方法和相关知识复习讲解
- 丰胸神器?网友曝椰树椰汁新广告低俗 疑似虚假宣传被调查...
- Numpy中的时间类型
- mysql创建用户navicat_14MYSQL创建用户和授权、15Navicat的使用、16-pymysql模块的使用、17-索引...
- Java分代垃圾回收机制:年轻代/年老代/持久代(转)
- wifi信号桥怎么设置_手机设置路由器WDS桥连方法
- 吞剑!喷火!这种江湖卖艺套路能吸引观众吗?
- 视频横竖屏模式切换,如何将多个视频任意转换
- 在Vmware14中安装Linux系统教程(图文教程)
- kindle出现电池感叹号,充电黄灯亮,怎么解决?按AWZ客服的回复弄好了。
- ZYNQ有两个CPU?(二)——OCM共享内存
- 泸州职业技术学院计算机单招试题,泸州职业技术学院普通类9+3单招考试技能测试考纲...
- -Dmaven.multiModuleProjectDirectory system propert
- 2022-2028年中国桌面云产业发展动态及市场需求预测报告
- 删除u盘插拔记录linux,电脑u盘插拔记录_电脑u盘插拔时间记录
- java csv 复杂表头_java读csv 和excel
- Python王牌加速库:奇异期权定价的利器!
- Python基于BeautifulSoup4库爬取亚马逊网页