技术

JVM系列-01-JVM内存模型

2016-12-18  本文已影响68人  hylexus

[TOC]

声明

本篇文章是本人阅读《深入理解JVM》和《java虚拟机规范》时的笔记。
JVM是HotSpot,jdk1.7。
大神绕路,不喜勿喷。

1 JVM内存模型

JVM内存模型

2 程序计数器(PC)

每个线程都会有自己私有的程序计数器(PC)。可以看作是当前线程所执行的字节码的行号指示器。
也可以理解为下一条将要执行的指令的地址或者行号。字节码解释器就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、 循环、 跳转、 异常处理、 线程上下文切换,线程恢复时,都要依赖PC.

JVM内存模型

说白了,PC就是一块内存区域。存放着下一条要执行的指令的地址。

3 虚拟机栈(VM Stack)

3.1 简介

VM Stack也是线程私有的区域。他是java方法执行时的字典:它里面记录了局部变量表、 操作数栈、 动态链接、 方法出口等信息。

在《java虚拟机规范》一书中对这部分的描述如下:

栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接 (Dynamic Linking)、 方法返回值和异常分派( Dispatch Exception)。
栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。
栈帧的存储空间分配在 Java 虚拟机栈( §2.5.5)之中,每一个栈帧都有自己的局部变量表( Local Variables, §2.6.1)、操作数栈( OperandStack, §2.6.2)和指向当前方法所属的类的运行时常量池( §2.5.5)的引用。

VM-Stack

说白了,VM Stack是一个,也是一块内存区域
所以,他是有大小的。虽然有大小,但是一般而言,各种虚拟机的实现都支持动态扩展这部分内存。

3.2 StackOverflowError

以下代码肯定会导致StackOverflowError:

public static void m1() {
    m1();
}

public static void main(String[] args) {
    m1();
}
Exception in thread "main" java.lang.StackOverflowError
    at xxx.xxx.xxx.m1(JavaVMStackSOF.java:10)

4 本地方法栈(Native Method Stack)

Java 虚拟机实现可能会使用到传统的栈(通常称之为“ C Stacks”)来支持 native 方法( 指使用 Java 以外的其他语言编写的方法)的执行,这个栈就是本地方法栈( Native MethodStack)。

VM Stack是为执行java方法服务的,此处的Native Method Stack是为执行本地方法服务的。
此处的本地方法指定是和具体的底层操作系统层面相关的接口调用了(这部分太高高级了,不想深究……)。

《java虚拟机规范》中没有对这部分做具体的规定。所以就由VM的实现者自由发挥了。
有的虚拟机(比如HotSpot)将VM Stack和Native Method Stack合二为一,所以VM的另一种内存区域图就如下面所示了:

JVM内存模型-虚拟机栈和本地方法栈合二为一

5 Java堆(Heap)

5.1 简介

在 Java 虚拟机中,堆( Heap)是可供各条线程共享的运行时内存区域,也是供所有类实例和数组对象分配内存的区域。

以下是本人对《java虚拟机规范》一书中对Java堆的介绍的总结:

JVM-Heap

至于堆内存的详细情况,将在后续的GC相关文章中介绍。

5.2 堆内存中的OutOfMemoryError

以下示例代码肯定导致堆内存溢出:

public static void main(String[] args) {
    ArrayList<Integer> list = Lists.newArrayList();
    while (true) {
        list.add(1);
    }
}

无限制的往list中添加元素,无论你的堆内存分配的多大,都会有溢出的时候。

java.lang.OutOfMemoryError: Java heap space

6 方法区(Method Area)

方法区是由所有线程共享的内存区域。
方法区存储的大致内容如下:

以下是本人对《java虚拟机规范》一书中对方法区的介绍的总结:

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

《java虚拟机规范》中对常量池的介绍:

运行时常量池( Runtime Constant Pool)是每一个类或接口的常量池( Constant_Pool,§4.4)的运行时表示形式,它包括了若干种不同的常量:从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用。
运行时常量池扮演了类似传统语言中符号表( SymbolTable)的角色,不过它存储数据范围比通常意义上的符号表要更为广泛。

运行时常量池

每一个运行时常量池都分配在 Java 虚拟机的方法区之中,在类和接口被加载到虚拟机后,对应的运行时常量池就被创建出来。

7 直接内存(Direct Memory)

此处的直接内存并不是由JVM管理的内存。他是利用本地方法库直接在java堆之外申请的内存区域。
比如NIO中的DirectByteBuffer就是操作直接内存的。

直接内存的好处就是避免了在java堆和native堆直接同步数据的步骤。但是他并不是由JVM来管理的。

当然,这部分内存区域的操作也可能会抛出OutOfMemoryError

参考文章

上一篇下一篇

猜你喜欢

热点阅读