一、Java内存区域

1、概述

对于java程序员来说,在虚拟机的自动内存管理机制的帮助下,不需要为每一个new操作去写delete/free代码,而且不容易出现内存泄漏和内存溢出问题。但是把内存控制的权利交给虚拟机管理,一旦出现内存泄漏和溢出的问题,不了解虚拟机是怎样使用内存的,那排查错误将会成为一项异常艰难的工作。

2、运行时数据区域

2.1、程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,他的作用可以看做事当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基本功能都需要依赖这个计数器来完成。

由于java虚拟机的多线程是通过线程轮流切换并分配处理执行时间的方式实现的,所以每条线程都需要有一个独立的程序计数器,因此程序计数器是线程私有的内存区域。

如果线程正在执行的是一个java方法,则计数器记录的是正在执行的虚拟机字节码执行的地址,如果正在执行的是Native方法,则计数器为空(Undefined)。

2.2、Java虚拟机栈

与程序计数器一样,java虚拟机栈也是线程私有的,他的生命周期于线程相同。虚拟机栈主要描述的java方法执行的内存模型,每个方法被执行的时候会同时创建一个栈帧用于存储局部表量表、操作栈、动态链接、方法出口等信息。方法被调用时就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

1、局部表量表:是存放了编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型)和returnAddress类型。

注意:其中64位长度的long和double数据类型会占用2个局部变量空间,其余的数据类型只占用1个。

a、reference类型:他不等于对象本身,根据不同的虚拟机实现,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置。

b、returnAddress类型:指向了一条字节码指令的地址。

2.3、本地方法栈

本地方法栈和虚拟机栈的作用是非常相似的,区别不过是虚拟机执行java方法服务,也就是字节码服务,而本地方法栈是为虚拟机使用的Native方法服务。

2.4、java堆

java堆是java虚拟机所管理的内存中最大的一块。是被所有线程共享的内存区域,在虚拟机启动时创建。存放的是对象实例,几乎所有的对象实例都在这里分配内存。

java堆是垃圾收集器管理的主要区域,因此很多时候也称为“GC堆”。

java堆细分为:新生代和老年代,再细致一点有Eden空间、From Survivor空间、To Survivor空间。

2.5、方法区

方法区与java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

2.6、运行时常量池

是方法去的一部分。Class文件中除了有类的版本、字段、方法、接口等信息外,还有一项信息就是常量池,用于存放编译期生成的各种字面量和符号引用,这些内容都会放在类加载后存放到方法区的运行时常量池中。

注意:运行时常量池相对于Class文件常量池的另一个重要特征就是具备动态性,java语言并不要求常量一定只能在编译期产生,也就是并非预置入Class文件中的常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中。

2.7、直接内存

直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机规则中定义的内存区域,但是这部分内存也是被频繁使用的。

3、对象访问

在java语言中,对象访问是如何进行的?对象访问也会涉及java栈、java堆、方法区这三个重要的内存区域。列如:

Object object = new Object();

假如这行代码出现在方法体中,“Object object”这部分的语义会反映到java栈的本地变量表中,作为一个reference类型数据出现。而“new Object()”的语义会反映到java堆中,形成一块存储了object类型所有实例数据值的结构化内存,根据具体类型以及虚拟机实现的对象内存布局的不同,这块内存的长度是不固定的。

对象访问的两种方式:句柄和直接指针。

使用句柄方式:Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息。

使用直接指针方式:java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,reference中直接存储的就是对象地址。

二、内存溢出异常

1、Java堆溢出

java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量到达最大堆的容量限制后产生内存溢出异常。

2、虚拟机栈和本地方法栈溢出

在HotSpot虚拟机中并不是区分虚拟机栈和本地方法栈,因此对于HotSpot来说,-Xoss参数虽然存在,但实际上是无效的,栈容量只有-Xss参数设定。在java虚拟机规范中描述了两种异常:

a、如果线程请求的长深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常;

b、如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

注:在单线程下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是StackOverflowError异常。

3、运行时常量池溢出

要向运行时常量池中添加内容,最简单的做法是使用String.intern()这个Native方法。作用:如果池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加但常量池中,并且返回此String对象的应用。由于常量池分配在方法区内,可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而限制其中常量池的容量。

4、方法区溢出

方法区用于存放Class的相关信息,方法区溢出一般是运行时产生大量的类去填满方法区,就会出现溢出。

方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾回收器回收掉,判断条件是非常苛刻的。即使是同一个类文件,被不同的加载器加载时也会视为不同的类。

5、本机直接内存溢出

DirectMemory容量可通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与java堆的最大值(-Xmx)一样。列如:使用DirectByteBuffer分配内存也会抛出内存溢出异常,但他抛出异常时并没有真正的向操作系统申请分配内存,而是通过计算得知内存无法分配,于是手动抛出异常,真正申请分配内存的方法是unsafe.allocateMemory()。

