# Nicksxs's Blog
learn from zero,技术博客，Nicksxs，史学森

0%

## docker比一般多一点的初学者介绍

Posted on 2020-03-08 Edited on 2020-03-15 In Docker , 介绍 Views: Disqus:

因为最近想搭一个phabricator用来做看板和任务管理，一开始了解这个是Easy大大有在微博推荐过，后来苏洋也在群里和博客里说到了，看上去还不错的样子，因为主角是docker所以就不介绍太多，后面有机会写一下。

docker最开始是之前在某位大佬的博客看到的，看上去有点神奇，感觉是一种轻量级的虚拟机，但是能做的事情好像差不多，那时候是在Ubuntu系统的vps里起一个Ubuntu的docker，然后在里面装个nginx，配置端口映射就可以访问了，后来也草草写过一篇使用docker搭建mysql集群，但是最近看了下好像是因为装docker的大佬做了一些别名还是什么操作，导致里面用的操作都不具有普遍性，而且主要是把搭的过程写了下，属于囫囵吞枣，没理解docker是干啥的，为啥用docker，就是操作了下，这几天借着搭phabricator的过程，把一些原来不理解，或者原来理解错误的地方重新理一下。

之前写的 mysql 集群，一主二备，这种架构在很多小型应用里都是这么配置的，而且一般是直接在三台 vps 里启动三个 mysql 实例，但是如果换成 docker 会有什么好处呢，其实就是方便部署，比如其中一台备库挂了，我要加一台，或者说备库的 qps 太高了，需要再加一个，如果要在 vps 上搭建的话，首先要买一台机器，等初始化，然后在上面修改源，更新，装 mysql ，然后配置主从，可能还要处理防火墙等等，如果把这些打包成一个 docker 镜像，并且放在自己的 docker registry，那就直接run 一下就可以了；还有比如在公司要给一个新同学整一套开发测试环境，以 Java 开发为例，要装 git，maven，jdk，配置 maven settings 和各种 rc，整合在一个镜像里的话，就会很方便了；再比如微服务的水平扩展。

但是为啥 docker 会有这种优势，听起来好像虚拟机也可以干这个事，但是虚拟机动辄上 G，而且需要 VMware，virtual box 等支持，不适合在Linux服务器环境使用，而且占用资源也会非常大。说得这么好，那么 docker 是啥呢

docker 主要使用 Linux 中已经存在的两种技术的一个整合升级，一个是 namespace，一个是cgroups，相比于虚拟机需要完整虚拟出一个操作系统运行基础，docker 基于宿主机内核，通过 namespace 和 cgroups 分隔进程，理念就是提供一个隔离的最小化运行依赖，这样子相对于虚拟机就有了巨大的便利性，具体的 namespace 和 cgroups 就先不展开讲，可以参考耗子叔的文章

## 安装

那么我们先安装下 docker，参考官方的教程，安装，我的系统是 ubuntu 的，就贴了 ubuntu 的链接，用其他系统的可以找到对应的系统文档安装，安装完了的话看看 docker 的信息

    1

    sudo docker info

输出以下信息

## 简单运行

然后再来运行个 hello world 呗，

    1

    sudo docker run hello-world

输出了这些

