JVM内存管理之JAVA语言的内存管理详解
作者:jingxian 发布时间:2021-11-01 12:00:10
引言
内存管理一直是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的结构图,由于运行时常量池是由方法区分配出来的区域,所以此图当中没有运行时常量池。
内存区域详解
针对上面这张图,内存就是指的矩形框当中运行期数据区这部分,下面简单介绍一下各个部分的作用:
1、PC寄存器(线程独有):全称是程序计数寄存器,它记载着每一个线程当前运行的JAVA方法的地址,如果是当前执行的是本地方法,则程序计数器会是一个空地址。它的作用就是用来支持多线程,线程的阻塞、恢复、挂起等一系列操作,直观的想象一下,要是没有记住每个线程当前运行的位置,又如何恢复呢。依据这一点,每一个线程都有一个PC寄存器,也就是说PC寄存器是线程独有的。
2、JAVA虚拟机栈(线程独有):JAVA虚拟机栈是在创建线程的同时创建的,用于存储栈帧,JAVA虚拟机栈也是线程独有的。
栈帧:简单点说,可以解释为是一个方法运行时,临时数据的存储区域,具体点说,它里面包括了数据和部分的过程结果,与此同时,它又肩负着处理方法返回值、动态链接以及异常分派的任务。栈帧是随着方法的创建而创建,随着方法的结束而销毁,如果方法抛出异常,也算方法结束。然而在每一个栈帧中,都有着自己的局部变量表以及操作数栈以及对当前类的运行时常量池的引用。
局部变量表:它是一个方法局部变量的列表,是在编译时期就写入了class文件当中。简单的理解,可以将它理解为一个对象数组,而里面按照索引0到length-1分别对应于每一个局部变量,特别的,如果是实例方法的局部变量表,第0个局部变量会是一个指向当前实例的引用,也就是this关键字,其余的局部变量则从索引1开始。
操作数栈:它是一个后进先出(pFO)栈,而它的长度也是在编译时期就写入了class文件当中,是固定的。它的作用就是提供字节码指令操作变量计算的空间,比如简单的,对于int a=9这句话来说,就需要先将9压入操作数栈,再将9赋给a这个变量。
3、JAVA堆(全局共享):这一部分是JAVA内存中最重要的一部分,之所以说是最重要的一部分,并不是因为它的重要性,而是指作为开发人员最应该关注的一部分。它随着JAVA虚拟机的启动创建,储存着所有对象实例以及数组对象,而且内置了“自动内存管理系统”,也就是我们常说的垃圾搜集器(GC)。JAVA堆中的内存释放是不受开发人员控制的,完全由JAVA虚拟机一手操办。对于JAVA虚拟机如何实现垃圾搜集器,JAVA虚拟机规范没有明确的规定,也正因如此,我们平时使用的JAVA虚拟机中提供了许多种垃圾搜集器,它们采用不同的算法以及实现方式,已满足多方面的性能需求。
4、方法区(全局共享):方法区也是堆的一个组成部分,它主要存储的是运行时常量池、字段信息、方法信息、构造方法与普通函数的字节码内容以及一些特殊方法。它与JAVA堆的区别除了存储的信息与JAVA堆不一样之外,最大的区别就是这一部分JAVA虚拟机规范不强制要求实现自动内存管理系统(GC)。
5、本地方法栈(线程独有):本地方法栈是一个传统的栈,它用来支持native方法的执行。如果JAVA虚拟机是使用的其它语言实现指令集解释器的时候,也会用到本地方法栈。如果前面这两种都未发生,也就是说如果JAVA虚拟机不依赖于本地方法栈,而且JAVA虚拟机也不支持native方法,则不需要本地方法栈。而如果需要的话,则本地方法栈也是随每一个线程的启动而创建的。
上面五个内存区域,除了PC寄存器之外,其余四个一般情况下,都要求JAVA虚拟机实现提供给客户调节大小的参数,也就是我们常用的Xms、Xmx等等。
内存管理
内存管理分为内存分配和内存释放,看一下上面的五个内存区域,其实可以大致分为两部分,一部分是全局共享,一部分是线程独有。
对于线程独有的这部分内存,都是随着线程的启动而创建,而当线程被销毁时,内存也就随之释放。这一部分内存,不需要垃圾搜集器的管理,而是JAVA虚拟机来主动管理,每当一个线程被创建的时候,JAVA虚拟机就会为其分配相应的PC寄存器和JAVA虚拟机栈,如果需要的话,还会有本地方法栈。相应的,当一个线程被销毁的时候,JAVA虚拟机也会将这个线程所占有的内存全部释放。
相对于线程独有的那部分内存,全局共享的这部分内存更加难以处理,不过这只是针对于虚拟机的实现来说,因为这一部分内存是要实现自动内存管理系统(GC)的。
全局共享的这部分内存(以下简称堆),内存分配主要是由程序员显示的使用new关键字来触发的,至于new出来的这部分内存在哪分配,如何分配,则是JAVA虚拟机来决定。而这部分内存的释放,则是由自动内存管理系统(以下简称GC)来管理的。
通常情况下,堆内存分配是要依赖于GC的策略与实现的,在分配的时候,就要考虑好到时候如何回收这部分内存。也是正因为如此,对于内存分配这一部分的讲解来说,我们必须得先了解内存是如何被回收的,才能更好的理解内存要怎么被分配。
结束语
本次对于JAVA语言中内存管理的概述就到此结束了。
来源:http://www.cnblogs.com/JavaBlackHole/archive/2017/08/17/7383821.html