Java虚拟机——内存区域及内存溢出异常的更多相关文章

  1. 《深入理解Java虚拟机》Java内存区域与内存溢出异常

    注:“蓝色加粗字体”为书本原语 先来一张JVM运行时数据区域图,再接下来一一分析各区域功能:   程序计数器 程序计数器(program Counter Register)是一块较小的内存空间,它可以 ...

  2. 深入理解java虚拟机系列(一):java内存区域与内存溢出异常

    文章主要是阅读<深入理解java虚拟机:JVM高级特性与最佳实践>第二章:Java内存区域与内存溢出异常 的一些笔记以及概括. 好了開始.假设有什么错误或者遗漏,欢迎指出. 一.概述 先上 ...

  3. 《深入理解Java虚拟机》-----第2章 Java内存区域与内存溢出异常

    2.1 概述 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又是执行最基础工作的劳动人民——拥有每一个对象的“所有权”,又担负着每一个对象生命开始到终结的维护责任 ...

  4. 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常

    第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域  

  5. 深入了解Java虚拟机(1)java内存区域与内存溢出异常

    java内存区域与内存溢出异常 一.运行时数据区域 1.程序计数器:线程私有,用于存储当前所执行的指令位置 2.Java虚拟机栈:线程私有,描叙Java方法执行模型:执行方法时都会创建一个栈帧,存储局 ...

  6. 《深入理解Java虚拟机》笔记--第二章、Java内存区域与内存溢出异常

    Java程序员把内存的控制权交给了Java虚拟机.在Java虚拟机内存管理机制的帮助下,程序员不再需要为每一个new操作写对应的delete/free代码,而且不容易出现内存泄露和溢出. 虚拟机在执行 ...

  7. 深入理解java虚拟机---->java内存区域与内存溢出异常

    2. java内存区域于内存溢出异常 2.1 概述: 对于C/C++而言,内存管理具有最高的权利,既拥有每一个对象的“所有权”,又担负着每一个对象生命开始到结束的维护责任. 对于java而言,则把内存 ...

  8. 《深入理解 Java 虚拟机》读书笔记:Java 内存区域与内存溢出异常

    前言 最近开始看这本书,记得前段时间拿起这本书的时候,心情是相当沉重的!当时的剧本是这样的-- 内景.家里 - 下午 我(画外):唉,有点无聊啊!(偶然撇过书架)这么多书得看到什么时候啊,要不要拿一本 ...

  9. [深入理解JVM虚拟机]第2章-Java内存区域与内存溢出异常

    2.0引-Java内存区域中,栈内存和堆内存分别装什么,为什么? 栈:解决程序的运行问题,即程序如何执行,或者说如何处理数据. 堆:解决的是数据存储的问题,即数据怎么放,放在哪儿. 参考链接https ...

  10. 虚拟机--第二章java内存区域与内存溢出异常--(抄书)

    这是本人阅读周志明老师的<深入理解Java虚拟机>第二版抄写的,有很多省略,不适合直接阅读,需要阅读请出门左转淘宝,右转京东,支持周老师(侵权请联系删除) 第二章java内存区域与内存溢出 ...

随机推荐

  1. SpringBoot整合EasyExcel

    1.Excel导入导出的应用场景 在做项目中很多时候都会用到Excel的导入和导出 2.解决方案 POI:操作比较繁琐 EasyExcel:正如其名,'Easy'Excel相对于POI使用起来还是比较 ...

  2. 如何使用 Helm 在 K8s 上集成 Prometheus 和 Grafana|Part 1

    本系列将分成三个部分,您将学习如何使用 Helm 在 Kubernetes 上集成 Prometheus 和 Grafana,以及如何在 Grafana 上创建一个简单的控制面板.Prometheus ...

  3. 有了Excel和PPT,为什么我们还需要数据可视化工具?

    在当今信息时代,数据扮演着越来越重要的角色,而数据的可视化呈现正是一种强大的工具,能够帮助我们更好地理解和利用这些数据.虽然Excel和PPT在处理数据方面有着不可否认的作用,但在处理大规模.复杂数据 ...

  4. MinIO客户端之head

    MinIO提供了一个命令行程序mc用于协助用户完成日常的维护.管理类工作. 官方资料 mc head 查看对象的前N行内容,N默认为10,命令如下: ./mc head local1/bkt1/doc ...

  5. 春秋云镜 - CVE-2022-32991

    靶标介绍: 该CMS的welcome.php中存在SQL注入攻击. 访问页面,先注册,使用邮箱加密码登录. bp抓包,后台挂上sqlipy然后去测welcome.php,常用的语句都没成功但过一会就有 ...

  6. Python——第五章:处理异常try、except、else、finally

    处理异常try 和 except 在 Python 中,try 和 except 语句用于处理异常(错误).通过使用这两个关键字,你可以编写代码来捕获和处理可能发生的异常,以保持程序的稳定性. try ...

  7. 别再傻傻地用 ifconfig 查地址了!这条命令足以让你摘掉小白工程师的帽子

    大家好,我是民工哥. 众所周知,在 Linux 系统中,ip 和 ifconfig 这个两命令的功能十分相似,ifconfig 是 net-tools 中已被弃用的一个命令,很多年前就已经没有维护了. ...

  8. C#新鲜面试题出炉(2024)

    总所周知  C#这门语言 没有Java的八股文,所以面试题一般都是问的业务, 那么对于新手来讲,最起码也要会一些基础性问题, 以下就是包含C# 和sqlserver几个常见的面试题   1) Dele ...

  9. 【Python】人工智能-机器学习——不调库手撕深度网络分类问题

    1. 作业内容描述 1.1 背景 数据集大小150 该数据有4个属性,分别如下 Sepal.Length:花萼长度(cm) Sepal.Width:花萼宽度单位(cm) Petal.Length:花瓣 ...

  10. 提升软件质量?为什么不试试华为云CodeArts Check

    摘要:华为云CodeArts Check代码检查服务为用户提供包括代码风格.通用质量与代码安全风险等在内的检查能力,同时提供问题闭环处理.检查报告等功能,从而一站式完成代码检查作业. 本文分享自华为云 ...