看看这个运行命令是怎么用的，一般都会看到这样子的，sudo docker run -it ubuntu bash`, 前面的 docker run 反正就是运行一个容器的意思，`-it`是啥呢，还有这个什么 ubuntu bash，来看看`docker run`的命令帮助信息

    1

    -i, --interactive                    Keep STDIN open even if not attached

就是要有输入，我们运行的时候能输入

    1

    -t, --tty                            Allocate a pseudo-TTY

要有个虚拟终端，

    1
    2
    3

    Usage:	docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

    Run a command in a new container

## 镜像

上面说的-it 就是这里的 options，后面那个 ubuntu 就是 image 辣，image 是啥呢

Docker 把应用程序及其依赖，打包在 image 文件里面，可以把它理解成为类似于虚拟机的镜像或者运行一个进程的代码，跑起来了的叫docker 容器或者进程，比如我们将要运行的`docker run -it ubuntu bash`的ubuntu 就是个 ubuntu 容器的镜像，将这个镜像运行起来后，我们可以进入容器像使用 ubuntu 一样使用它，来看下我们的镜像，使用`sudo docker image ls`就能列出我们宿主机上的 docker 镜像了

一个 ubuntu 镜像才 64MB，非常小巧，然后是后面的`bash`，我通过交互式启动了一个 ubuntu 容器，然后在这个启动的容器里运行了 bash 命令，这样就可以在容器里玩一下了

## 在容器里看下进程，

只有刚才运行容器的 bash 进程和我刚执行的 ps，这里有个可以注意下的，bash 这个进程的 pid 是 1，其实这里就用到了 linux 中的PID Namespace，容器会隔离出一个 pid 的名字空间，这里面的进程跟外部的 pid 命名独立

## 查看宿主机上的容器

    1

    sudo docker ps -a

## 如何进入一个正在运行中的 docker 容器

这个应该是比较常用的，因为比如是一个微服务容器，有时候就像看下运行状态，日志啥的

    1

    sudo docker exec -it [containerID] bash

## 查看日志

    1

    sudo docker logs [containerID]

我在运行容器的终端里胡乱输入点啥，然后通过上面的命令就可以看到啦

## 寄生虫观后感

Posted on 2020-03-01 In 生活 , 影评 , 2020 Views: Disqus:

寄生虫这部电影在获得奥斯卡之前就有关注了，豆瓣评分很高，一开始看到这个片名以为是像《铁线虫入侵》那种灾难片，后来看到男主，宋康昊，也是老面孔了，从高中时候在学校操场组织看的《汉江怪物》，有点二的感觉，后来在大学寝室电脑上重新看的时候，室友跟我说是韩国国宝级演员，真人不可貌相，感觉是个呆子的形象。

但是你说这不是个灾难片，而是个反映社会问题的，就业比较容易往这个方向猜，只是剧情会是怎么样的，一时也没啥头绪，后来不知道哪里看了下一个剧情透露，是一个穷人给富人做家教，然后把自己一家都带进富人家，如果是这样的话可能会把这个怎么带进去作为一个主线，不过事实告诉我，这没那么重要，从第一步朋友的介绍，就显得无比顺利，要去当家教了，作为一个穷成这样的人，瞬间转变成一个衣着得体，言行举止都没让富人家看出破绽的延世大学学生，这真的挺难让人理解，所谓江山易改，本性难移，还有就是这人也正好有那么好能力去辅导，并且诡异的是，多惠也是瞬间就喜欢上了男主，多惠跟将男主介绍给她做家教，也就是多惠原来的家教敏赫，应该也有不少的相处时间，这变了有点大了吧，当然这里也可能因为时长需要，如果说这一点是因为时长，那可能我所有的槽点都是因为这个吧，因为我理解的应该是把家里的人如何一步步地带进富人家，这应该是整个剧情的一个需要更多铺垫去克服这个矛盾点，有时候也想过如果我去当导演，是能拍出个啥，没这个机会，可能有也会是很扯淡的，当然这也不能阻拦我谈谈对这个点的一些看法，毕竟评价一台电冰箱不是说我必须得自己会制冷对吧，这大概是我觉得这个电影的第一个槽点，接下去接二连三的，就是我说的这个最核心的矛盾点，不知道谁说过，这种影视剧应该是源自于生活又高于生活，越是好的作品，越要接近生活，这样子才更能有感同身受。

接下去的点是金基宇介绍金基婷去给多颂当美术家教，这一步又是我理解的败笔吧，就怎么说呢，没什么铺垫，突然从一个社会底层的穷姑娘，转变成一个气场爆表，把富人家太太唬得一愣一愣的，如果说富太太是比较简单无脑的，那富人自己应该是比较有见识而且是做 IT 的，给自己儿子女儿做家教的，查查底细也很正常吧，但是啥都没有，然后呢，她又开始耍司机的心机了，真的是莫名其妙了，司机真的很惨，窈窕淑女君子好逑，而且这个操作也让我摸不着头脑，这是多腹黑并且有经验才会这么操作，脱内裤真的是让我看得一愣愣的，更看得我一愣一愣的，富人竟然也完全按着这个思路去想了，完全没有别的可能呢，甚至可以去查下行车记录仪或者怎样的，或者有没有毛发体液啥的去检验下，毕竟金基婷也乘坐过这辆车，但是最最让我不懂的还是脱内裤这个操作，究竟是什么样的人才会的呢，值得思考。

金基泽和忠淑的点也是比较奇怪，首先是金基泽，引起最后那个杀人事件的一个由头，大部分观点都是人为朴社长在之前跟老婆啪啪啪的时候说金基泽的身上有股乘地铁的人的味道，简而言之就是穷人的味道，还有去雯光丈夫身下拿钥匙是对金基泽和雯光丈夫身上的味道的鄙夷，可是这个原因真的站不住脚，即使是同样经济水平，如果身上有比较重的异味，背后讨论下，或者闻到了比较重的味道，有不适的表情和动作很正常吧，像雯光丈夫，在地下室里呆了那么久，身上有异味并且比较重太正常了，就跟在厕所呆久了不会觉得味道大，但是从没味道的地方一进有点味道的厕所就会觉得异样，略尴尬的理由；再说忠淑呢，感觉是太厉害了，能胜任这么一家有钱人的各种挑剔的饮食口味要求的保姆职位，也是让人看懵逼了，看到了不禁想到一个问题，这家人开头是那么地穷，不堪，突然转变成这么地像骗子家族，如果有这么好的骗人能力，应该不会到这种地步吧，如果真的是那么穷，没能力，没志气，又怎么会突然变成这么厉害呢，一家人各司其职，把富人家唬得团团转，而这个前提是，这些人的确能胜任这四个位置，这就是我非常不能理解的点。

然后说回这个标题，寄生虫，不知道是不是翻译过来不准确，如果真的是叫寄生虫的话，这个寄生虫智商未免也太低了，没有像新冠那样机制，致死率低一点，传染能力强一点，潜伏期也能传染，这个寄生虫第一次受到免疫系统的攻击就自爆了；还有呢，作为一个社会比较低层的打工者，乡下人，对这个审题也是不太审的清，是指这一家人是社会的寄生虫，不思进取，并且死的应该，富人是傻白甜，又有钱又善良，这是给有钱人洗地了还是啥，这个奥斯卡真不知道是怎么得的，总觉得奥斯卡，甚至低一点，豆瓣，得奖的，评分高的都是被一群“精英党”把持的，有黑人主角的，得分高；有同性恋的，得分高；结局惨的，得分高；看不懂的，得分高；就像肖申克的救赎，真不知道是哪里好了，最近看了关于明朝那些事的三杨，杨溥的经历应该比这个厉害吧，可是外国人看不懂，就像外国人不懂中国为什么有反分裂国家法，经历了鸦片战争，八国联军，抗日战争等等，其实跟外国对于黑人的权益的问题，因为有南北战争，所以极度重视这个问题，相应的中国也有自己的历史，请理解。

简而言之我对寄生虫的评分大概 5~6 分吧。

## gogs使用webhook部署react单页应用

Posted on 2020-02-22 Edited on 2020-02-23 In 持续集成 Views: Disqus:

众所周知，我是个前端彩笔，但是也想做点自己可以用的工具页面，所以就让朋友推荐了蚂蚁出品的 ant design，说基本可以直接 ctrl-c ctrl-v，实测对我这种来说还是有点难的，不过也能写点，但是现在碰到的问题是怎么部署到自己的服务器上去
用 ant design 写的是个单页应用，实际来说就是一个 html 加 css 跟 js，最初的时候是直接 build 完就 scp 上去，也考虑过 rsync 之类的，但是都感觉不够自动化，正好自己还没这方面的经验就想折腾下，因为我自己搭的仓库应用是 gogs，搜了一下主要是跟 drones 配合做 ci/cd，研究了一下发现其实这个事情没必要这么搞(PS：drone 也不好用)，整个 hook 就可以了, 但是实际上呢，这东西也不是那么简单
首先是需要在服务器上装 webhook，这个我一开始用 snap 安装，但是出现问题，run 的时候会出现后面参数带的 hooks.json 文件找不到，然后索性就直接 github 上下最新版，放 /usr/local/bin 了，webhook 的原理呢其实也比较简单，就是起一个 http 服务，通过 post 请求调用，解析下参数，如果跟配置的参数一致，就调用对应的命令或者脚本。

## 配置 hooks.json

webhook 的配置，需要的两个文件，一个是 hooks.json，这个是 webhook 服务的配置文件，像这样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52

    [
      {
        "id": "redeploy-app",
        "execute-command": "/opt/scripts/redeploy.sh",
        "command-working-directory": "/opt/scripts",
        "pass-arguments-to-command":
        [
          {
            "source": "payload",
            "name": "head_commit.message"
          },
          {
            "source": "payload",
            "name": "pusher.name"
          },
          {
            "source": "payload",
            "name": "head_commit.id"
          }
        ],
        "trigger-rule":
        {
          "and":
          [
            {
              "match":
              {
                "type": "payload-hash-sha1",
                "secret": "your-github-secret",
                "parameter":
                {
                  "source": "header",
                  "name": "X-Hub-Signature"
                }
              }
            },
            {
              "match":
              {
                "type": "value",
                "value": "refs/heads/master",
                "parameter":
                {
                  "source": "payload",
                  "name": "ref"
                }
              }
            }
          ]
        }
      }
    ]

这是个跟 github搭配的示例，首先 id 表示的是这个对应 hook 的识别 id，也可以看到这个 hooks.json 的结构是这样的一个数组，然后就是要执行的命令和命令执行的参数，值得注意的是这个`trigger-rule`,就是请求进来了回去匹配里面的，比如前一个是一个加密的，放在请求头里，第二个 match 表示请求里的 ref 是个 master 分支，就可以区分分支进行不同操作，但是前面的加密配合 gogs 使用的时候有个问题(PS: webhook 的文档是真的烂)，gogs 设置 webhook 的加密是用的

> 密钥文本将被用于计算推送内容的 SHA256 HMAC 哈希值，并设置为 `X-Gogs-Signature` 请求头的值。

这种加密方式，所以 webhook 的这个示例的加密方式不行，但这货的文档里居然没有说明支持哪些加密，神TM，后来还是翻 issue 翻到了, 需要使用这个`payload-hash-sha256`

## 执行脚本 redeploy.sh

脚本类似于这样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    #!/bin/bash -e

    function cleanup {
          echo "Error occoured"
    }
    trap cleanup ERR

    commit_message=$1 # head_commit.message
    pusher_name=$2 # pusher.name
    commit_id=$3 # head_commit.id

    cd ~/do-react-example-app/
    git pull origin master
    yarn && yarn build

就是简单的拉代码，然后构建下，真实使用时可能不是这样，因为页面会部署在 nginx 的作用目录，还需要 rsync 过去，这部分可能还涉及到两个问题第一个是使用 rsync 还是其他的 cp，不过这个无所谓；第二个是目录权限的问题，以我的系统ubuntu 为例，默认用户是 ubuntu，nginx 部署的目录是 www，所以需要切换用户等操作，一开始是想用在shell 文件中直接写了密码，但是不知道咋传，查了下是类似于这样 `echo "passwd" | sudo -S cmd`，通过管道命令往后传，然后就是这个`-S`, 参数的解释是`-S, --stdin read password from standard input`,但是这样么也不是太安全的赶脚，又看了下还有两种方法，

- 就是给root 设置一个不需要密码的命令类似于这样，

      1
      2

      myusername ALL = (ALL) ALL
      myusername ALL = (root) NOPASSWD: /path/to/my/program

- 另一种就是把默认用户跟 root 设置成同一个 group 的

## 使用

真正实操的时候其实还有不少问题，首先运行 webhook 就碰到了我前面说的，使用 snap 运行的时候会找不到前面的 hooks.json配置文件，执行`snap run webhook -hooks /opt/hooks/hooks.json -verbose`就碰到下面的`couldn't load hooks from file! open /opt/hooks/hooks.json: no such file or directory`，后来直接下了个官方最新的 release，就直接执行 `webhook -hooks /opt/hooks/hooks.json -verbose` 就可以了，然后是前面的示例配置文件里的几个参数，比如`head_commit.message` 其实 gogs 推过来的根本没这玩意，而且都是数组，不知道咋取，烂文档，不过总比搭个 drone 好一点就忍了。补充一点就是在 debug 的时候需要看下问题出在哪，看看脚本有没有执行，所以需要在前面的 json 里加这个参数`"include-command-output-in-response": true`, 就能输出来脚本执行结果

