0%

编写测试程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- more -->
/**
jvm参数:
* -verbose:gc 输出打印详细的GC日志
* -Xms20M 堆空间最小值
* -Xmx20M 堆空间最大值
* -Xmn10M 堆空间新生代的大小
* -XX:+PrintGCDetails 打印GC的详细的信息
* -XX:SurvivorRatio=8 eden和survivor的比例是8:1
*/
public class MyTest1 {
public static void main(String[] args) {
int size = 1024 * 1024;
byte[] myAlloc1 = new byte[3 * size];
byte[] myAlloc2 = new byte[3 * size];
byte[] myAlloc3 = new byte[3 * size];
//byte[] myAlloc4 = new byte[3 * size];
System.out.println("hello world");
}
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
[GC (Allocation Failure) [PSYoungGen: 5431K->840K(9216K)] 5431K->3920K(19456K), 0.0027228 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
hello world
Heap
PSYoungGen total 9216K, used 7304K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 78% used [0x00000000ff600000,0x00000000ffc50230,0x00000000ffe00000)
from space 1024K, 82% used [0x00000000ffe00000,0x00000000ffed2020,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 3080K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 30% used [0x00000000fec00000,0x00000000fef02010,0x00000000ff600000)
Metaspace used 3471K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 384K, capacity 388K, committed 512K, reserved 1048576K

** [GC (Allocation Failure) [PSYoungGen: 5431K->840K(9216K)] 5431K->3920K(19456K), 0.0027228 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] **
GC:表示是minor gc,如果是full gc就会显示“FULL GC”
Allocation Failure:表示失败原因,这里指的是内存分配失败,导致的GC
PSYoungGen:Parallel Scavenge 在新生代的收集器。
5431K->840K(9216K):垃圾回收之前在新生代存活的对象占据的空间是5431K -> 垃圾回收之后在新生代存活对象所占据的容量是840K(新生代总的空间是9216K,即9M,一个eden是8M + 一个survivor是1M = 9M)
5431K->3920K(19456K): 执行GC之前总的堆里边存活对象占据的大小是5431K -> GC执行完之后整个堆存活对象占据的大小是3920K(整个堆的容量是19456K,即19M,新生代浪费了一个survivor,所以是20M - 1M = 19M)。
0.0027228 secs:指的是本次执行GC耗费的时间是 0.0027228秒。
[Times: user=0.00 sys=0.00, real=0.00 secs]:用户空间用了0.00秒,内核空间用了0.00秒,实际用的时间是0.00秒。

** PSYoungGen total 9216K, used 7304K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) **

Parallel Scavenge 在新生代,一共新生代的大小是9216K,已经使用了7304K。

** eden space 8192K, 78% used [0x00000000ff600000,0x00000000ffc50230,0x00000000ffe00000) **
eden空间是8192K,使用了78%的eden空间。

** from space 1024K, 82% used [0x00000000ffe00000,0x00000000ffed2020,0x00000000fff00000) **
from空间是1024K,使用了82%的from survivor空间。

** to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000) **
to空间是1024K,使用了0%的to survivor空间。

** ParOldGen total 10240K, used 3080K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) **
老年代的收集器Parallel old,parOld收集器,老年代占据的空间是10240K,即10M,已经使用了3080K。
5431K - 840K = 4591K //指的是新生代释放的空间
5431K - 3920K = 1511K //指的是整个堆释放的空间,1511K是真正从整个堆里边彻底消失的的空间。

4591K - 1511K = 3080K, 3080K就是ParOldGen已经使用的空间大小,新生代释放的空间4591K一部分是彻底从堆里边消失的,另一部分去了老年代,那我们用新生代释放的空间4591K减去彻底从堆里边释放的1511K剩下的就是从新生代晋升到老年代的对象,这部分晋升的对象占据的空间就是老年代的使用空间3080K。

