当前位置: 首页 > 科技观察

我需要看父母的评估才能找到Java合作伙伴吗?

时间:2023-03-18 18:25:50 科技观察

程序员经常开玩笑说现在找对象太难了,看看我们的代码找对象多容易,想找的时候再新建一个。笑话就是笑话。其实对于代码来说,对象并不是想new就new就可以的东西。像在现实社会中见家长,认识家人等等,在实际的代码操作中,并不多。一个对象new的过程也要经过层层“考核”。今天就来看看,代码中创建一个新对象的流程是怎样的。在Java开发中,你能想到的对象创建过程最直接的体现在代码中的newMyObject()。然而,这只是“冰山一角”。在虚拟机的执行过程中,需要给堆中的对象空间。比如最容易想到的就是你需要的对象有点大,new的时候内存不够用。或者当前创建的对象的Class继承或者组合了其他的类或者接口,在类加载过程中并没有发现,这和现实中的父母不同意几乎是一样的。另外,在多线程环境下,对象可能会过早地“逃逸”,就像父母让对象去相亲一样,让其他线程修改对象的值,让你始料未及。...你以为只是一个简单的new,但是JVM真的做了很多事情~JVM默默的承担了这一切,让困难和复杂无形。比如,为了让你更快的拥有对象,JVM会看能不能“快速分配”,如果满足条件,甚至会有专门的内存空间来保证不被影响其他线程等等,如果还是不行,那就只能慢下来了。创建对象的基本流程如下:获取对象在常量池中的索引和对应的常量池项验证类是否被解析过获取其对应的instanceKlass确保初始化完成如果条件满足遇到,进入快速分配过程不满意或快速分配失败,进入慢速分配,然后初始化类。综上所述,实例分配似乎只有两步:为对象分配空间和设置对象的引用,但是分配中有很多细节。为了进一步加快分配速度,并不是所有的对象都分配在Eden区。想一想,整个堆都是线程共享的。如果大家都在这里分内存,加锁操作就不可避免了。为此,JVM提供了一个加速选项。在伊甸园中,线程被分配了自己的私有分配区,也就是传说中的“TLAB(ThreadLocalAllocationBuffers)”。这样每个线程在分配的时候,因为是线程私有的,所以不需要加锁,可以很快完成。如果分配失败,它将尝试在Eden中锁定和分配空间。是不是感觉TLAB的分布类似于动物占地,在某个区域拉屎或者撒尿,提醒其他动物这个地方被占了。:-)生活中的例子有点类似于带计时的VIP通道。比如你在理发店充值成为VIP,10次理发就够了。然后你去10次以内,不用和其他人排队,直接给你分配一个固定的master。那如果还有一次,你要烫发,原来的VIP不够用,就需要在“烫发”资源里排队。在《虚拟机设计与实现》一书中,线程局部分配是这样描述的:跳转指针分配只能在空闲空间属于单线程时使用。如果有多个线程,分配应用程序是线程安全的。为每个对象分配使用原子指令曾经很昂贵。一个常见的解决方案是只使用原子操作进行块分配。每个线程用一个原子指令从全局空间中抓取一个空闲块,然后使用一个跳转指针在块中分配一个对象。没有使用原子操作。该块在本地用于线程分配。TLAB设置有多合适?毕竟这个块的分配还是需要原子操作的。如果太小,就需要频繁请求块的分配,从而打消线程局部块的优势。但是block不能太大,否则如果应用中线程很多,可能有些线程不会主动分配对象,里面只有几个对象的block空间就会被浪费掉。TLAB的大小在HotSpot中默认是自动调整的,根据一系列线程和每次创建的对象大小自动评估。当然,你也要想想,如果你的对象占了很大的空间怎么办?因此,在进行TLAB分配时,会判断现有的margin是否足够分配对象。够了就直接分配。如果不够,看看还剩多少。如果剩余量大于允许的浪费量,则直接在Eden上分配,而不是在TLAB上分配。否则,再申请一个TLAB,继续分配。如何计算物体尺寸?之前有一篇文章《怎样计算一个 Java 对象大小?这儿有几种方法~》这种TLAB分配形式,也叫“跳转指针分配”,也就是说,分配对象时,只需要在空闲空间移动指针,跳转对象大小即可。如果TLAB分配失败,只能在Eden重试分配空间。分配空间后,虚拟机根据配置决定是否将实例数据填充为0。这样下面的Java代码就可以不用初始化赋值直接使用了。然后初始化对象头以更新堆栈顶部的对象引用。慢速分配需要在分配实例之前解析类,以确保类和依赖类已经被解析和初始化。首先你需要检查类,确保它不是抽象类和接口;然后确保类已经初始化,计算对象大小,在堆上创建实例,最后在线程栈中设置对象引用?代码中找对象没那么容易:-)【本文为专栏作家“侯树成”原创稿件,转载请通过作者微信获取授权公众号“Tomcat那些事”】点此查看查看该作者更多好文章