## Maven实用小技巧

Posted on 2020-02-16 In Java , Maven Views: Disqus:

Maven 翻译为”专家”、”内行”，是 Apache 下的一个纯 Java 开发的开源项目。基于项目对象模型（缩写：POM）概念，Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。

Maven 是一个项目管理工具，可以对 Java 项目进行构建、依赖管理。

Maven 也可被用于构建和管理各种项目，例如 C#，Ruby，Scala 和其他语言编写的项目。Maven 曾是 Jakarta 项目的子项目，现为由 Apache 软件基金会主持的独立 Apache 项目。

maven也是我们日常项目中实用的包管理工具，相比以前需要用把包下载下来，放进 lib 中，在平时工作中使用的话，其实像 idea 这样的 ide 工具都会自带 maven 工具和插件

### maven的基本操作

- `mvn -v`
  查看 maven 信息
- `mvn compile`
  将 Java 编译成 class 文件
- `mvn test`
  执行 test 包下的测试用例
- `mvn package`
  将项目打成 jar 包
- `mvn clean`
  删除package 在 target 目录下面打出来的 jar 包和 target 目录
- `mvn install`
  将打出来的 jar 包复制到 maven 的本地仓库里
- `mvn deploy`
  将打出来的 jar 包上传到远程仓库里

### 与 composer 对比