打开注释的【byte[] myAlloc4 = new byte[3 * size];】运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
[GC (Allocation Failure) [PSYoungGen: 5431K->840K(9216K)] 5431K->3920K(19456K), 0.0028338 secs] [Times: user=0.08 sys=0.02, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 7222K->792K(9216K)] 10302K->10016K(19456K), 0.0051217 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 792K->0K(9216K)] [ParOldGen: 9224K->9894K(10240K)] 10016K->9894K(19456K), [Metaspace: 3438K->3438K(1056768K)], 0.0064424 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
hello world
Heap
PSYoungGen total 9216K, used 3501K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 42% used [0x00000000ff600000,0x00000000ff96b7e8,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 10240K, used 9894K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 96% used [0x00000000fec00000,0x00000000ff5a9a48,0x00000000ff600000)
Metaspace used 3460K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 383K, capacity 388K, committed 512K, reserved 1048576K

** [Full GC (Ergonomics) [PSYoungGen: 792K->0K(9216K)] [ParOldGen: 9224K->9894K(10240K)] 10016K->9894K(19456K), [Metaspace: 3438K->3438K(1056768K)], 0.0064424 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] **

Ergonomics:失败原因是Ergonomics。
PSYoungGen:新生代的收集器。
[PSYoungGen: 792K->0K(9216K)]:新生代收集之前对象占用的空间是792K,回收之后对象占用的空间是0K,新生代一共9216K(9M)。
[ParOldGen: 9224K->9894K(10240K)]:老年代的收集器ParOldGen,回收之前老年代对象占用空间是9224K,回收之后老年代对象占用空间是
9894K(没有减少反而增多,是因为新生代晋升到老年代的对象),老年代总共的大小是10240K(10M)。
10016K->9894K(19456K):整个堆空间里边回收之前对象占用的空间是10016K,回收之后对象占用的空间是9894K,整个堆大小是19456K。
[Metaspace: 3438K->3438K(1056768K)]:元空间回收之前对象占用空间是3438K,回收之后占用的空间是3438K,即没有变化,元空间总共大小是1056768K。
0.0064424 secs:本次 Full GC执行花费时间是 0.0064424 secs。

** ParOldGen total 10240K, used 9894K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) **
老年的收集器,总共空间是10240K,已经使用了9894K,和上面的Full GC收集完毕之后对象占用的空间9894K是一致的。

现在我们修改下程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* -verbose:gc 输出打印详细的GC日志
* -Xms20M 堆空间最小值
* -Xmx20M 堆空间最大值
* -Xmn10M 堆空间新生代的大小
* -XX:+PrintGCDetails 打印GC的详细的信息
* -XX:SurvivorRatio=8 eden和survivor的比例是8:1
*
* @author : CeaserWang
* @version : 1.0
* @since : 2019/5/12 14:26
*/
public class MyTest1 {
public static void main(String[] args) {
int size = 1024 * 1024;
byte[] myAlloc1 = new byte[3 * size];
byte[] myAlloc2 = new byte[3 * size];
byte[] myAlloc3 = new byte[4 * size];
byte[] myAlloc4 = new byte[4 * size];
System.out.println("hello world");
}
}

myAlloc3和myAlloc4改成4倍的size(之前是倍的size),然后运行程序:

