内存编织技术,JVM对内存的又一次压榨

Posted 硬核子牙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内存编织技术,JVM对内存的又一次压榨相关的知识,希望对你有一定的参考价值。

hello,小伙伴们好,我是江湖人送外号[道格牙]的子牙老师。


先说下手写JVM小班的事情。手写JVM小班一期已经完成招生,谢谢各位小姥的信任与支持。让你们变得牛叉,让你们成为别人眼中心中口中的大佬就是我的责任。我会为此目标持续优化升级课程。


目前市面上确实没人教这块内容,所以目前依然有小伙伴来咨询。手写JVM小班分两个阶段实现,第一阶段是用Java实现,第二阶段用C++实现。我预测了下,五月底之前报名,一期班可以跟得上,之后只能跟二期班了。想报名学习手写JVM实现自己的Java虚拟机,同时掌握C++开发底层项目能力的小伙伴可以加我微信:jvm-ziya。


今天这个问题就比较卷了,也是一位面试被虐得体无完肤的小伙伴提供的。放心哈,我已经安抚住他想砍面试官的心了。


其实看到这个问题,我还是挺感叹的:现在的面试题已经难到这个程度了吗?这个问题可是需要你完整得理解JVM是如何实现OOP的封装机制才能答出来的。


所有呢,给小伙伴们一个建议:简历不要凡尔赛,带来关注的同时,也带来了高期待。直接的结果就是问超难的面试题,一上来就给你打蒙圈了。



问题分析


我们先来分析下这个问题。如果你想知道怎么访问对象实例属性的底层原理,就得知道是如何存储的。存储搞明白了,访问就是一句话的事。而想搞明白存储,恰恰不是一件简单的事情。JVM中对象实例属性的存储甚至比原生的C++对象还要复杂。为什么这么说呢?往后看。


对于面向对象类型的语言来说,有两个很重要的概念:类、对象。类的所有信息在编译时就已经确定下来了。但是对象是运行时结构,它的实例属性信息,只有在执行完当前方法及其父类的构造方法才能知晓。针对这个情况,你可以事先定义一个list<T>用来存储对象的实例属性。但是实例属性的类型不确定,有可能是char、int、double、指针…你这个T好像只能用8字节的数据类型来接收才能兼容所有情况。这带来的问题就是严重的内存浪费。


那C++及Java是怎么做的呢?内存编织。解释下这个名词:编织,抽象来说,就是精细化构造。内存编织,即精细化构造内存。即在创建对象时,为了节省内存,根据不同类型的数据,精细化地向内存中填充数据。


为什么说JVM的对象实例属性存储机制比C++更难呢?因为JVM的内存编织需要考虑的点更多:一、JVM有运行时数据结构:数组。什么意思呢?就是说非数组类的元信息是在编译时确定的,而数组的元信息是在运行时确定的;二、JVM为了节省内存,开发了指针压缩技术。一开一关,两套机制需要研究;三、JVM为了比C++更节省内存,引入了字段重排机制,又给研究增加了难度。


OK!问题已经分析完了,开始展开来说。本篇文章只把谜底揭开,不会面面俱到地讲到所有。剩余的情况,有能力有好奇心的小伙伴就自行去研究啦。


其实这个问题的本质就是:JVM是如何实现OOP的封装机制的。看懂了,你对Java的理解就比别人高好几个维度。


C++中的实例属性存储


对比学习效果更佳!先来看看C++中的实例属性是如何存储的,上代码

class Person {private: short s; int i; long l;};


这段代码生成的对象在内存中长这个样子,占用16B,浪费了2B


内存编织技术,JVM对内存的又一次压榨


如果我把代码改一下,int移到long的下方,又完全不一样了。就变成了24字节,浪费了10字节。为什么会这样呢?因为C++这块只做到了这个程度。它是希望你在写代码的时候定规范解决这个问题。怎么规范呢?往后看。


内存编织技术,JVM对内存的又一次压榨


所以高手都知道:定义属性要遵循占字节少的数据类型放前面,占字节多的数据类型放后面。是不是好麻烦?我们写Java代码完全不需要考虑这个问题,JVM已经通过技术手段帮我们解决了。怎么解决的呢?往后看。


JVM中的实例属性存储


说完了C++的对象内存模型,再来说说JVM的对象内存模型,两句话:

  1. 创建对象进行属性编织时,按照8字节、4字节、2字节、1字节、指针的顺序进行编织。其中指针可以在最后,也可以在最前,可以通过参数控制。默认是放在最后。

  2. 编织时会将相同类型的属性放在一起。这样敲代码时就不需要关注属性定义的顺序。这项就是JVM独有的字段重排。这个实现起来也不是那么容易的,需要考虑很多很多。


还是上面的代码,对应的JVM对象的内存模型长这个样子


内存编织技术,JVM对内存的又一次压榨


如果上面的图比较抽象,那看这张图



JVM很牛叉有木有?反正我是很喜欢研究JVM的,研究过程中真的给了我很多惊喜。不得不赞叹,跟这些个大佬相比,我怎么好意思睡觉!^_^


JVM中的实例属性访问


JVM的对象内存模型已分析完毕,可以给出答案了。其实这里还要一个问题,就是内存是无态的,比如对象中有两个int,我要取第2个,取的时候怎么知道取的是哪一个呢?这时候就要找到对象的类信息,找到类信息中存储的属性表,然后才能完成取值。上伪代码


属性表 = 对象.类型指针.属性表属性地址 = 属性表[int][1


推荐阅读



结语


我是子牙老师,喜欢钻研底层,深入研究Windows、Linux内核、JVM。喜欢分享硬核知识,如果你也喜欢研究底层,喜欢硬核知识,关注我。



以上是关于内存编织技术,JVM对内存的又一次压榨的主要内容,如果未能解决你的问题,请参考以下文章

限制容器对内存的使用 - 每天5分钟玩转 Docker 容器技术(27)

限制容器对内存的使用 - 每天5分钟玩转 Docker 容器技术(27)

学习JVM--垃圾回收

第 4 章 容器 - 027 - 限制容器对内存的使用

丧,Scrum开发的又一次预料中的失败心得

JVM内存分配和常量池