因为我也是个 PHP 程序员，所以对比一下两种语言，很容易想到在 PHP 的 composer 跟 Java 的 maven 是比较类似的作用，有一点两者是非常相似的，就是原仓库都是因为某些原因连接拉取都会很慢，所以像 composer 会有一些国内源，前阵子阿里也出了一个，类似的 maven 一般也会使用阿里的镜像仓库，通过在 setting.xml 文件中的设置

    1
    2
    3
    4
    5
    6
    7
    8

    <mirrors>
        <mirror>
            <id>aliyun</id>
            <name>aliyun maven</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <mirrorOf>central</mirrorOf>
        </mirror>
    </mirrors>

这算是个尴尬的共同点，然后因为 PHP 是解释型脚本语言，所以 php 打出来的 composer 包其实就是个 php 代码包，使用SPL Autoload等方式加载代码包，maven 包则是经过编译的 class 包，还有一点是 composer 也可以直接使用 github 地址作为包代码的拉取源，这点也是比较大的区别，maven使用 pom 文件管理依赖

### maven 的个人小技巧

- maven 拉取依赖时，同时将 snapshot 也更新了，就是 `mvn compile`加个`-U`参数，如果还不行就需要将本地仓库的 snapshot 删掉，
  这个命令的 help 命令解释是 -U,–update-snapshots Forces a check for missing releases and updated snapshots on
  remote repositories，这个在日常使用中还是很经常使用的
