Java程序员一般不需要太关注内存,因为操作内存的权力都交给了Java虚拟机,但是Java程序员必须需要了解JVM是如何使用内存的,否则一旦内存出现泄漏或事溢出的话,就会一筹莫展不知道从哪去入手排查问题。

一、JVM内存模型

JVM在运行时会把它管理的内存划分成若干个不同区域,每个区域有各自不同的用处,以及不同的创建和销毁的时间,有的随着JVM进程启动而存在,而有的需要随着用户线程的启动和结束而创建和销毁,主要内存区域划分如下图示:

主要分成两大类,线程共享的区域和线程独享的区域。

线程共享区域

1.堆

堆内存是JVM管理的最大的内存区域,堆内存被所有线程共享,随着虚拟机的启动而创建,存放的全部是对象实例,几乎所有的对象实例都是在堆内存中被分配内存。随着JIT编译器的发展和逃逸分析技术的逐渐成熟,栈上分配、标量替换优化技术会导致对象不一定会在堆中分配内存了。

堆内存是垃圾回收主要的区域,而主流的垃圾回收都是采用的分代收集算法,所以堆内存也常常会被分为新生代和老年代,新生代又可以被分成Eden区、From Survivor区和To Survivor区。堆内存虽然可以划分成很多区,但仅仅是逻辑上的区分,实际存储的都是对象实例。而且堆内存可以在物理内存空间上不连续,只需要逻辑上连续即可。一般JVM优化主要也是针对堆内存来进行优化。而堆内存的扩展主要是通过-Xms和-Xmx参赛来进行配置,当堆内存不足以再创建新对象时就会抛出OutOfMemoryError异常

2.方法区

方法区和堆内存一样也是所有线程共享的,主要存储已经被虚拟机加载的类信息、静态变量、常量、即时编译器编译后的代码等信息。由于方法区存储的一般都是不变的数据,所以垃圾回收很少会在这个区域进行。方法区的垃圾回收主要是针对常量池和对类型的卸载。这个区域如何内存不足也会抛出OutOfMemoryError异常。

线程独享区域

1.虚拟机栈

虚拟机栈属于线程私有,随着线程的创建而创建,随着线程的销毁而销毁。而每执行一个Java方法,都会在虚拟机栈中创建一个栈帧 (Stack Frame),栈帧中又包含局部变量表、操作栈、动态链接和方法出口等信息。每一个Java方法被调用直到方法执行完成的过程,对应着一个栈帧在虚拟机栈中的入栈到出栈道过程。一般有一种说法是Java内存分为堆内存和栈内存,这种是比较笼统的,这里的栈内存实际指的仅仅是虚拟机栈中的一个栈帧的局部变量表区域。局部变量表中存储八种基本数据类型、对象引用(不同的虚拟机可能是指向对象起始地址的引用指针,也可能是指向一个对象的句柄或与对象相关的位置)

虚拟机栈内存区域每执行一个方法,有创建一个栈帧压栈,执行完后出栈,栈的特点是先进后出,后进先出,如果线程请求的栈深度大于虚拟机允许的深度,会抛StackOverFlowError(栈溢出);而如果虚拟机栈内存不足且扩展时也无法申请到足够的内存的话,则会抛OutOfMemoryError(内存溢出)

1.1、局部变量表:存储方法局部变量

1.2、操作数栈:出入栈进行操作数的计算

1.3、动态连接:动态寻址的过程,比如方法定义UserService userService; 那么实际运行的时候需要找到UserService接口具体是由哪个对象实现的

1.4、顾名思义就是方法的出口

下面以简单例子查看虚拟机栈的工作方式:

  public static Integer num = 10;

     public int add(int i){
int j = 5;
int k = i+j;
j++;
k = num + j;
return k;
}

