--------------------------------【Java虚拟机】系列--------------------------------
1、Java技术体系
2、Java内存区域
3、虚拟机对象
4、OutOfMemoryError异常
--------------------------------【Java虚拟机】系列--------------------------------
Part II: 自动内存管理机制
第2章 Java内存区域与内存溢出异常
概述
Java程序中,虚拟机有自动内存管理机制,不再需要为每个new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出问题。但是一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会成为一次异常艰难的工作。
运行时数据区域
上图:JVM运行时数据区,引自《深入理解Java虚拟机》读书笔记–vernlium
程序计数器(Program Counter Register)
- 程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器;
- 每条线程都有一个独立的程序计数器,各条线程之间计数器互不影响,称之为“线程私有”的内存;
- 如果线程执行的是Java方法,该计数器记录的是正在执行的虚拟机字节码指令的地址;如果是Native方法则为空;
- 此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域;
Java虚拟机栈(Java Virtual Machine Stack)
)
上图引用自:《Java虚拟机原理图解》3、JVM运行时数据区
- Java虚拟机栈是
线程私有
,它的生命周期与线程相同; - Java虚拟机栈描述的是Java方法执行的内存模型:每个方法在在执行的同时都会创建一个栈帧(Stack Frame)用于存储
局部变量表
、操作数栈
、动态链接
、方法接口
等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈出栈的过程; - 局部变量表存放了编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用和returnAddress类型;其所需的内存空间在编辑期完成分配,不会在运行期改变;
- 可能存在两种异常:StackOverflowError和OutOfMemoryError;
- -Xoss参数设置本地方法栈大小(对于HotSpot无效)
- -Xss参数设置栈容量 例: -Xss128k
本地方法栈(Native Method Stack)
- 与虚拟机栈所发挥的作用非常相似,区别是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务;
- 可能存在两种异常:StackOverflowError和OutOfMemoryError;
Java堆(Java Heap)
- Java堆是被所有
线程共享
的,在虚拟机启动时创建; - 此内存区域的唯一目的就是
存放对象实例
,几乎所有的对象实例都在这分配; - 是垃圾收集器管理的主要区域,可以分为新生代和老年代;
- 可以物理不连续,只要逻辑上是连续的即可;
- 如果堆中没有内存完成实例分配也无法再扩展时,会抛出OutOfMemoryError异常;
- -Xms参数初始分配的堆内存,默认是物理内存的1/64;
- -Xmx参数最大允许分配的堆内存;
- -XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出是dump出当前的内存堆转储快照。
- 例:VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError,若-Xms=-Xmx,则可避免堆自动扩展;
方法区(Method Area)
上图引用自:《Java虚拟机原理图解》3、JVM运行时数据区
- 是各个
线程共享
的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据; - 永久代:JavaGC的分代收集机制分为3个代:年青代,老年代,永久代,将方法区定义为“永久代”,这是因为,对于之前的HotSpot Java虚拟机的实现方式中,将分代收集的思想扩展到了方法区,并将方法区设计成了永久代。不过,除HotSpot之外的多数虚拟机,并不将方法区当做永久代,随着Java8的到来,已放弃永久代改为采用Native Memory来实现方法区的规划。
- 当无法满足内存分配需求时,将抛出OutOfMemoryError异常;
- -XX:MaxPermSize设置上限;
- -XX:PermSize设置最小值;
- 例:VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M;
运行时常量池(Runtime Constant Pool)
- 是方法区的一部分;
- Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,在部分内容将在类加载后进入方法区的运行时常量池中存放;
- 运行时常量池相对于Class文件常量池,具有动态性,运行期间也可以将新的常量放入常量池,比如String类的intern()方法;
- 当运行时常量池无法申请到更多的内存时,将会抛出OutOfMemoryError异常;
直接内存(Direct Memory)
- 并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域;
- JDK 1.4的NIO引入了基于通道(Channel)和缓冲区(Buffer)的IO方法,可以使用Native函数库直接分配对外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作以提升性能;
- 设置Xmx等参数信息时注意不能忽略直接内存,不然会引起OutOfMemoryError异常;
- -XX:MaxDirectMemorySize设置最大值,默认与java堆最大值一样;
- 例:-XX:MaxDirectMemorySize=10M -Xmx20M