面试官Q1:请问为什么String用"+"拼接字符串效率低下,最好能从JVM角度谈谈吗?

对于这个问题,我们先来看看如下代码:

public class StringTest {
public static void main(String[] args) {
String a = "abc";
String b = "def";
String c = a + b;
String d = "abc" + "def";
System.out.Println(c);
System.out.Println(d);
}
}

打印结果:

abcdef
abcdef

从上面代码示例中,我们看到两种方式拼接的字符串打印的结果是一样的。但这只是表面上的,实际内部运行不一样。

两者究竟有什么不一样?

为了看到两者的不同,对代码做如下调整:

public class StringTest {
public static void main(String[] args) {
String a = "abc";
String b = "def";
String c = a + b;
System.out.Println(c);
}
}

我们看看编译完成后它是什么样子:

C:\Users\GRACE\Documents>javac StringTest.java
2C:\Users\GRACE\Documents>javap -verbose StringTest
3Classfile /C:/Users/GRACE/Documents/StringTest.class
4 Last modified 2018-7-21; size 607 bytes
5 MD5 checksum a2729f11e22d7e1153a209e5ac968b98
6 Compiled from "StringTest.java"public class StringTest
8 minor version: 0
9 major version: 52
10 flags: ACC_PUBLIC, ACC_SUPER
11Constant pool:
12 #1 = Methodref #11.#20 // java/lang/Object."<init>":()V
13 #2 = String #21 // abc
14 #3 = String #22 // def
15 #4 = Class #23 // java/lang/StringBuilder
16 #5 = Methodref #4.#20 // java/lang/StringBuilder."<init>":()V
17 #6 = Methodref #4.#24 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18 #7 = Methodref #4.#25 // java/lang/StringBuilder.toString:()Ljava/lang/String;
19 #8 = Fieldref #26.#27 // java/lang/System.out:Ljava/io/PrintStream;
20 #9 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V
21 #10 = Class #30 // StringTest
22 #11 = Class #31 // java/lang/Object
23 #12 = Utf8 <init>
24 #13 = Utf8 ()V
25 #14 = Utf8 Code
26 #15 = Utf8 LineNumberTable
27 #16 = Utf8 main
28 #17 = Utf8 ([Ljava/lang/String;)V
29 #18 = Utf8 SourceFile
30 #19 = Utf8 StringTest.java
31 #20 = NameAndType #12:#13 // "<init>":()V
32 #21 = Utf8 abc
33 #22 = Utf8 def
34 #23 = Utf8 java/lang/StringBuilder
35 #24 = NameAndType #32:#33 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36 #25 = NameAndType #34:#35 // toString:()Ljava/lang/String;
37 #26 = Class #36 // java/lang/System
38 #27 = NameAndType #37:#38 // out:Ljava/io/PrintStream;
39 #28 = Class #39 // java/io/PrintStream
40 #29 = NameAndType #40:#41 // println:(Ljava/lang/String;)V
41 #30 = Utf8 StringTest
42 #31 = Utf8 java/lang/Object
43 #32 = Utf8 append
44 #33 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
45 #34 = Utf8 toString
46 #35 = Utf8 ()Ljava/lang/String;
47 #36 = Utf8 java/lang/System
48 #37 = Utf8 out
49 #38 = Utf8 Ljava/io/PrintStream;
50 #39 = Utf8 java/io/PrintStream
51 #40 = Utf8 println
52 #41 = Utf8 (Ljava/lang/String;)V
53{
54 public StringTest();
55 descriptor: ()V
56 flags: ACC_PUBLIC
57 Code:
58 stack=1, locals=1, args_size=1
59 0: aload_0
60 1: invokespecial #1 // Method java/lang/Object."<init>":()V
61 4: return
62 LineNumberTable:
63 line 1: 0
64
65 public static void main(java.lang.String[]);
66 descriptor: ([Ljava/lang/String;)V
67 flags: ACC_PUBLIC, ACC_STATIC
68 Code:
69 stack=2, locals=4, args_size=1
70 0: ldc #2 // String abc
71 2: astore_1
72 3: ldc #3 // String def
73 5: astore_2
74 6: new #4 // class java/lang/StringBuilder
75 9: dup
76 10: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
77 13: aload_1
78 14: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
79 17: aload_2
80 18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
81 21: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
82 24: astore_3
83 25: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
84 28: aload_3
85 29: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
86 32: return
87 LineNumberTable:
88 line 3: 0
89 line 4: 3
90 line 5: 6
91 line 6: 25
92 line 7: 32
93}
94SourceFile: "StringTest.java"

首先看到使用了一个指针指向一个常量池中的对象内容为“abc”,而另一个指针指向“def”,此时通过new申请了一个StringBuilder,然后调用这个StringBuilder的初始化方法;然后分别做了两次append操作,然后最后做一个toString()操作;可见String的+在编译后会被编译为StringBuilder来运行,我们知道这里做了一个new StringBuilder的操作,并且做了一个toString的操作,如果你对JVM有所了解,凡是new出来的对象绝对不会放在常量池中,toString会发生一次内容拷贝,但是也不会在常量池中,所以在这里常量池String+常量池String放在了堆中。

我们再来看看另外一种情况,用同样的方式来看看结果是什么:

代码如下:

public class StringTest {
public static void main(String[] args) {
String c = "abc" + "def";
System.out.println(c);
}
}

我们也来看看它编译完成后是什么样子:

C:\Users\GRACE\Documents>javac StringTest.java
2
3C:\Users\GRACE\Documents>javap -verbose StringTest
4Classfile /C:/Users/GRACE/Documents/StringTest.class
5 Last modified 2018-7-21; size 426 bytes
6 MD5 checksum c659d48ff8aeb45a3338dea5d129f593
7 Compiled from "StringTest.java"public class StringTest
9 minor version: 0
10 major version: 52
11 flags: ACC_PUBLIC, ACC_SUPER
12Constant pool:
13 #1 = Methodref #6.#15 // java/lang/Object."<init>":()V
14 #2 = String #16 // abcdef
15 #3 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;
16 #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
17 #5 = Class #21 // StringTest
18 #6 = Class #22 // java/lang/Object
19 #7 = Utf8 <init>
20 #8 = Utf8 ()V
21 #9 = Utf8 Code
22 #10 = Utf8 LineNumberTable
23 #11 = Utf8 main
24 #12 = Utf8 ([Ljava/lang/String;)V
25 #13 = Utf8 SourceFile
26 #14 = Utf8 StringTest.java
27 #15 = NameAndType #7:#8 // "<init>":()V
28 #16 = Utf8 abcdef
29 #17 = Class #23 // java/lang/System
30 #18 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
31 #19 = Class #26 // java/io/PrintStream
32 #20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
33 #21 = Utf8 StringTest
34 #22 = Utf8 java/lang/Object
35 #23 = Utf8 java/lang/System
36 #24 = Utf8 out
37 #25 = Utf8 Ljava/io/PrintStream;
38 #26 = Utf8 java/io/PrintStream
39 #27 = Utf8 println
40 #28 = Utf8 (Ljava/lang/String;)V
41{
42 public StringTest();
43 descriptor: ()V
44 flags: ACC_PUBLIC
45 Code:
46 stack=1, locals=1, args_size=1
47 0: aload_0
48 1: invokespecial #1 // Method java/lang/Object."<init>":()V
49 4: return
50 LineNumberTable:
51 line 1: 0
52
53 public static void main(java.lang.String[]);
54 descriptor: ([Ljava/lang/String;)V
55 flags: ACC_PUBLIC, ACC_STATIC
56 Code:
57 stack=2, locals=2, args_size=1
58 0: ldc #2 // String abcdef
59 2: astore_1
60 3: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
61 6: aload_1
62 7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
63 10: return
64 LineNumberTable:
65 line 3: 0
66 line 4: 3
67 line 5: 10
68}
69SourceFile: "StringTest.java"

这一次编译完后的代码比前面少了很多,而且,仔细看,你会发现14行处,编译的过程中直接变成了"abcdef",这是为什么呢?因为当发生“abc” + “def”在同一行发生时,JVM在编译时就认为这个加号是没有用处的,编译的时候就直接变成

String d = "abcdef";

同理如果出现:String a =“a” + 1,编译时候就会变成:String a = “a1″;

再补充一个例子:

final String a = "a";
final String b = "ab";
String c = a + b;

在编译时候,c部分会被编译为:String c = “aab”;但是如果a或b有任意一个不是final的,都会new一个新的对象出来;其次再补充下,如果a和b,是某个方法返回回来的,不论方法中是final类型的还是常量什么的,都不会被在编译时将数据编译到常量池,因为编译器并不会跟踪到方法体里面去看你做了什么,其次只要是变量就是可变的,即使你认为你看到的代码是不可变的,但是运行时是可以被切入的。

那么效率问题从何说起?

那说了这么多,也没看到有说效率方面的问题呀?

其实上面两个例子,连接字符串行表达式很简单,那么"+"和StringBuilder基本是一样的,但如果结构比较复杂,如使用循环来连接字符串,那么产生的Java Byte Code就会有很大的区别。我们再来看看下面一段代码:

import java.util.*;
public class StringTest {
public static void main(String[] args){
String s = "";
Random rand = new Random();
for (int i = 0; i < 10; i++){
s = s + rand.nextInt(1000) + " ";
}
System.out.println(s);
}
}

上面代码反编译后的结果如下:

C:\Java\jdk1.8.0_171\bin>javap -c E:\StringTest.class
Picked up _JAVA_OPTIONS: -Xmx512M
Compiled from "StringTest.java"
public class StringTest {
public StringTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return public static void main(java.lang.String[]);
Code:
//String s = "";
0: ldc #16 // String
2: astore_1
//Random rand = new Random();
3: new #18 // class java/util/Random
6: dup
7: invokespecial #20 // Method java/util/Random."<init>":()V
10: astore_2
//StringBuilder result = new StringBuilder();
11: iconst_0
12: istore_3
13: goto 49
//s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").toString();
16: new #21 // class java/lang/StringBuilder
19: dup
20: aload_1
21: invokestatic #23 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
24: invokespecial #29 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
27: aload_2
28: sipush 1000
31: invokevirtual #32 // Method java/util/Random.nextInt:(I)I
34: invokevirtual #36 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
37: ldc #40 // String
39: invokevirtual #42 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
42: invokevirtual #45 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
45: astore_1
46: iinc 3, 1
49: iload_3
50: bipush 10
52: if_icmplt 16
//System.out.println(s);
55: getstatic #49 // Field java/lang/System.out:Ljava/io/PrintStream;
58: aload_1
59: invokevirtual #55 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
62: return
}

我们可以看到,虽然编译器将"+"转换成了StringBuilder,但创建StringBuilder对象的位置却在for语句内部。这就意味着每执行一次循环,就会创建一个StringBuilder对象(对于本例来说,是创建了10个StringBuilder对象),虽然Java有垃圾回收器,但这个回收器的工作时间是不定的。如果不断产生这样的垃圾,那么仍然会占用大量的资源。解决这个问题的方法就是在程序中直接使用StringBuilder来连接字符串,代码如下:

import java.util.Random;
public class StringTest {
public static void main(String[] args) {
Random rand = new Random();
StringBuilder result = new StringBuilder();
for (int i = 0; i < 10; i++) {
result.append(rand.nextInt(1000));
result.append(" ");
}
System.out.println(result.toString());
}
}

上面代码反编译后的结果如下:

C:\Java\jdk1.8.0_171\bin>javap -c E:\Dubbo\Demo\bin\StringTest.class
Picked up _JAVA_OPTIONS: -Xmx512M
Compiled from "StringTest.java"
public class StringTest {
public StringTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return public static void main(java.lang.String[]);
Code:
//Random rand = new Random();
0: new #16 // class java/util/Random
3: dup
4: invokespecial #18 // Method java/util/Random."<init>":()V
7: astore_1
//StringBuilder result = new StringBuilder();
8: new #19 // class java/lang/StringBuilder
11: dup
12: invokespecial #21 // Method java/lang/StringBuilder."<init>":()V
15: astore_2
//for(int i = 0; i < 10; i++)
16: iconst_0
17: istore_3
18: goto 43
//result.append(rand.nextInt(1000));
21: aload_2
22: aload_1
23: sipush 1000
26: invokevirtual #22 // Method java/util/Random.nextInt:(I)I
29: invokevirtual #26 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
32: pop
//result.append(" ");
33: aload_2
34: ldc #30 // String
36: invokevirtual #32 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
39: pop
40: iinc 3, 1
43: iload_3
44: bipush 10
46: if_icmplt 21
//System.out.println(result.toString());
49: getstatic #35 // Field java/lang/System.out:Ljava/io/PrintStream;
52: aload_2
53: invokevirtual #41 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
56: invokevirtual #45 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
59: return
}

从上面的反编译结果可以看出,创建StringBuilder的代码被放在了for语句外。虽然这样处理在源程序中看起来复杂,但却换来了更高的效率,同时消耗的资源也更少了。

所以,从上述几个例子中我们得出的结论是:String采用连接运算符(+)效率低下,都是上述循环、大批量数据情况造成的,每做一次"+"就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后append字符串,如此循环直至结束。如果我们直接采用StringBuilder对象进行append的话,我们可以节省创建和销毁对象的时间。如果只是简单的字面量拼接或者很少的字符串拼接,性能都是差不多的。

C:\Users\GRACE\Documents>javac StringTest.java
2C:\Users\GRACE\Documents>javap -verbose StringTest
3Classfile /C:/Users/GRACE/Documents/StringTest.class
4  Last modified 2018-7-21; size 607 bytes
5  MD5 checksum a2729f11e22d7e1153a209e5ac968b98
6  Compiled from "StringTest.java"
7public class StringTest
8  minor version: 0
9  major version: 52
10  flags: ACC_PUBLIC, ACC_SUPER
11Constant pool:
12   #1 = Methodref          #11.#20        // java/lang/Object."<init>":()V
13   #2 = String             #21            // abc
14   #3 = String             #22            // def
15   #4 = Class              #23            // java/lang/StringBuilder
16   #5 = Methodref          #4.#20         // java/lang/StringBuilder."<init>":()V
17   #6 = Methodref          #4.#24         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18   #7 = Methodref          #4.#25         // java/lang/StringBuilder.toString:()Ljava/lang/String;
19   #8 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
20   #9 = Methodref          #28.#29        // java/io/PrintStream.println:(Ljava/lang/String;)V
21  #10 = Class              #30            // StringTest
22  #11 = Class              #31            // java/lang/Object
23  #12 = Utf8               <init>
24  #13 = Utf8               ()V
25  #14 = Utf8               Code
26  #15 = Utf8               LineNumberTable
27  #16 = Utf8               main
28  #17 = Utf8               ([Ljava/lang/String;)V
29  #18 = Utf8               SourceFile
30  #19 = Utf8               StringTest.java
31  #20 = NameAndType        #12:#13        // "<init>":()V
32  #21 = Utf8               abc
33  #22 = Utf8               def
34  #23 = Utf8               java/lang/StringBuilder
35  #24 = NameAndType        #32:#33        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36  #25 = NameAndType        #34:#35        // toString:()Ljava/lang/String;
37  #26 = Class              #36            // java/lang/System
38  #27 = NameAndType        #37:#38        // out:Ljava/io/PrintStream;
39  #28 = Class              #39            // java/io/PrintStream
40  #29 = NameAndType        #40:#41        // println:(Ljava/lang/String;)V
41  #30 = Utf8               StringTest
42  #31 = Utf8               java/lang/Object
43  #32 = Utf8               append
44  #33 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
45  #34 = Utf8               toString
46  #35 = Utf8               ()Ljava/lang/String;
47  #36 = Utf8               java/lang/System
48  #37 = Utf8               out
49  #38 = Utf8               Ljava/io/PrintStream;
50  #39 = Utf8               java/io/PrintStream
51  #40 = Utf8               println
52  #41 = Utf8               (Ljava/lang/String;)V
53{
54  public StringTest();
55    descriptor: ()V
56    flags: ACC_PUBLIC
57    Code:
58      stack=1, locals=1, args_size=1
59         0: aload_0
60         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
61         4: return
62      LineNumberTable:
63        line 1: 0
64
65  public static void main(java.lang.String[]);
66    descriptor: ([Ljava/lang/String;)V
67    flags: ACC_PUBLIC, ACC_STATIC
68    Code:
69      stack=2, locals=4, args_size=1
70         0: ldc           #2                  // String abc
71         2: astore_1
72         3: ldc           #3                  // String def
73         5: astore_2
74         6: new           #4                  // class java/lang/StringBuilder
75         9: dup
76        10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
77        13: aload_1
78        14: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
79        17: aload_2
80        18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
81        21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
82        24: astore_3
83        25: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
84        28: aload_3
85        29: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
86        32: return
87      LineNumberTable:
88        line 3: 0
89        line 4: 3
90        line 5: 6
91        line 6: 25
92        line 7: 32
93}
94SourceFile: "StringTest.java"

String拼接字符串效率低,你知道原因吗?的更多相关文章

  1. c++拼接字符串效率比较(+=、append、stringstream、sprintf)

    转自:http://www.cnblogs.com/james6176/p/3222671.html c++拼接字符串效率比较(+=.append.stringstream.sprintf) 最近写的 ...

  2. C#入门篇6-4:字符串操作 string分割字符串效率比较

    //分割字符串效率比较 public static void Fund() { //1.用string.Split方法 //a.字节数组: //625毫秒/百万次 string str1 = &quo ...

  3. java 几种拼接字符串的效率问题

    拼接字符串,大致有3个class可以用,他们是String, StringBuffer,StringBuilder, StringBuilder是1.5中来代替StringBuffer的.检验方法如下 ...

  4. 昨天还在for循环里写加号拼接字符串的那个同事,今天已经不在了

    引言 都说 StringBuilder 在处理字符串拼接上效率要强于 String,但有时候我们的理解可能会存在一定的偏差.最近我在测试数据导入效率的时候就发现我以前对 StringBuilder 的 ...

  5. 解决:Oracle 异常 ORA-01861: literal does not match format string(字符串格式不匹配)

    今天在写存储过程的时候,老是报一个错.如下,记录下来,以供日后查阅. 报错:  Oracle 异常 ORA-01861: literal does not match format string(字符 ...

  6. C#的StringBuilder 以及string字符串拼接的效率对照

    今天公司一个做Unity3d的人在说字符串拼接的一个效率问题,他觉得string拼接会产生新的一个内存空间,假设不及时回收会产生大量的碎片,特别是在Unity3d这样一个Updata环境下,由于每一帧 ...

  7. Lua大量字符串拼接方式效率对比及原因分析

    Lua大量字符串拼接方式效率对比及原因分析_AaronChan的博客-CSDN博客_lua字符串拼接消耗 https://blog.csdn.net/qq_26958473/article/detai ...

  8. 谈JavaScript组合拼接字符串的效率 --转载

    JavaScript组合拼接字符串的效率.在脚本开发过程中,经常会按照某个规则,组合拼接出一个大字符串进行输出.比如写脚本控件时控制整个控件的外观的HTML标签输出,比如AJAX里得到服务器端的回传值 ...

  9. Java中测试StringBuilder、StringBuffer、String在字符串拼接上的性能

    应一个大量字符串拼接的任务 测试一下StringBuilder.StringBuffer.String在操作字符串拼接时候的性能 性能上理论是StringBuilder  >  StringBu ...

随机推荐

  1. [颜色知识] 潘通色卡、CMYK、RGB、 ARGB...

    潘通色卡C结尾的色号都是RGB色系的,而CMYK是印刷系的,两者不能通用的,所以,不能完全对等进行转换 Pantone Colors [色卡]PANTONE潘通色卡C面颜色http://blog.si ...

  2. android 换行符(\n) 在TextView中显示不正常的问题

    问题描述 在Android开发,使用TextView设置换行的时候,会有这种情况: 1.如果直接在XML文件中写入"aaaaa\nbbbb"可以换行,显示为: aaaaabbbbb ...

  3. 高版本js实现live

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. python全栈开发day23-面向对象高级:反射(getattr、hasattr、setattr、delattr)、__call__、__len__、__str__、__repr__、__hash__、__eq__、isinstance、issubclass

    一.今日内容总结 1.反射 使用字符串数据类型的变量名来操作一个变量的值. #使用反射获取某个命名空间中的值, #需要 #有一个变量指向这个命名空间 #字符串数据类型的名字 #再使用getattr获取 ...

  5. qrcodebox 面向移动设备的二维码弹出框

    qrcodebox 面向移动设备的二维码弹出框 qrcodebox 简介 qrcode box,一个小小的二维码工具,通过调用它,可以在页面中间弹出一个二维码窗口,主要是面向移动设备的,对于PC端浏览 ...

  6. 6-5 巡逻机器人 uva1600

    一开始按照标准bfs来写  标记为二维数组 后来按照三维数组写过了    ps大部分bfs都不会是二维数组搞定!!! 其中有一个bug弄了半个小时... 一开始我是先判断!vis[x][y][v.c] ...

  7. Storm消息可靠机制

    一:介绍 1.介绍 默认情况是,Spout每获取一条数据,封装后发送给后面的组件,不再管后面是否处理完成或成功接收,不再考虑. 这种的情况是不用太精确,没有启用可靠性消息机制. 2.方面的体现 spo ...

  8. 阿里dubbo服务注册原理解析

           阿里分布式服务框架 dubbo现在已成为了外面很多中小型甚至一些大型互联网公司作为服务治理的一个首选或者考虑方案,相信大家在日常工作中或多或少都已经用过或者接触过dubbo了.但是我搜了 ...

  9. nc高级应用

    参数介绍: nc.exe -h即可看到各参数的使用方法. 基本格式:nc?[-options]?hostname?port[s]?[ports]?... nc?-l?-p?port?[options] ...

  10. 012.Docker私有仓库多Harbor同步部署

    一 Harbor主从介绍 harbor官方默认提供主从复制的方案来解决镜像同步问题,通过复制的方式,我们可以实时将测试环境harbor仓库的镜像同步到生产环境harbor,类似于如下流程: Harbo ...