【笔记】自动内存管理的艺术:堆内存的划分

为何要进行分区

之前几章讲解垃圾回收时,都假定:所有对象由相同的垃圾回收算法管理,并且所有垃圾都将在同一时间得到回收。然而如果我们对不同的对象加以区别对待的话,在回收处理的性能上将得到相当大的好处。最广为人知的例子就是所谓“分代回收算法”,该算法将对象按不同的年龄进行分割,并优先回收更年轻的对象。

如何分区

分区原则 详细描述 案例
移动性 传递给操作系统的对象无法被移动 Java 里有 native memory
对象大象 大对象移动代价高 JVM 参数 -XX:PretenureSizeThreshold 可以指定直接在老年代分配的大对象的阈值
生命周期 对于生命周期较长,并且内存碎片对其并非急需处理的对象来说,我们可以将其保存在一个主要是非移动操作、偶尔会进行一趟扫描来整理的空间里。而那些分配频率、死亡率更高的对象则可以放到一个由分配速度快且回收代价小的复制式回收器管理的空间中。 年轻代、老年代的划分
对象类型 虚拟机通常在堆内存中生成并保持代码序列,移动和清理代码会有一些特殊问题,如确定和保持一致性、代码的引用或确定代码何时不再使用并因此可以被卸载,代码对象也通常是体积庞大且长寿的。 HotSpot 方法区
线程 垃圾回收动作需要在赋值器线程和回收器线程之间进行同步,对于每次只需终止不超过一个赋值器线程的即时回收,其可能需要一个与其他赋值器线程进行握手同步的复杂系统才能实现,而即使是万物静止的回收算法也需要通过同步来终止所有赋值器线程的运行。如果每次只终止一个线程,并仅回收由该线程分配的尚且不能从其他线程可达的那些对象,则同步代价是可以减少的。为了做到这一点,回收器必须能够区分出哪些对象是仅单个线程可访问的,哪些对象是被多个线程共享的,然后可以使用线程本地子堆进行分配 Java 里的 TLAB(Thread-local allocation buffer)是为了减少内存分配过程中的同步操作,与这里的线程本地子堆有些相似

何时进行分区

分区的决策既可以是静态完成(编译时),也可以是动态进行——当一个对象被创建时,或者在垃圾回收期间,或者在赋值器访问对象时。

最著名的分区方案是按对象的年代进行划分,在此方案中,对象根据其年龄被分割开来。在这种情况下,分区是由回收器动态执行的,当对象年龄超过一定阈值时,该对象将被提升到下一个分代的空间中。

分区的决策也可能是由分配器完成的,最常见的情况是,分配器会根据一个分配请求所需内存大小来决定对象是否应该被分配到大对象空间里。通过对象类型、代码或一些其他分析,对象所属空间也可以被静态划定。如果能够提前知道某种类型对象都存在某种公共属性,比如永生性,则编译器可以决定将这些对象分配到哪个空间,并生成适当的代码序列。∏

参考资料

  1. 《垃圾回收算法手册:自动内存管理的艺术》
  2. TLAB 与 Java 对象内存分配
发布了232 篇原创文章 · 获赞 347 · 访问量 79万+

猜你喜欢

转载自blog.csdn.net/hustspy1990/article/details/89340135
今日推荐