猜你喜欢
- 前言Spring Boot中在yaml中编写的自定义变量、数组、对象等,在代码中读取该yaml配置文件中内容的三种方式。实现在代码中运用配置
- 工欲善其事,必先利其器很多程序员可能都忘了记录应用程序的行为是一件多么重要的事,当遇到多线程环境下高压力导致的并发bug时,你就能体会到记录
- 用注解实现Mybatis插入数据返回自增的主键Id我们在数据库表设计的时候,一般都会在表中设计一个自增的id作为表的主键。这个id也会关联到
- 冒泡排序:就是按索引逐次比较相邻的两个元素,如果大于/小于(取决于需要升序排还是降序排),则置换,否则不做改变这样一轮下来,比较了n-1次,
- 一、C#代码将html样式文件转为Word文档首先有个这样的需求,将以下网页内容下载为Word文件。html代码:<div class
- 桶排序桶排序是计数排序的升级,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过函数的某种映射关系,将待排序数组中
- 结论先行Kotlin协程中的Channel用于处理多个数据组合的流,随用随取,时刻准备着,就像自来水一样,打开开关就有水了。Channel使
- Filter过滤器和Listener * 详解Filter过滤器Filter的简介 对资源的访问进行过滤,相当于小区的保安,进去
- 本文实例为大家分享了Android自定义View画天气预报折线图的具体代码,供大家参考,具体内容如下效果图如下:刚开始尝试用第三方画曲线的框
- 不同的加锁顺序我们来看一个不同加锁顺序的例子:public class DiffLockOrder {private int amount;
- 前言在微服务的多样化世界中,HTTP是代理到代理通信中无可争议的领导者。它成熟,无处不在。但在某些情况下,HTTP请求-响应可能很麻烦。如果
- 这个问题来来回回困扰了我很久,一直没能妥善解决。场景1:华为手机遮挡了屏幕底部。场景2:进入应用时,虚拟键自动缩回,留下空白区域。需求:需要
- 本文实例为大家分享了C#实现拼手气红包算法的具体代码,供大家参考,具体内容如下一、方案1:即开即中,考虑机会均等,减少金额差较大的几率可以每
- 本文以一个非常简单的实例讲述了Winform实现抓取web页面内容的方法,代码简洁易懂,非常实用!分享给大家供大家参考。具体实现代码如下:W
- 本文实例讲述了Android编程实现实时监听EditText文本输入的方法。分享给大家供大家参考,具体如下:平时在做Android开发过程中
- 在练习Java的Scanner时,EditPlus如何读取从键盘输入的数呢?例如如下程序,编译通过,运行时却输入不了数据:package m
- 在为什么阿里巴巴不建议在for循环中使用”+”进行字符串拼接一文中,我们介绍了几种Java中字符串拼接的方式,以及优缺点。其中还有一个重要的
- 大多数情况下你不需要访问者模式,但当一旦需要访问者模式时,那就是真的需要它了,这是设计模式创始人的原话。可以看出应用场景比较少,但需要它的时
- 一、点名器需求:我有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器实现步骤:创建字符缓冲输入流对象创建Arr
- 先看看效果图:源码下载:Android Navigation TabBar控件实现多彩标签栏代码:MainActivity.javapack