- maven 出现依赖冲突的时候的解决方法
  首先是依赖分析，使用`mvn dependency:tree`分析下依赖关系，如果要找具体某个包的依赖引用关系可以使用`mvn dependency:tree -Dverbose -Dincludes=org.springframework:spring-webmvc`命令进行分析，如果发现有冲突的依赖关系，本身 maven 中依赖引用有相对的顺序，大致来说是引用路径短的优先，pom 文件中定义的顺序优先，如果要把冲突的包排除掉可以在 pom 中用

      1
      2
      3
      4
      5
      6

      <exclusions>
        <exclusion>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </exclusion>
      </exclusions>

  将冲突的包排除掉
- maven 依赖的 jdk 版本管理
  前面介绍的`mvn -v`可以查看 maven 的安装信息
  比如

      1
      2
      3
      4
      5

      Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
      Maven home: /usr/local/Cellar/maven/3.6.3_1/libexec
      Java version: 1.8.0_201, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre
      Default locale: zh_CN, platform encoding: UTF-8
      OS name: "mac os x", version: "10.14.6", arch: "x86_64", family: "mac"

  这里可以看到用了 mac 自带的 jdk1.8，结合我之前碰到的一个问题，因为使用 homebrew 升级了 gradle，而 gradle 又依赖了 jdk13，因为这个 mvn 的 Java version 也变成 jdk13 了，然后 mvn 编译的时候出现了 `java.lang.ExceptionInInitializerError: com.sun.tools.javac.code.TypeTags`这个问题，所以需要把这个版本给改回来，但是咋改呢，网上搜来的一大堆都是在 pom 文件里的
  source和 target 版本

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13

      <build>
        <plugins>
      <plugin>
      	<groupId>org.apache.maven.plugins</groupId>
      	<artifactId>maven-compiler-plugin</artifactId>
      	<configuration>
      		<source>1.8</source>
      		<target>1.8</target>
      		<encoding>UTF-8</encoding>
      	</configuration>
      </plugin>
        </plugins>
      <build>

  或者修改 maven 的 setting.xml中的

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13

      <profiles>
            <profile>
                <id>ngmm-nexus</id>
              <activation>
                <jdk>1.8</jdk>
              </activation>
              <properties>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
                <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
            </properties>
            </profile>
      </profiles>

  但是这些都没啥用啊，真正有办法的是建个 `.mavenrc`，这个顾名思义就是 maven 的资源文件，类似于 `.bashrc`和`.zshrc`，在里面添加 MAVEN_HOME 和 JAVA_HOME,然后执行 `source .mavenrc`就 OK 啦

