一、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. termux+anlinux+Rvnc viewer来使安卓手机(平板)变成linux服务器

    第一步,先安装termux和anlinux,在此之前先安装一个vpn 下面是termux的官网(官网是没有内嵌任何广告的): termux/termux-app: Termux - a termina ...

  2. 429 You are being rate limited

    记录贴 429 真的很让人伤心 清除浏览器数据 我用的 Edge : 设置 ⇒ 隐私.搜索和服务 ⇒ 清除浏览器数据 ⇒ 立即清除 然后就重新登陆可以了

  3. 16、strconv

    1.strconv是什么? strconv是用来处理字符串和基本类型之间的转换的 2.strconv的使用 /** * @author ly (个人博客:https://www.cnblogs.com ...

  4. --{module_name}_binary_host_mirror和--{module_name}_binary_site

    --{module_name}_binary_host_mirror和--{module_name}_binary_site demo // .npmrc文件 sass_binary_site=htt ...

  5. Protobuf 的基本使用

    Protobuf 是 Google 用于序列化数据对象的一种机制,使得数据对象能够在应用程序和服务器之间进行交互,尽管现在 Java 已经对应的序列化的实现方式,但是传统的序列化方式存在严重的缺陷,因 ...

  6. pyecharts 保存图表至本地

    pip install snapshot-selenium from pyecharts.render import make_snapshot from snapshot_selenium impo ...

  7. 神经网络优化篇:理解mini-batch梯度下降法(Understanding mini-batch gradient descent)

    理解mini-batch梯度下降法 使用batch梯度下降法时,每次迭代都需要历遍整个训练集,可以预期每次迭代成本都会下降,所以如果成本函数\(J\)是迭代次数的一个函数,它应该会随着每次迭代而减少, ...

  8. Guava常用工具类总结

    === -"我想写得更优雅,可是没人告诉我怎么写得更优雅" -"Null的含糊语义让人很不舒服.Null很少可以明确地表示某种语义,例如,Map.get(key)返回Nu ...

  9. Blog Statistics Dec 1, 2021 - Dec 1, 2022

    1. Overview Data Date: Dec 1, 2021 - Dec 1, 2022 Number of articles: 51 All Platform Total Visits: 3 ...

  10. 云图说丨云数据库GaussDB(for MySQL)事务拆分大揭秘

    摘要:数据库代理提供事务拆分的功能,能够将事务内写操作之前的读请求转发到只读节点,降低主节点负载. 本文分享自华为云社区<[云图说]第270期 云数据库GaussDB(for MySQL)事务拆 ...