定义一个简单的add方法,定义一个静态变量,代码编译之后结果如下:

  public int add(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=2
0: iconst_5
1: istore_2
2: iload_1
3: iload_2
4: iadd
5: istore_3
6: iinc 2, 1
9: getstatic #2 // Field num:Ljava/lang/Integer;
12: invokevirtual #3 // Method java/lang/Integer.intValue:()I
15: iload_2
16: iadd
17: istore_3
18: iload_3
19: ireturn
LineNumberTable:
line 7: 0
line 8: 2
line 9: 6
line 10: 9
line 11: 18 

(具体每一步的含义及局部变量表和操作数栈的变化情况在《JVM探秘6--图解虚拟机栈的局部变量表和操作数栈工作流程》详细图解)

2.程序计数器

程序计数器占有的内存较小,主要存储当前线程执行的字节码的行号,字节码执行器工作时需要根据程序计数器来选取下一条需要执行的字节码指令,由于JVM的多线程数通过线程轮流切换被分配CPU执行的,一个CPU同一时间只能处理一个线程的一个指令,为了在线程切换后能够恢复到正确的指令位置,就需要每个线程都有一个独立的程序计数器,各个线程之间的程序计数器互不影响。如果线程正在执行JAVA方法,则计数器记录正在执行的字节码指令的地址,如果正在执行的事本地方法,计数器值则为空。程序计数器由于存储的值只有字节码地址,没有扩展的需要,所以这个区域是不会出现OutOfMemoryError情况的。

3.本地方法栈

本地方法栈和虚拟机栈类似,不同的是虚拟机栈是为Java方法服务,而本地方法栈是为本地方法服务,而本地方法使用的语言不同的虚拟机是不一样的,也有的虚拟机会将本地方法栈和虚拟机栈合二为一。本地方法栈和虚拟机栈一样也会抛StackOverFlowError和OutOfMemoryError。

除了上面的JVM运行时内存划分,还有一类内存叫做直接内存,这类内存是不受JVM管控的,但是也会出现OutOfMemoryError异常。JDK1.4加入了NIO,引入了通道和缓冲区的I/O方式,可以使用本地方法直接分配堆外内存,然后通过一个存储在Java堆里的DirectByteBuffer对象作为这块内存的引用进行操作,可以避免java堆和native堆中来回复制数据而提高性能。直接内存不受JVM控制,但是会受到机器总内存的影响,如果JVM内存分配过大加上其他的内存大于了服务器的总内存,就会导致直接内存无法分配,同样也会出现OutOfMemoryError异常。

JVM探秘1--JVM内存运行时区域划分的更多相关文章

  1. JVM知识总结-运行时区域划分

    区域简介 JVM运行时区域有些随着虚拟机进程的启动而存在,有些依赖于用户线程的启动和结束而建立和销毁,大致分为以下几类:方法区,虚拟机栈,本地方法栈,堆,程序计数器,概念图如下(源于<深入理解J ...

  2. JVM学习笔记:Java运行时数据区域

    JVM执行Java程序的过程中,会使用到各种数据区域,这些区域有各自的用途.创建和销毁时间.根据<Java虚拟机规范>,JVM包括下列几个运行时数据区域,如下图所示: 其中红色部分是线程私 ...

  3. JVM运行时区域详解。

    我们知道的JVM内存区域有:堆和栈,这是一种泛的分法,也是按运行时区域的一种分法,堆是所有线程共享的一块区域,而栈是线程隔离的,每个线程互不共享. 线程不共享区域 每个线程的数据区域包括程序计数器.虚 ...

  4. JVM笔记【1】-- 运行时数据区

    目录 (一)java内存区域管理 1.1 程序计数器 1.2 虚拟机栈 1.3 本地方法栈 1.4 java堆 1.5 方法区 1.5.1 运行时常量池 (二)直接内存 (一)java内存区域管理 C ...

  5. Java 内存管理、JVM 工作原理与 Java 运行时系统

    Java 虚拟机规范中说明:所有的对象实例(all class instances)以及数组都要在堆上分配: the heap is the runtime data area from which ...

  6. JVM探秘:Java内存区域

    本系列笔记主要基于<深入理解Java虚拟机:JVM高级特性与最佳实践 第2版>,是这本书的读书笔记. 概述 Java 虚拟机为程序员分担了很多内存管理的工作,不再像 C/C++ 那样容易出 ...

  7. JVM 专题十二:运行时数据区(七)对象的实例化内存布局与访问定位

    1. 对象的实例化 1.1 创建对象的方式 new 最常见的方式 变形1 : Xxx的静态方法 变形2 : XxBuilder/XxoxFactory的静态方法 Class的newInstance() ...

  8. JVM之基础概念(运行时数据区域、TLAB、逃逸分析、分层编译)

    运行时数据区域 JDK8 之前的内存布局 JDK8 之后的 JVM 内存布局 JDK8 之前,Hotspot 中方法区的实现是永久代(Perm),JDK8 开始使用元空间(Metaspace),以前永 ...

  9. 深入浅出JVM(一):运行时数据区域

    程序计数器 线程私有 指向了正在执行的虚拟机字节码指令的地址:如果是本地方法,数值为空 没有 OutOfMemoryError 错误的区域 Java虚拟机栈 线程私有: 生命周期与线程相同: 代表着 ...

随机推荐

  1. .net framework 项目 build 出现 未能加载文件或程序集“netfx.force.conflicts”或它的某一个依赖项

    问题描述 Severity Code Description Project File Line Suppression State Error 未能加载文件或程序集"netfx.force ...

  2. python设计模式之猴子补丁模式

    1.所有书中都没有把猴子补丁作为一种设计模式来看待.因为设计模式的模式的命名是根据java中提炼出来的,语言方式决定了java绝对不会有也不需要有这种操作,不存在的.那自然设计模式不会包括猴子补丁模式 ...

  3. 关于npm的坑

    http://javascript.ruanyifeng.com/nodejs/packagejson.html 一.npm常用易忘指令 npm list --depth=0:查看所有高级的npm m ...

  4. deep learning 以及deep learning 常用模型和方法

    首先为什么会有Deep learning,我们得到一个结论就是Deep learning需要多层来获得更抽象的特征表达. 1.Deep learning与Neural Network 深度学习是机器学 ...

  5. 【奇淫技巧】API接口字段table文档转代码工具

    今天做一个视频接口对接,发现对方提供的文档没有json格式,无法自动生成请求和响应对象 json自动生成C#类的工具 http://tool.sufeinet.com/Creater/JsonClas ...

  6. VBA 全局常量定义

    Public Const INDEX As Integer = 16 Public Const RESULT_SHEET As String = "result" Public C ...

  7. react+dva+antd项目构建

    一:版本升级 首先说明一下这里的dva是最新版: 2.3.2 1.初始化项目构建 npm install dva-cli -g dva -v dva-cli version 注:通过 npm 安装 d ...

  8. 已知两点的经度和纬度,计算两点间的距离(php,javascript)

    php代码:转载 http://www.cnblogs.com/caichenghui/p/5977431.html /** * 求两个已知经纬度之间的距离,单位为米 * * @param lng1 ...

  9. centOS 安装gitlab-runner

    1. curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sud ...

  10. RFID世界网

    RFID世界网 地址:http://www.rfidworld.com.cn/NFC/