主页

无锁无界队列

正常不支持并发的队列,这里的实现是头尾节点初始状态都是指向一个哑结点,方便处理队列为空这一边界情况 public class Queue<T> { private Node<T> head; private Node<T> tail; public Queue() { head = tail = new Node<>(null); } public void enq(T e) { Node<T> newNode = new Node<>(e); tail.next = newNode; tail = new...

阅读更多

垃圾回收算法手册读书笔记(八)并发垃圾回收

并发垃圾回收 之前我们介绍的所有垃圾回收,包括并行垃圾回收,都会在垃圾回收过程中将赋值器线程挂起。而并发垃圾回收,将会考虑如何使回收器线程和赋值器线程交替或者并行执行,以达到降低停顿时间的目的 上图列出多种并发策略,其中白条代表赋值器的执行,灰条与黑条代表回收器的不同回收周期的执行 增量回收 单处理器下的增量回收,这种策略下,回收器在执行垃圾回收的过程中赋值器仍然挂起,但与之前不同的是,回收器并不会在一次赋值器线程挂起中完成所有的垃圾回收任务,而是在一个垃圾回收周期中,回收器与赋值器交替的执行,回收器每次执行增量的完成垃圾回收任务,这一策略很容易扩展到多处理器环境下,同时也可以将增量回收并行化。 主体并发回收 在回收的某些阶段(比如线程栈的扫描),同步是一种比并发更加简单高效...

阅读更多

垃圾回收算法手册读书笔记(七)并行垃圾回收

并行垃圾回收 到目前为止,我们讨论的垃圾回收都是假定存在多个赋值器线程,但只存在一个回收器线程。这显然对于现代多核或多处理器而言,是极大的资源浪费。如果我们能够将垃圾回收并行化,将明显的提升垃圾回收器的性能。 我们需要注意的是,并行回收仍然需要在回收的过程中stop the world,即挂起所有赋值器线程,在此期间使用多个线程并行执行垃圾回收任务。 图中可以看出并行回收与之前讨论的单线程回收之间的差异,仅仅在于垃圾回收过程的并行化。之前我们讨论回收基本算法的时候已经知道了,除去引用计数法,垃圾回收的主要过程包括 标记 清扫 复制 整理 因此并行垃圾回收主要讨论的是以上四个过程的并行化 是否有足够多的工作可以并行 在讨论并行化的时候,首先要确定的就是当前...

阅读更多

垃圾回收算法手册读书笔记(六)特定语言相关内容

对象的终结 自动垃圾回收能够很好的管理其托管范围内的对象,但对于其托管范围外的对象无能为力。如果托管对象引用了一个非托管对象,当该托管对象不可达将被回收时,回收器也无法对该非托管对象做进一步处理,这有可能造成资源的泄漏。一个典型的例子就是程序打开一个文件,如果开发人员忘记关闭文件,垃圾回收器在回收文件对象时也不会帮助开发人员自动关闭文件。 但是对于一个文件对象被多个组件共享的场景,开发人员不一定能够准确的判断出关闭文件的时机。而对于垃圾回收器来说把握这个时机容易的多。只要组件在完成工作后将文件对象的引用置空,最终垃圾回收器在回收的过程中就能探测到该文件对象不可达,这时候就是安全关闭文件的好时机。 为了达到这个目的,我们需要在对象不可达后,执行一些用户自定义的行为(比如关闭文件)。典...

阅读更多

垃圾回收算法手册读书笔记(五) 分代垃圾回收

分代垃圾回收 垃圾回收的主要目的是找到已经死亡的对象并且回收其所占用的内存。在堆内存分区中有提到,对于长寿对象,反复的标记,移动这些对象将影响垃圾回收的效益。弱分代假说告诉我们,大部分对象在其年轻时死亡,利用这一特性,开发人员根据对象年龄进行分代(分区),将年轻对象放入年轻代,将长寿对象放入老年代。回收器优先回收年轻代,并将其中年龄足够长的对象提升到更老的一代。 分代垃圾回收希望通过较多的次级回收(minor collection)与较少的主回收(major collection),来达到降低停顿时间的期望 示例 上图是一个分代垃圾回收的简单示例,它使用两个分代,每个新创建的对象被分配在年轻代,回收器优先回收年轻代对象,并负责将年轻代中寿命够长的对象提升到老年代。其中有几个问题...

阅读更多

垃圾回收算法手册读书笔记(四) 堆内存的划分

堆内存划分 到目前为止我们都假定垃圾回收以这样的方式进行: 所有的对象是由相同的垃圾回收算法管理的,并且所有垃圾将在同一时间得到回收。 然而这个假设并不是必须的,在之前的介绍中我们也注意到,不同的垃圾回收算法在处理具有不同特性的对象时互有优势,如果我们将堆划分成多个分区,具有相同特性的对象分配在同一个分区,并且在不同分区上应用不同的垃圾回收算法,这样使得不同的垃圾算法能够尽可能的发挥出自己的优势,对于垃圾回收性能的提升显而易见。 根据什么分区 根据对象是否移动进行分区 当一个对象被传递给非托管代码时,回收器不能贸然移动这个对象。此时称该对象被”钉住”。对于不能被移动的对象,使用非移动式的标记-清扫算法显然是个好的选择 根据对象大小进行分区 在某些情况下移动对象可能是不合...

阅读更多

垃圾回收算法手册读书笔记(三) 内存分配

内存分配 基本内存分配策略: 顺序分配 空闲链表分配 顺序分配 对于一个可分配的空闲内存块,顺序分配将从内存块的其中一端开始进行分配。 顺序分配主要特征如下: 简单:所需数据结构非常简单,只需要一个空闲指针(free pointer)和一个界限指针(limit pointer)。分配过程中只需要不断的移动空闲指针 高效:相较于空闲链表分配,顺序分配可以给赋值器带来更好的空间局部性 回收器限制:不适用于非移动式回收器 空闲链表分配 空闲链表分配使用某种数据结构来记录空闲内存单元的位置与大小,该数据结构即是空闲内存单元集合。空闲内存单元的组织形式并不一定是链表,不过我们这里仍使用”空闲链表”这个名字来称呼这种记录内存单元的数据结构。 空闲链表分配时,分...

阅读更多

垃圾回收算法手册读书笔记(二) 基本垃圾回收策略

基本垃圾回收策略 标记-清扫 (mark-sweep) 标记-复制 (mark-copy) 标记-整理 (mark-compact) 引用计数 (reference-counting) 主要着重于前三中基本回收策略,由于目前主流的垃圾回收器都不是基于引用计数的,所以只是简单提一提,并不详细介绍 在对这几种基本回收策略进行描述时,我们假定的前提是赋值器运行在一个或多个线程上,而回收器则是单线程的,并且回收器运行时,所有赋值器线程均处于停止状态。这种“万物静止”(stop the world)的策略排除了赋值器与回收器间的互相干扰,大幅简化了回收器的实现。更为复杂的,赋值器与回收器并发执行的垃圾执行策略,将留给以后再做介绍。 标记-清扫算法 标记-清扫算法通过对赋...

阅读更多