为了加深对Java语言的理解,加深对Java虚拟机工作机制、底层特性的了解和掌握,准备在闲暇时间,抽空对《深入理解Java虚拟机 JVM高级特性与最佳实践》一书进行学习。本文是学习此书第2章时的总结与笔记,加入了一些自己的理解,也希望能帮助到需要的人。

1 运行时数据区域:

image

1.1 程序计数器:

一块较小的内存空间(线程私有的内存),当前线程所执行的字节码的行号指示器。字节码解释器通过改变这个计数器的值来选取下一条需要执行的字节码指令:分支、循环、跳转等。

每条线程都有一个独立的程序计数器(在一个时刻,一个处理器只会执行一条线程的指令)。

唯一一个在Java虚拟机规范中没有任何OutOfMemoryError的区域

1.2 虚拟机栈:

线程私有的,每个(java)方法(也就是字节码)在执行的时候会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。方法从调用到执行完成的过程,就是栈帧在栈中入栈到出栈的过程。

局部变量表存放编译期可知的基本数据类型、对象引用、returnAddress类型。所需的内存空间会在编译期完成分配,进入一个方法时在帧中的局部变量空间是完全确定的,不会运行时改变。

线程请求的栈深度大于虚拟机允许的深度,抛出SatckOverFlowError异常;虚拟机动态扩展时,无法申请到足够的内存,抛出OutOfMemoryError异常。

1.3 本地方法栈:

为虚拟机中使用到的Native方法服务,对本地方法栈中方法使用的语言、使用方式、数据结构没有强制规定,虚拟机可自由实现。

占用的内存区大小也不是固定的,可以根据需要动态的扩展或收缩。

1.4 Java堆:

Java堆是Java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时就创建

存放对象实例,几乎所有的对象实例都在堆上分配。也是垃圾收集器管理的主要区域。

1.5 方法区:

方法区也是被所有线程共享的内存区域。

用于存储类信息、常量、静态变量、即时编译器编译后的代码数据。

无法满足内存分配需求时,抛出OutOfMemoryError。

1.6 运行时常量池:

是方法区的一部分,存放编译期生成的各种字面量和符号引用。在类加载后才进入方法区的运行时常量池

特征是具备动态性,在运行时产生的常量被放入运行时常量池。包括:基本类型包装类(不管理浮点型:float和double,整形只管理-128 - 127)和String

常量池的好处:避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享;常量池中所有相同的字符串常量被合并,只占用一个内存空间。

Java中基本类型的包装类都实现了常量池技术,即Byte,Short,Integer,Long,Character,Boolean。

String str1 = "abcd";

String str2 = new String("abcd");

System.out.println(str1==str2);//false

String str1 = "str";

String str2 = "ing";

String str3 = "str" + "ing";

String str4 = str1 + str2;

System.out.println("string" == "str" + "ing");// true

System.out.println(str3 == str4);//false

String str5 = "string";

System.out.println(str3 == str5);//true

//连接表达式 +,只有使用“引号包含文本”的方式创建的String对象之间使用“+”连接产生的新对象才会被加入常量池中.

//对于字符串变量的“+”连接表达式,它所产生的新对象都不会被加入字符串池中,其属于在运行时创建的字符串,具有独立的内存地址,所以不引用自同一String对象。

2 虚拟机对象

2.1 对象的创建

虚拟机接收到new指令时,检查这个指令能否在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化。如果都没有,先执行类加载过程。

在类加载通过后,虚拟机为新对象分配内存(把一块确定大小的内存从Java堆中划分出来),内存大小在类加载完成后即可完全确定。

两种分配方式:

(1):指针碰撞:假设Java堆中内存是绝对规整的,即使用过的内存在一边,空闲的内存在另外一边,中间放着一个指针作为指示器,通过移动指针实现内存分配。

(2):空闲列表:如果Java堆中的内存并不是规整的,即已使用的内存和空闲的内存相互交错,虚拟机就必须维护一个列表,记录哪些内存块是可用的,通过从列表中寻找空间划分给对象实例来分配内存。

Java堆是否规整由所采用的垃圾收集器是否有压缩整理功能决定。

在虚拟机中创建对象不是线程安全的行为,可能出现在给对象A分配内存,指针还没来得及修改,对象B又使用了原来的指针来分配内存。有两种解决方案:

(1):对分配内存空间的动作进行同步处理,实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性;

(2):把内存分配的动作按照线程划分在不同的空间中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Loal Allocation Buffer,TLAB)。

内存分配完成后,需要将分配到的内存空间都初始化为零值,保证对象的实例字段在Java代码中可以不赋初始值就可以直接使用,程序能访问到这些字段的数据类型对应的零值。

设置对象,把对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等存放在对象头中。

2.2 对象的内存布局:

对象在内存中存储的布局可以分为3块:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。

对象头,包括两部分信息:

(1):存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机中分别为32bit和64bit,官方称为Mark Word(非固定的数据结构,根据对象的状态复用自己的存储空间)。

image

(2):类型指针,即指向对象的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

实例数据:对象真正存储的有效信息,即程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是子类自己定义的,都需要记录。

对齐填充:不是必然存在,起着占位符的作用,由于HotSpot VM要求对象的大小必须是8字节的整数倍,而对象头部分正好是8字节的整数倍,因此当实例数据没有对齐时,通过对齐填充来补全。

2.3 对象的访问定位:

Java通过栈上的reference数据(局部变量表中的对象引用)来操作堆上的具体对象,reference只规定了指向对象的引用,没有定义怎么去定位,访问堆中的对象的位置。对象访问方式由迅疾实现。