## G1收集器概述

Posted on 2020-02-09 In Java , JVM , GC , C++ Views: Disqus:

G1: The Garbage-First Collector, 垃圾回收优先的垃圾回收器，目标是用户多核 cpu 和大内存的机器，最大的特点就是可预测的停顿时间，官方给出的介绍是提供一个用户在大的堆内存情况下一个低延迟表现的解决方案，通常是 6GB 及以上的堆大小，有低于 0.5 秒稳定的可预测的停顿时间。

这里主要介绍这个比较新的垃圾回收器，在 G1 之前的垃圾回收器都是基于如下图的内存结构分布，有新生代，老年代和永久代（jdk8 之前），然后G1 往前的那些垃圾回收器都有个分代，比如 serial，parallel 等，一般有个应用的组合，最初的 serial 和 serial old，因为新生代和老年代的收集方式不太一样，新生代主要是标记复制，所以有 eden 跟两个 survival区，老年代一般用标记整理方式，而 G1 对这个不太一样。
看一下 G1 的内存分布
可以看到这有很大的不同，G1 通过将内存分成大小相等的 region，每个region是存在于一个连续的虚拟内存范围，对于某个 region 来说其角色是类似于原来的收集器的Eden、Survivor、Old Generation，这个具体在代码层面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55

    // We encode the value of the heap region type so the generation can be
     // determined quickly. The tag is split into two parts:
     //
     //   major type (young, old, humongous, archive)           : top N-1 bits
     //   minor type (eden / survivor, starts / cont hum, etc.) : bottom 1 bit
     //
     // If there's need to increase the number of minor types in the
     // future, we'll have to increase the size of the latter and hence
     // decrease the size of the former.
     //
     // 00000 0 [ 0] Free
     //
     // 00001 0 [ 2] Young Mask
     // 00001 0 [ 2] Eden
     // 00001 1 [ 3] Survivor
     //
     // 00010 0 [ 4] Humongous Mask
     // 00100 0 [ 8] Pinned Mask
     // 00110 0 [12] Starts Humongous
     // 00110 1 [13] Continues Humongous
     //
     // 01000 0 [16] Old Mask
     //
     // 10000 0 [32] Archive Mask
     // 11100 0 [56] Open Archive
     // 11100 1 [57] Closed Archive
     //
     typedef enum {
       FreeTag               = 0,

       YoungMask             = 2,
       EdenTag               = YoungMask,
       SurvTag               = YoungMask + 1,

       HumongousMask         = 4,
       PinnedMask            = 8,
       StartsHumongousTag    = HumongousMask | PinnedMask,
       ContinuesHumongousTag = HumongousMask | PinnedMask + 1,

       OldMask               = 16,
       OldTag                = OldMask,

       // Archive regions are regions with immutable content (i.e. not reclaimed, and
       // not allocated into during regular operation). They differ in the kind of references
       // allowed for the contained objects:
       // - Closed archive regions form a separate self-contained (closed) object graph
       // within the set of all of these regions. No references outside of closed
       // archive regions are allowed.
       // - Open archive regions have no restrictions on the references of their objects.
       // Objects within these regions are allowed to have references to objects
       // contained in any other kind of regions.
       ArchiveMask           = 32,
       OpenArchiveTag        = ArchiveMask | PinnedMask | OldMask,
       ClosedArchiveTag      = ArchiveMask | PinnedMask | OldMask + 1
     } Tag;

