为什么JVM要用到压缩指针?Java对象要求8字节的整数倍?
前言
前两天在一个帖子中看到一道面试题:
堆内存超过32G时,为什么压缩指针失效?
之前没有了解过这方面的知识,于是开始google起来,但当我翻看了不下一页的帖子,我都仍然没有搞懂,因为好多答案给我的感觉更像是:
因为堆内存超过32G,压缩指针会失效,所以堆内存超过32G时,压缩指针会失效。
我:???
本着有问题搞不懂就吃不下冰激凌的原则,我决定搞清楚这个问题。
32位和64位
首先我们都知道知道操作系统有32位操作系统(别名 x86 )和64位操作系统(别名 x86-64 或 x64),相对的JVM也分为32位和64位。
什么?你没注意过?
如何知道现在自己使用的JVM是32位还是64位?
命令行输入:java -version
如果你是64位的话,会出现 64-Bit 这样的内容:
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)
如果你是32位的话,则没有相关的内容:
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) Client VM (build 25.211-b12, mixed mode)
32位操作系统只可以运行32位的JVM,64位操作系统则都可以运行(有点类似兼容老版本)。
那我应该如何选择多少位的JVM呢?
32位JVM的寻址空间只有4G(2^32),也就是你的java进程最大只能使用4G内存(因为有其他开销,实际远小于4G);而64位JVM的寻址空间最大有2^64,差不多可以理解为无限大。
(不同操作系统和JVM对应的 进程最大内存 和 最大堆内存)
(Approx:大概,approximately的缩写)
64位的JVM听起来是升级版,性能要比32位的JVM要高吧?
其实恰恰相反,64位JVM的寻址空间更大了,但是会带来性能的损耗;同样的应用,运行在64位JVM上 比起 运行在32位JVM上 会有0~20%的性能损耗(取决于应用里面指针的数量)。
其实从名字上我们可以就可以区分,64位JVM,他的每一个native指针都占用64位(即64bit,也就是8字节)。32位JVM则只有4字节。加载这些额外的字节也自然会影响内存的占用。
铺垫这么多,指针的寻址原理奉上
假如我们这里有3个对象A、B、C,他们的大小分别为8、16、8字节(为什么假设这几个值,我们先按下不表),并且在内存中连续存储,为更好理解,我们简化为下图:
指针用 内存位置 来标记对象在内存中的位置:
A:00000000 00000000 00000000 00000000 (十进制表示:0)
B:00000000 00000000 00000000 00001000 (十进制表示:8)
C:00000000 00000000 00000000 00011000 (十进制表示:24)
从上面可以看出32位的指针,满打满算也只能存储2^32,约4GB的内存地址。
如果是64位的指针,就能表示2^64,上面说的可以理解为无限大,如果你有听过 国际象棋盘与麦粒 的故事就知道,它肯定能满足需求,甚至感觉还有点浪费。
回归题目,压缩指针的引入。
既然64位指针用来存储太浪费了,有什么更好的办法可以在32位的限制下表示更多的内存地址吗?
这时,我们发现对象A、B、C大小都是8字节的整数倍,即8是他们对象大小的最大公约数!
我这边就不卖关子,直接说答案,我们可以借助索引来标识。
用 8位内存地址偏移量 代表 1索引
那么A的位置就可以标识为 索引0,B为 索引1,C为 索引3。
指针用 索引 来标记对象在内存中的位置:
A:00000000 00000000 00000000 00000000 (十进制表示:0)
B:00000000 00000000 00000000 00000001 (十进制表示:1)
C:00000000 00000000 00000000 00000011 (十进制表示:3)
加入索引这一概念是为了方便理解;实际上JVM是通过读取时左移3位,存储时右移3位来完成的。
也就是说原本可表示4GB的内存地址,因为1索引表示8个内存地址偏移量,现在可以表示最高存储32GB的内存地址了。
伏笔回收:Java对象的大小为什么必须是8字节的整数倍?
上面的对象A、B、C我们假设的大小是8字节、16字节、8字节;共同点你可能发现了,他们都是8字节的倍数,其实Java对象的大小就必须是8字节的整数倍,如果没有这个条件,上面说的索引说法也不成立。
当然除了为了支持上面这些功能外,另外还有的就是因为现在大多数计算机都是高效的64位处理器,顾名思义,一次能处理64位的指令,即8个字节的数据,HotSpot VM的自动内存管理系统也就遵循了这个要求,这样子性能更高,处理更快。
JVM如何保证Java对象的大小都是8字节的整数倍?
用一个普通的Java对象举个简单的栗子
在JVM中,Java对象保存在堆中,由三部分组成:
1.对象头 (Object Header)
2.示例数据(Instance Data)
3.对齐填充(Padding)
如果你还不了解的话也没关系,我们讨论的只牵扯到最后一个部分,也就是 对齐填充。
对象可以有对齐填充,也可以没有;如果一个对象的前两部分所占大小不是8字节的整数倍,比如12字节,那么对齐填充会以此来填充对象大小到8字节的整数倍,即对齐填充占4字节,java对象一共16字节。
对齐填充:8字节的倍数就由我来组成!
有无压缩指针的区别
把64位JVM的指针压缩为32位,即引入压缩指针的原因是为了节省内存,但其实64位JVM的指针本来就可以是64位。
从8字节压缩到4字节,听起来好像才少了4个字节,但要知道,因为Java对象要补齐8字节的倍数;假如一个Java对象刚好满足了8字节整数倍,但因为没有压缩指针多出来4字节,这时又因为要补齐,还需要再补上4字节,一个对象就多了8字节!听起来好像还不怎么多,但这可是一个对象就少8字节,如果是一个大项目,差的可能就不是一星半点了。
所以肯定是开比不开好(JDK1.6的版本后,64位的JVM默认情况下是开启指针压缩的。)
冰激凌
我的心也终于放下了,马上下楼买冰激凌吃~
写在最后
我是苏易困,大家也可以叫我易困,一名Java开发界的小学生,文章可能不是很优质,但一定会很用心。
之前一直有自己写博客的想法,但无奈真的太拖延了,说懒说躺平都行,而且我也不是说真就很忙,忙到连码字的时间都没有了,都是自己给自己找的借口。
但自己其实也在不停地学习,奈何一直没有系统性地整理过,作为萌新,我真心觉得写博客跟其他大佬们交流真的是一件好事,很多东西其实你自己刚开始也不懂,但你为了整理成文章就需要去了解和学习更多,而且如果你能把这些知识传授给别人,别人也能听懂,那我觉得这些知识才是真的跟你自己融会贯通了。
确实真的不能再这样下去了,一定要克服自己的惰性,精力管理真的很重要,健身也要重新捡起来。
大家如果有什么建议或者文章有什么问题也都可以提出来,一起共同加油~
为什么JVM要用到压缩指针?Java对象要求8字节的整数倍?的更多相关文章
- java.lang.instrument: 一个Java对象占用多少字节?
一.对象头包括两部分信息:Mark Word(标记字段)和 Klass Pointer(类型指针) 1. Mark Word 用于存储对象自身的运行时数据,如哈希码(HashCode).GC分代年 ...
- JVM(2)--深入理解java对象创建始终
java对象探秘 java是一门面向对象的语言,我们无时无刻不在创建对象和使用对象,那么java虚拟机是如何创建对象的?又是如何访问对象的?java对象中究竟存储了什么运行时所必需的数据?在学习了ja ...
- jvm详情——2、Java对象在jvm中的大小
Java对象的大小 基本数据的类型的大小是固定的,这里就不多说了.对于非基本类型的Java对象,其大小就值得商榷.在Java中,一个空Object对象的大小是8byte,这个大小只是保存堆中一个没有任 ...
- Synchronized加锁、锁升级和java对象内存结构
首先了解一下JMM中定义的内存操作: 一个线程操作数据时候都是从主内存(堆内存)读取到自己工作内存(线程私有的数据区域)中再进行操作.对于硬件内存来说,并没有工作内存和主内存的区分,这都是java内存 ...
- Java对象的大小及应用类型
基础类型数据的大小是固定的,对于非基本类型的java对象,其大小就值得商榷了. 在java中一个空Object对象的大小是8byte,这个大小只是保存堆中没有任何属性的对象的大小,看下面的语 ...
- 重磅硬核 | 一文聊透对象在 JVM 中的内存布局,以及内存对齐和压缩指针的原理及应用
欢迎关注公众号:bin的技术小屋 大家好,我是bin,又到了每周我们见面的时刻了,我的公众号在1月10号那天发布了第一篇文章<从内核角度看IO模型的演变>,在这篇文章中我们通过图解的方式以 ...
- 【深入理解JVM】:Java对象的创建、内存布局、访问定位
对象的创建 一个简单的创建对象语句Clazz instance = new Clazz();包含的主要过程包括了类加载检查.对象分配内存.并发处理.内存空间初始化.对象设置.执行ini方法等. 主要流 ...
- JVM探秘:Java对象
本系列笔记主要基于<深入理解Java虚拟机:JVM高级特性与最佳实践 第2版>,是这本书的读书笔记. 对象的创建 虚拟机遇到一条 new 指令时,首先去检查这个指令的参数是否能在方法区常量 ...
- jvm源码解析java对象头
认真学习过java的同学应该都知道,java对象由三个部分组成:对象头,实例数据,对齐填充,这三大部分扛起了java的大旗对象,实例数据其实就是我们对象中的数据,对齐填充是由于为了规则分配内存空间,j ...
随机推荐
- soc AXI接口术语和特性
AXI接口术语和特性 1.outstanding 2.interleaving 3.out-of-oder 4.写数据可以优先于写地址 5.大小端 小端:低地址数据放在总线bus的低位. 大端:低地址 ...
- Centos下Ambari2.7.5的编译和安装
前言 终于,要开始写点大数据相关的文章了.当真的要开始写老本行的时候,还是考虑了挺久的.一是不知道从何处写起,二是如何能写点有意思的. 我们常说,过程比结果重要.也是有很多人喜欢准备完全之后,才会开始 ...
- CentOS7更新OpenSSH8
今天使用漏洞扫描工具扫描了一下系统漏洞,发现有一个openssh版本的漏洞.所以本着安全的原则修复一下. 第一时间打开百度搜索相关内容,大多数是编译安装的.每个人的环境和版本也不一样.有一定连不上去的 ...
- 如何让测试RFC2544更便捷——RFC2544测试实操
关键词:RFC2544:吞吐量测试:时延测试:丢包率:背靠背. 作为一名网络测试人员,大家肯定熟知一个测试标准,那就是RFC2544,RFC2544通过提供一个测试网络设备的测试标准,并规定了一系列测 ...
- 能无缝嵌入Excel的报表工具,报表轻松做!
现在很多人做数据分析会被推荐选择去用编程R和Python.PowerBI.Tableau.Qlik.Smartbi... 从目前数据分析的行业来说,Excel是世界上使用人数最多的报表分析工具,没有之 ...
- 实现按钮跳转&下划线等
按钮点击跳转页面:(在java文件里写) 1 public class MainActivity extends AppCompatActivity { 2 3 private Button mbtn ...
- SpringBoot进阶教程(七十三)整合elasticsearch
Elasticsearch 是一个分布式.高扩展.高实时的搜索与数据分析引擎.它能很方便的使大量数据具有搜索.分析和探索的能力.充分利用Elasticsearch的水平伸缩性,能使数据在生产环境变得更 ...
- 2020.9.28 多进程multiprocess 进程池pool 子进程subprocess 进程间通信
1.multiprocessing模块--跨平台版本的多进程模块 multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束: from ...
- pandas模块篇(之二)
今日内容概要 布尔选择器 索引 数据对齐 数据操作(增出改查) 算术方法 DataFrame(Excel表格数据) 布尔选择器 import numpy as np import pandas as ...
- QUIC协议详解
声明 本文可以自由转载但需注明原始链接.本文为本人原创,作者LightningStar,原文发表在博客园.本文主体内容参考论文[1]完成. 介绍 QUIC,发音同quick,是"Quick ...