[翻译]JVM 虚拟机创建对象的过程分析(上)
JVM 虚拟机创建对象的过程分析

JVM是怎么创建对象的呢?当你new Object()的时候到底发生了什么?
对象的分配通过是使用线程本地分配缓存(TLABs):每个线程独有的内存分配区域。因为在这块区域中不需要考虑同步,所以创建对象的速度会很快。
但是怎样正确地选择TLAB的大小呢?当你要分配10%的TLAB空间,但只剩9%的时候该怎么办呢?对象可以被分配在TLAB外吗?当分配的内存设置为0的时候会怎样?
在问了这些问题但没有找到答案之后,我决定写一篇文章来讲述这些内容。
在阅读后面的内容之前,如果对垃圾回收的工作机制有一些理解的话,会更容易理解
介绍
创建一个对象需要哪些步骤?
首先,需要找到一片未被占用且足够大的内存区域,然后初始化对象:用0填充内存区域,初始化一些内部结构(用于调用getClass()的信息及同步需要的信息等),最后调用构造函数。
本文的组织结构如下:首先在理论上分析创建对象时会发生些什么,然后我们进入JVM内部看看真实的情况是什么样的,最后写一些测试程序来验证它们。
声明:某些部分在不丢失一般性的情况下简化了。当说到垃圾回收的时候,我是指任意的压缩回收器,当说到地址空间的时候,我是指新生代的eden区。对于其他的(标准的或是知名的)垃圾回收器,部分地方可能会有变化,但不会变化太多。
TLAB 101
第一部分是给我们的对象分配内存。
一般而言,内存的分配是一项非常重要的任务,充满了痛苦。例如,链表的创建需要两倍的内存空间,在必要的情况下,需要搜索并将一部分内存区域从一个链表剪切到另一个链表(也就是buddy allocator)。幸运的是,Java中有一个垃圾收集器,它承担了其中大部分工作。在对新生代进行垃圾回收的过程中,所有存活的对象会被转移到幸存区,这样eden区就会空出来一大段连续的内存。
因为JVM会进行垃圾回收,分配器只需要知道去哪里找到空闲的内存,实际情况是,通过控制一个指向这片最大空闲内存区间的指针来寻找空闲内存。也就是说,分配过程非常简单:你需要将eden区的指针增加这个对象的大小(这一技术称之为指针碰撞,bump-the-pointer)。内存可以被同时分配给好几个线程,所以需要一些同步操作。如果你用最简单的方法来实现(阻塞堆内存或是使用原子操作实现指针的增长),内存分配就会成为一个瓶颈,所以JVM的开发者发明了指针碰撞:每个线程分配一大片独占的内存区域。如果可能的话,在其中的内存分配操作会导致指针的增长(但是因为是发生在线程本地,所以不需要同步),当这一片内存区域用完的时候会再申请一片内存空间。这一区域称之为线程本地分配缓存,它最终表现为基于层级的指针碰撞,第一层是堆区域,第二层是是当前线程的TLAB。这篇文章里有更进一步的探讨,分层是通过将缓存区内部再分割出一片缓存区实现的。
注意:上传附件及图片大小不得大于30M。
⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。