1
2
3
4
5
6
7
8
9
10
11
[GC (Allocation Failure) [PSYoungGen: 5431K->872K(9216K)] 5431K->3952K(19456K), 0.0024476 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
hello world
Heap
PSYoungGen total 9216K, used 8360K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 91% used [0x00000000ff600000,0x00000000ffd50230,0x00000000ffe00000)
from space 1024K, 85% used [0x00000000ffe00000,0x00000000ffeda020,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 7176K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 70% used [0x00000000fec00000,0x00000000ff302020,0x00000000ff600000)
Metaspace used 3488K, capacity 4498K, committed 4864K, reserved 1056768K
class space used 387K, capacity 390K, committed 512K, reserved 1048576K

我们加大了内存申请的大小反而没有出现Full GC,如果是3倍的size会出现Full GC,这是什么原因?

myAlloc1、myAlloc2如果能在新生代存放,新生代是10M的空间,这时候myAlloc3来了,之前已被myAlloc1和myAlloc2占据了6M的新生代空间,
这次4M的myAlloc3肯定无法在新生代存放,这种情况JVM的策略是把myAlloc3直接放到老年代,myAlloc3加myAlloc4是8M空间,老年代是10M的
空间大小,是能够存放的。所以没有发生Full GC。

另外jdk1.8新生代和老年代默认收集器:
PSYoungGen: Parallel Scavenge(新生代垃圾收集器)
ParOldGen:Parallel Old (老年代垃圾收集器)

metaspace

jdk8当中class的元数据放在元空间里边,元空间是os的一部分内存,对元空间的管理会存在元空间不够会动态扩容,如果扩容还不够就会oomm异常,为了模拟这种错误,我们可以限制metaspace的大小,下面是

Read more »

数据卷

数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

Read more »

新建并启动

1
2
3
4
5
6
<!-- more -->
docker run ubuntu:14.04 /bin/echo 'Hello world'
Hello world

docker run -t -i ubuntu:14.04 /bin/bash
root@af8bae53bdd3:/#

-t:打开终端,让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上
-i:交互式,让容器的标准输入保持打开。在这种模式下,用户可以输入ls、pwd这类命令和os进行交互

启动已终止容器

可以利用 docker container start 命令,直接将一个已经终止的容器启动运行。

后台运行

更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。
此时,可以通过添加 -d 参数来实现。
example:不使用-d:

1
2
3
4
5
docker run ubuntu:17.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
hello world
hello world
hello world
hello world

使用了 -d 参数运行容器:

1
2
$ docker run -d ubuntu:17.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a

此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用docker logs/docker container logs 查看)。
注: 容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关。
使用 -d 参数启动后会返回一个唯一的 id,也可以通过 docker container ls 命令来查看容
器信息。

docker container logs

1
2
3
4
5
$ docker container logs [container ID or NAMES]
hello world
hello world
hello world
. . .

终止容器

docker container stop用来终止一个正在运行的容器,如果容器指定的app终止,那么容器也会立刻终止。
可以使用 docker container ls -a查看到终止的容器。
docker container start:将终止的容器重新运行。
docker container restart:将运行的容器终止,然后再重启。

进入容器

使用-d启动容器后,容器会在后台运行,这时候,我们想再次进入容器操作,就需要一些方式,下面介绍2种命令。

attach 命令

1
2
3
4
5
6
7
8
>docker run -dit  jdk8:v1
bba6e1a848711fd046a4a6d934ac9de0f58736bcade81e42d30ce05aa620ee30
>docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bba6e1a84871 jdk8:v1 "/bin/bash" 43 seconds ago Up 40 seconds suspicious_blackwell
>docker attach bba
[root@bba6e1a84871 /]# ls /
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

2-1
PS:使用attach退出终端后,容器也会终止,退出。

exec 命令

2-2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>docker run -dit jdk8:v1
15febd290b57ea968d233096e701b8f73bf11ca90db6692da33e185e8a016103

>docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
15febd290b57 jdk8:v1 "/bin/bash" 12 seconds ago Up 9 seconds goofy_banach
>docker exec -it 15f bash
[root@15febd290b57 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
[root@15febd290b57 /]# exit
exit

>docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
15febd290b57 jdk8:v1 "/bin/bash" About a minute ago Up About a minute goofy_banach

可以看到使用exec进入容器操作,然后退出终端后,容器并没有终止。这个是和attach的主要区别,也是推荐使用的。
docker exec -it 15f bash:bash指的是运行bash,exec要求至少2个参数,一个是容器的id,一个是运行的方式。

删除容器

可以使用 docker container rm 来删除一个处于终止状态的容器。例如
1
2
$ docker container rm trusting_newton
trusting_newton

清理所有处于终止状态的容器

用 docker container ls -a 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器。
2-3
同样的道理也可以清理镜像:
2-4