(1):句柄访问:Java堆会划分出一块内存作为句柄池,reference存储的就是对象的句柄地址,句柄包含了对象实例数据和类型数据各自的地址信息。

image

优势:reference中存储的是稳定的句柄地址,对象移动时只改变句柄中的实例数据指针,不改变reference。

(2):直接指针:reference中存储的直接就是对象地址,Java堆中放置访问对象类型数据(存放在方法区)的地址。

image

优势:速度更快,节省了一次指针定位的时间开销,HotSpot是使用直接指针访问。

java内存区域_JVM学习之—Java内存区域相关推荐

  1. 适合Java零基础小白学习的Java零基础教程

    很多Java零基础小白,在刚刚快入门的时候玩命的学习,玩命的记住Java原理,天天早上五点起床背Java的一些英文词汇,然后遇见一些未知的困难,让自己打到癫狂状态,逐渐迷失自我放弃Java,为了解决这 ...

  2. 【高级Java架构师系统学习】java问答社区系统

    02 JVM 线程 JVM内存区域 JVM运行时内存 垃圾回收与算法 JAVA四种引用类型 GC分代收集算法 VS 分区收集算法 GC垃圾收集器 JAVA IO/NIO JVM类加载器 03 JAVA ...

  3. 【高级Java架构师系统学习】java十六进制字符串转数字

    一.字节跳动技术一面(算法) Java 的 16 进制与字符串的相互转换函数 JAVA 时间格式化处理 将毫秒转化为日期 文本的倒序输出 判断一个数字是奇数还是偶数 用Hibernate 实现分页 3 ...

  4. java进出栈_JVM函数调用:Java出入栈

    JVM函数调用:Java出入栈 JVM函数调用:Java出入栈 目录 局部变量表 索引复用 垃圾回收 栈数据区 栈上分配 线程作为系统运算调度的最小单位,在JVM中线程的行为体现就是函数调用,函数调用 ...

  5. java volatile lock_Java并发学习笔记 -- Java中的Lock、volatile、同步关键字

    Java并发 一.锁 1. 偏向锁 1. 思想背景 来源:HotSpot的作者经过研究发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同 一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁 ...

  6. 讲解java源码_Java学习之Java源码讲解

    关于Java中源码的学习,是不少同学头疼的知识点.本文整理了JAVA源码学习的八大要点,分别是基础知识.面向对象.异常处理.集合.综合类核心代码.JAVA8新特性.Input/Output和Java小 ...

  7. Java核心技术基础知识学习之Java集合(三)

    文章目录 七.Java集合 7.6 Java 8 增强的 Map 集合 7.6.1 Java 8 中 Map 新增的方法 7.6.2 Java 8 改进的 HashMap 和 HashTable 实现 ...

  8. java堆外内存溢出_JVM 案例 - 堆外内存导致的溢出错误

    案例 一个网站为了实现客户端实时从服务端接收数据,使用了 CometD 1.1.1 作为服务端推送框架,服务器是 Jetty7.1.4,CPU i5,内存 4G,操作系统 32位Windows. 服务 ...

  9. java 内存压缩_JVM之指针压缩内存如何设置

    在32位到64位的转变中,人们最大的获益是内存容量.在一个32位的系统中,内存地址的宽度就是32位,这就意味着,我们最大能获取的内存空间是2^32(也就是4G)字节.这个容量明显不够用!在一个64位的 ...

最新文章

  1. java bigdecimal乘法_Java BigDecimal类型的 加减乘除运算
  2. 使用HBase Client访问阿里云NoSQL数据库表格存储
  3. v-distpicker 一个好用的三级联动的插件
  4. mysql命令详细解_mysql命令详解
  5. java 微信图片上传_微信小程序图片上传java端以及前端实现
  6. docker hive nagasuga_制作一个用来调试hive的docker镜像
  7. cocos2d-x 2.x版本接入bugly的总结
  8. 01 docker容器技术基础入门
  9. 计算机上的mrc功能,计算器上的MRC有什么功能?
  10. 得力助手 消防员的 消防机器人_机器人化身消防员“得力助手”,进入危险火场执行工作|机器人日报...
  11. hpsocket错误码对照表
  12. python强制删除文件夹_python删除文件夹下的文件保留但清空子文件夹
  13. 宇宙最帅XX--Alpha阶段项目复审
  14. chrome主页被毒霸网址大全劫持解决办法
  15. JNI/NDK入门指南之正确姿势了解JNI和NDK
  16. 使用Power Automate (MS Flow) 发送审批邮件
  17. 上天入地影无踪:十大超级老牌黑客
  18. 还记得maven使用之令人头疼的.lastUpdated文件吗?
  19. python接私活王者_Python从青铜到王者这5个实战项目要会
  20. 精选“数据分析”好问题汇总·第一期

热门文章

  1. python空值赋0_Python中的空值判断
  2. pmbook 知识领域 第六版_PMP项目管理10大知识领域脑图
  3. 洛谷3605 Promotion Counting
  4. 《敏捷可执行需求说明 Scrum提炼及实现技术》—— 1.2 识别不确定性的影响
  5. tomcat实现session集群及tomcat+memcached共享session存储(四)
  6. WIF基本原理(2)基于声明的标识模型
  7. Windows批处理命令学习三
  8. 试图抓取非英文windows操作系统镜像时PE无法正常启动解决方法
  9. javaweb学习总结(十九)——JSP标签
  10. JS(去掉前后空格或去掉所有空格)的用法