JVM 内存管理之 JAVA 语言的内存管理详解
这里有新鲜出炉的 Java 函数式编程, 程序狗速度看过来!
Java 程序设计语言
java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言, 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台 (即 JavaEE(j2ee), JavaME(j2me), JavaSE(j2se)) 的总称
下面小编就为大家带来一篇 JVM 内存管理之 JAVA 语言的内存管理详解小编觉得挺不错的, 现在就分享给大家, 也给大家做个参考一起跟随小编过来看看吧
引言
内存管理一直是 JAVA 语言自豪与骄傲的资本, 它让 JAVA 程序员基本上可以彻底忽略与内存管理相关的细节, 只专注于业务逻辑不过世界上不存在十全十美的好事, 在带来了便利的同时, 也因此引入了很多令人抓狂的内存溢出和泄露的问题
可怕的事情还不只如此, 有些使用其它语言开发的程序员, 给 JAVA 程序员扣上了一个不懂内存的帽子, 这着实有点让人难以接受毕竟 JAVA 当中没有 malloc 和 delete 没有析构函数没有指针, 刚开始接触 JAVA 的程序员们又怎么可能接触内存这一部分呢, 更何况有不少 JAVA 程序员还是跳了专业半路出家的朋友
不过事实尽管难以接受, 但也确实有不少 JAVA 程序员对内存这部分可谓一窍不知, 尽管掌握内存的相关知识, 或许并不能给平时的开发带来翻天覆地的变化和好处, 不过它仍然会潜移默化的提高你的技术水准, 这一点在了解完内存管理之后, 相信各位就会深有体会了
内存划分
谈到内存这一词汇, 它是在程序运行时才有的数据存储区域, 而对于这一块区域的划分, 各个虚拟机有各自的划分方式, 不过它们都必须遵从 JAVA 虚拟机的基本规范去实现
虚拟机规范中, 将内存划分为六大部分, 分别是 PC 寄存器 JAVA 虚拟机栈 JAVA 堆方法区运行时常量池以及本地方法栈
JAVA 虚拟机规范与 JAVA 虚拟机
这里还需要解释一下 JAVA 虚拟机规范和 JAVA 虚拟机的区别, 顾名思义, JAVA 虚拟机规范是一种对 JAVA 虚拟机实现的规范要求, 是由 oracle 制定的, 而我们平时常说的 JAVA 虚拟机一般是指的一种具体的 JAVA 虚拟机规范的实现比如我们最经常使用的 JAVA 虚拟机 hotspot, 其实 JAVA 虚拟机还有很多种实现, 甚至如果你对 JAVA 虚拟机规范有了深入的了解而且对此有兴趣的话, 可以写一个自己的 JAVA 虚拟机, 当然这其中的难度不难想象
结构图
下图是引用于百度文库的一张 JVM 的结构图, 由于运行时常量池是由方法区分配出来的区域, 所以此图当中没有运行时常量池
内存区域详解
针对上面这张图, 内存就是指的矩形框当中运行期数据区这部分, 下面简单介绍一下各个部分的作用:
1PC 寄存器 (线程独有): 全称是程序计数寄存器, 它记载着每一个线程当前运行的 JAVA 方法的地址, 如果是当前执行的是本地方法, 则程序计数器会是一个空地址它的作用就是用来支持多线程, 线程的阻塞恢复挂起等一系列操作, 直观的想象一下, 要是没有记住每个线程当前运行的位置, 又如何恢复呢依据这一点, 每一个线程都有一个 PC 寄存器, 也就是说 PC 寄存器是线程独有的
2JAVA 虚拟机栈 (线程独有):JAVA 虚拟机栈是在创建线程的同时创建的, 用于存储栈帧, JAVA 虚拟机栈也是线程独有的
栈帧: 简单点说, 可以解释为是一个方法运行时, 临时数据的存储区域, 具体点说, 它里面包括了数据和部分的过程结果, 与此同时, 它又肩负着处理方法返回值动态链接以及异常分派的任务栈帧是随着方法的创建而创建, 随着方法的结束而销毁, 如果方法抛出异常, 也算方法结束然而在每一个栈帧中, 都有着自己的局部变量表以及操作数栈以及对当前类的运行时常量池的引用
局部变量表: 它是一个方法局部变量的列表, 是在编译时期就写入了 class 文件当中简单的理解, 可以将它理解为一个对象数组, 而里面按照索引 0 到 length-1 分别对应于每一个局部变量, 特别的, 如果是实例方法的局部变量表, 第 0 个局部变量会是一个指向当前实例的引用, 也就是 this 关键字, 其余的局部变量则从索引 1 开始
操作数栈: 它是一个后进先出 (pFO) 栈, 而它的长度也是在编译时期就写入了 class 文件当中, 是固定的它的作用就是提供字节码指令操作变量计算的空间, 比如简单的, 对于 int a=9 这句话来说, 就需要先将 9 压入操作数栈, 再将 9 赋给 a 这个变量
3JAVA 堆 (全局共享): 这一部分是 JAVA 内存中最重要的一部分, 之所以说是最重要的一部分, 并不是因为它的重要性, 而是指作为开发人员最应该关注的一部分它随着 JAVA 虚拟机的启动创建, 储存着所有对象实例以及数组对象, 而且内置了自动内存管理系统, 也就是我们常说的垃圾搜集器 (GC)JAVA 堆中的内存释放是不受开发人员控制的, 完全由 JAVA 虚拟机一手操办对于 JAVA 虚拟机如何实现垃圾搜集器, JAVA 虚拟机规范没有明确的规定, 也正因如此, 我们平时使用的 JAVA 虚拟机中提供了许多种垃圾搜集器, 它们采用不同的算法以及实现方式, 已满足多方面的性能需求
4 方法区 (全局共享): 方法区也是堆的一个组成部分, 它主要存储的是运行时常量池字段信息方法信息构造方法与普通函数的字节码内容以及一些特殊方法它与 JAVA 堆的区别除了存储的信息与 JAVA 堆不一样之外, 最大的区别就是这一部分 JAVA 虚拟机规范不强制要求实现自动内存管理系统 (GC)
5 本地方法栈 (线程独有): 本地方法栈是一个传统的栈, 它用来支持 native 方法的执行如果 JAVA 虚拟机是使用的其它语言实现指令集解释器的时候, 也会用到本地方法栈如果前面这两种都未发生, 也就是说如果 JAVA 虚拟机不依赖于本地方法栈, 而且 JAVA 虚拟机也不支持 native 方法, 则不需要本地方法栈而如果需要的话, 则本地方法栈也是随每一个线程的启动而创建的
上面五个内存区域, 除了 PC 寄存器之外, 其余四个一般情况下, 都要求 JAVA 虚拟机实现提供给客户调节大小的参数, 也就是我们常用的 XmsXmx 等等
内存管理
内存管理分为内存分配和内存释放, 看一下上面的五个内存区域, 其实可以大致分为两部分, 一部分是全局共享, 一部分是线程独有
对于线程独有的这部分内存, 都是随着线程的启动而创建, 而当线程被销毁时, 内存也就随之释放这一部分内存, 不需要垃圾搜集器的管理, 而是 JAVA 虚拟机来主动管理, 每当一个线程被创建的时候, JAVA 虚拟机就会为其分配相应的 PC 寄存器和 JAVA 虚拟机栈, 如果需要的话, 还会有本地方法栈相应的, 当一个线程被销毁的时候, JAVA 虚拟机也会将这个线程所占有的内存全部释放
相对于线程独有的那部分内存, 全局共享的这部分内存更加难以处理, 不过这只是针对于虚拟机的实现来说, 因为这一部分内存是要实现自动内存管理系统 (GC) 的
全局共享的这部分内存 (以下简称堆), 内存分配主要是由程序员显示的使用 new 关键字来触发的, 至于 new 出来的这部分内存在哪分配, 如何分配, 则是 JAVA 虚拟机来决定而这部分内存的释放, 则是由自动内存管理系统 (以下简称 GC) 来管理的
通常情况下, 堆内存分配是要依赖于 GC 的策略与实现的, 在分配的时候, 就要考虑好到时候如何回收这部分内存也是正因为如此, 对于内存分配这一部分的讲解来说, 我们必须得先了解内存是如何被回收的, 才能更好的理解内存要怎么被分配
结束语
本次对于 JAVA 语言中内存管理的概述就到此结束了
以上这篇 JVM 内存管理之 JAVA 语言的内存管理详解就是小编分享给大家的全部内容了, 希望能给大家一个参考, 也希望大家多多支持 PHPERZ
来源: http://www.phperz.com/article/18/0119/362797.html