JVM内存布局
1. 概述
对于从事c和c++程序开发的开发人员来说,在内存管理领域,他们既拥有最高权力的”皇帝“又是从事最基础工作的”劳动人民“---既拥有每个对象的”所有权“,又担负着每个对象开始到终结的维护责任。java把内存控制的权利交给了java虚拟机,一旦出现内存泄露和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查将会是一项异常艰难的工作。
2. 运行时数据区域
(1) 程序计数器
程序计数器是一块较小的内存空间,他可以看作是当前线程所执行的字节码的行号指示器。由于java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。因此,为了线程切换后能恢复正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器不影响,独立存储,我们称这类内存区域为”线程私有“的内存。
如果线程正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空。此内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
(2) java虚拟机栈
与程序计数器一样,java虚拟机栈也是线程私有的,每个线程创建的同时都会创建VM栈,他的生命周期与线程相同。虚拟机栈描述的是java方法执行的内存模型:每个方法在执行的同时会创建一个栈帧用于存储局部变量表(八种基本类型)、操作数帧、指向当前方法所属类的运行时常量池的引用、动态链接、返回结果等信息,非基本数据类型的对象在JVM栈上仅存放一个指向堆上的地址。
当线程执行一个方法时,就会随之创建一个对应的栈帧,压入栈。方法执行完成后,就会将方法出栈。线程当前执行的方法所对应的栈帧位于VM栈的顶部。
局部变量表存放了编译期可知的各种基本数据类型、对象引用。局部变量表所需的内存空间在编译期完全分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
对于这个区域规定了两种异常情况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
(3) 本地方法栈
虚拟机栈为虚拟机执行java方法(即字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError异常和OutOfMemoryError异常。
(4) java堆
java堆是java虚拟机所管理的内存中最大的一块,存储对象实例以及数组值的区域。java堆是被所有线程共享的一块内存区域,因此在其上进行对象内存的分配均需要进行加锁。java堆是垃圾收集器管理的主要区域。java堆细分为:新生代 和老年代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由Froom Space和To Space组成。java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
特点:
a. java堆是被所有线程共享的一块内存区域,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销,是比较大的。
b. 新生代:新建的对象都是用新生代分配内存的。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivior区,当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor区也满了的时候,从第一个Survivor区复制过来的并且还存活的对象,将被复制到老年代。
c. 老年代:在新生代中经历了N次垃圾回收后仍然存活的对象,就会被放到老年代中。因此,可以认为老年代中存放的都是一些生命周期较长的对象。
d. 永久代:实现方法区,主要存放所有已加载的类信息,方法信息,常量池等。永久代堆垃圾回收没有显著影响。
(5) 方法区
· 方法区与java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、final类型的常量、静态变量、属性、方法即时编译器编译后的代码等数据。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
(6) 运行时常量池
运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池。当常量池无法再申请到内存时就会抛出OutOfMemoryError异常。
3. 对象的创建
虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。之后虚拟机将为新生对象分配内存。对象所需内存的大小在来加载完成后便可以确定,为对象分配空间的任务等同于把一块确定大小的内存从java堆中划分出来。
对象在内存存储的布局可以分为3块区域:对象头、实例数据和对齐填充。
(1) 对象头包含两部分信息,第一部分用于存储对象自身的运行时数据,如哈希吗、GC分代年龄、锁状态标识、线程持有的锁、偏向线程ID、偏向时间戳等。对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
(2) 对象实例是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。
(3) 对齐填充:仅仅起到占位符的作用
对象的访问定位:java程序需要通过栈上的referrnce数据来操作堆上的具体对象。主流的访问方式有使用句柄和直接指针两种。
(1) 使用句柄访问的话,那么java堆中将会划出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与数据类型各自的具体地址信息。
(2) 使用直接指针访问,那么java堆对象的布局中就必须考虑如何放置访问类型的相关信息,而reference中存储的直接就是对象的地址。
JVM内存布局的更多相关文章
- JVM(二)JVM内存布局
这几天我再次阅读了<深入理解Java虚拟机>之第二章"Java内存区域与内存溢出异常",同时也参考了一些网上的资料,现在把自己的一些认识和体会记录一下. (本文为博主 ...
- JVM内存布局及GC知识回顾
注:本文篇幅较长,且需要有一定的java基础,建议各位看官,备好瓜子.饮料.小板凳,摆个让自己舒服的姿势,慢慢细看^_^, 文中所有素材,均来自互联网,本人只是详细梳理了一遍,形成此文. 一.JVM运 ...
- [转帖]详解JVM内存布局及GC原理,值得收藏
概述 https://www.toutiao.com/i6731345429574713868/ java发展历史上出现过很多垃圾回收器,各有各的适应场景,不仅仅是开发,作为运维也需要对这方面有一定的 ...
- JVM内存布局及GC知识
一.JVM运行时内存布局 按java 8虚拟机规范的原始表达:(jvm)Run-Time Data Areas, 暂时翻译为"jvm运行时内存布局". 从概念上大致分为6个(逻辑) ...
- 深入理解Java虚拟机之JVM内存布局篇
内存布局**** JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JVM的稳定高效运行.不同的JVM对于内存的划分方式和管理机制存在部分差异.结合JVM虚拟机规范,一起来 ...
- JVM 内存布局
JVM 内存布局规定了 Java 在运行过程中内存申请.分配.管理的策略,保证了 JVM 的高效稳定运行. 线程是否共享 Heap (堆区) 堆是 OOM 故障最主要的发生区域.它是内存区域中最大的一 ...
- JVM内存布局(又叫Java运行时数据区)
JVM 堆中的数据是共享的,是占用内存最大的一块区域. 可以执行字节码的模块叫作执行引擎. 执行引擎在线程切换时怎么恢复?依靠的就是程序计数器. JVM 的内存划分与多线程是息息相关的.像我们程序中运 ...
- JVM内存问题分析
JVM运行时数据区: 1.方法区:类信息(类名,访问修饰符.字段描述.方法 描述等).常量.静态变量.即时编译后的class文件等.在GC时用永久代来实现方法区 2.运行时常量池:是方法区的一部分,存 ...
- 2万字长文包教包会 JVM 内存结构 保姆级学习笔记
写这篇的主要原因呢,就是为了能在简历上写个"熟悉JVM底层结构",另一个原因就是能让读我文章的大家也写上这句话,真是个助人为乐的帅小伙....嗯,不单单只是面向面试学习哈,更重要的 ...
随机推荐
- 【转】最近很火的 Safe Area 到底是什么
iOS 7 之后苹果给 UIViewController 引入了 topLayoutGuide 和 bottomLayoutGuide 两个属性来描述不希望被透明的状态栏或者导航栏遮挡的最高位置(st ...
- Effective Java 第三版——66. 明智谨慎地使用本地方法
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- STM32F105 PA9/OTG_FS_VBUS Issues
https://www.cnblogs.com/shangdawei/p/3264724.html F105 DFU模式下PA9引脚用来检测USB线缆,若电平在2.7~5v则认为插入usb设备(检测到 ...
- 线程封装组件(BackgroundWorker)和线程(Thread)
BackgroundWorker是微软的在.net Framwork中添加的一个组件,主要对线程的访问提供了一种安全的方式.简单的说就是对Thread的一次封装. BackgroundWorker位于 ...
- Word Embedding/RNN/LSTM
Word Embedding Word Embedding是一种词的向量表示,比如,对于这样的"A B A C B F G"的一个序列,也许我们最后能得到:A对应的向量为[0.1 ...
- Python3输入输出
Python两种输出值的方式: 表达式语句和 print() 函数. 第三种方式是使用文件对象的 write() 方法,标准输出文件可以用 sys.stdout 引用. 如果你希望输出的形式更加多样, ...
- Google API Design Guide (谷歌API设计指南)中文版
面向资源的设计 这份设计指南的目标是帮助开发人员设计简单.一致.易用的网络API.同时,它也有助于收敛基于socket的API和(注:原文是with,这里翻译为“和”)基于HTTP的REST API. ...
- 最新Java基础面试题及答案整理
最近在备战面试的过程中,整理一下面试题.大多数题目都是自己手敲的,网上也有很多这样的总结.自己感觉总是很乱,所以花了很久把自己觉得重要的东西总结了一下. 面向对象和面向过程的区别 面向过程: 优 ...
- 同时执行多个$.getJSON() 数据混乱的问题的解决
在执行之前加$.ajaxSettings.async = false; (同步执行)执行你的代码之后及时恢复为$.ajaxSettings.async = true: (异步执行)不然影响别的地方的需 ...
- 让mysql 支持 emoji 表情
1.数据库配置 [client] default-character-set = utf8mb4 [mysql] default-character-set = utf8mb4 [mysqld] ch ...