文章目录
Java内存区域
如果你使用过C++,一定体会过需要为每一个new操作去写配对的delete/free代码的繁琐。
使用Java的时候,你发现这些事情(内存管理)都不用自己做了(JVM代劳),有些开心,但又觉得有些不放心,因为内存管理不能大意,稍有"管理不善",就很容易使程序导致内存泄露和内存溢出(什么是内存泄露和内存溢出?)。
所以很有必要去了解JVM怎么去管理内存的,而这就得先从Java内存区域开始说起。
1 运行数据区域
JVM(Java Virtual Machine),即Java虚拟机在执行Java程序时会把它管理的内存(即运行数据区)划分为几个不同的数据区。图例在最后。主要包括有:
1.1 程序计数器(Program Counter Register)
当前线程所执行的字节码行号指示器。在虚拟机概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令(如果你学过汇编语言,对这个过程应该会容易理解)。
每一条线程都需要有一个独立的程序计数器,以为了线程切换后能恢复到正确的执行位置。所以程序计数器这小块内存区域所是线程私有的。
如果执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果是Native方法,则为空(Undefined)。此内存区域是唯一一个在JVM规范中没有规定任何OutOfMemoryError情况的区域。
1.2 虚拟机栈(VM Stack)
每个方法执行时,都会创建一个栈帧,栈帧中用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法调用与执行完成,都对应栈帧在VM栈中的入栈与出栈。
VM栈也是线程私有的,生命周期与线程相同。
通常我们喜欢把内存划分为"堆"内存与“栈”内存, 可以粗略的把VM栈(Java虚拟机栈)中局部变量表(存放了编译期可知的基本数据类型、对象引用和returnAddress类型)的内存区域理解为我们常说的“栈”内存。
此内存区域会抛出两个异常:
- StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度。
- OutOfMemoryError:内存扩展无法申请到足够的内存。
1.3 本地方法栈(Native Method Stack)
与虚拟机栈作用相似,为虚拟机执行的Native方法服务。同样会抛出:
- StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度。
- OutOfMemoryError:内存扩展无法申请到足够的内存。
1.4 Java堆(Java Heap)
Java堆是JVM内存区域中最大的一块,用于存放对象实例,所以也是GC(Garbage Collected垃圾回收器)管理的主要区域,它随JVM启动创建,被所有线程共享,是线程公有的。
内存大小可扩展(可通过-Xmx和-Xms参数修改,分别代表是初始堆大小和最大堆内存大小)。
当堆内存大小无法扩展时,会抛出:
- OutOfMemoryError:内存扩展无法申请到足够的内存。
1.5 方法区(Method Area)
主要用于存储已被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。所以GC很少在这部分内存区域工作。
当方法区无法满足内存分配需求时,将抛出:
- OutOfMemoryError:内存扩展无法申请到足够的内存。
2 图例
画个图加深一下印象。
(以上内容均整理自《深入理解Java虚拟机:JVM高级特性与最佳实践 第二版》