`hotspot/share/gc/g1/heapRegionType.hpp`

当执行垃圾收集时，G1以类似于CMS收集器的方式运行。 G1执行并发全局标记阶段，以确定整个堆中对象的存活性。标记阶段完成后，G1知道哪些region是基本空的。它首先收集这些region，通常会产生大量的可用空间。这就是为什么这种垃圾收集方法称为“垃圾优先”的原因。顾名思义，G1将其收集和压缩活动集中在可能充满可回收对象（即垃圾）的堆区域。 G1使用暂停预测模型来满足用户定义的暂停时间目标，并根据指定的暂停时间目标选择要收集的区域数。

由G1标识为可回收的区域是使用撤离的方式(Evacuation)。 G1将对象从堆的一个或多个区域复制到堆上的单个区域，并在此过程中压缩并释放内存。撤离是在多处理器上并行执行的，以减少暂停时间并增加吞吐量。因此，对于每次垃圾收集，G1都在用户定义的暂停时间内连续工作以减少碎片。这是优于前面两种方法的。 CMS（并发标记扫描）垃圾收集器不进行压缩。 ParallelOld垃圾回收仅执行整个堆压缩，这导致相当长的暂停时间。

需要重点注意的是，G1不是实时收集器。它很有可能达到设定的暂停时间目标，但并非绝对确定。 G1根据先前收集的数据，估算在用户指定的目标时间内可以收集多少个区域。因此，收集器具有收集区域成本的合理准确的模型，并且收集器使用此模型来确定要收集哪些和多少个区域，同时保持在暂停时间目标之内。

注意：G1同时具有并发（与应用程序线程一起运行，例如优化，标记，清理）和并行（多线程，例如stw）阶段。Full GC仍然是单线程的，但是如果正确调优，您的应用程序应该可以避免Full GC。

在前面那篇中在代码层面简单的了解了这个可预测时间的过程，这也是 G1 的一大特点。