0%

对于web项目来说,打交道的不仅仅有后台,前台页面也是少不了的,而前台的页面js也常常是我们后台程序员必须要使用的语言, 今天说下项目中的js的组织方式。

文件函数型

所谓文件函数型是指所有的js的脚本都是中都是一个一个的方法,没有任何的封装,这也是传统的项目中常用的方法。如下:

file1.js

1
2
3
4
function add(a,b){
return a+b;
}

file2.js

1
2
3
4
function minutes(a,b){
return a-b;
}

当我们想使用个方法的时候只需要应用相应的js就行了,这样的缺点很明显,就是要时刻明确文件之间的相互依赖关系的顺序,不然就会导致程序无法正常的运行。 比如还是上面的两个js,我在file1.js中有家一段代码:

file1.js

1
2
3
4
5
6
7
8
function add(a,b){
return a+b;
}

function sum(a,b,c){
return add(a,minutes(b,c));
}


如果在引用的时候,先引用的file1.js则会导致files1.js中有方法没有找到。 如果项目足够的大,就纯粹的js的依赖关系就足以让我们焦头烂额。

这种方法还有很多潜在的风险,如果我在file1.js中有定义了一个minutes方法,这样file2.js中的方法就面临被覆盖的风险,所以这种布局的方式,不应该是项目的首选。

jquery扩展型

在web的项目中,在面对dom操作的时候,传统的js的过于繁杂,所以jquery的使用应该占了很大的一部分的比重(确切的说在MVVM框架流行前)。

阅读全文 »

一直都想写关于vim的使用教程,因为在很多的场景下不得不去使用vim去编辑文本,今天有时间就相关的常用的命令整理下(本文只适合入门的读者,想获得高级教程请止步) ,工具只要经常的使用自然就能熟能生巧。

vim概述

vim是linu下面常用的文本编辑工具,可以使用 vim -v 来查看有没有安装,如果没有安装可以使用 以下命令安装:

sudo apt-get install vim

vim 有两种模式,命令模式和插入模式。从字面的意思很容易理解两者的用的情景:

  • 命令模式:在这个情况下每一个字符都是一个命令 ,如果想转换成插入模式直接输入插入命令即可(a,A,i,I等)

  • 插入模式:可以正常的输入文本,使用esc可以切换成命令模式

vim 使用


打开、新建、保存、退出


阅读全文 »

有几天没有更新博客,现在已经是我的博客建立快200天了,在建立博客的过程中,也学到一些东西,同时也踩了一些坑,下面说说个人的经历:

seo相关

关于seo我了解的也是皮毛,收录也一直很低,中间有很长一段时间百度的收录基本上跌为0,可能是开始的时候使用了github Pages的原因,后来这个才知道gitpages对于百度的收录并不是很友好,在百度站长工具展现出很多拒绝访问,这个收录当然不会很高。

后来使用了code.net作为免费的云服务器,但是这个网站与github相比还是有点抠门的,对于免费的用户还有有一些限制的,比如项目最多建立5个,网站建立的时候还要保留对他网站的外链;空间也不大,但对于个人博客来说已经足够使用了,毕竟是个免费的,不能有太多过高的要求。

对于更好的能够被收录(个人博客虽然不能全部靠搜索引擎来获得流量,但也是一种引流的方式)个人有以下几点建议:

  1. 一定要是原创,不要做抄袭很伪原创的东西

  2. 使用sitemap.xml,这个对于搜索引擎来说是一个“康庄大道”,具体可以参见sitemap

  3. 使用robots.txt,这个是搜索引擎访问的第一个页面。具体robots

  4. 使用各个搜索引擎的push方法,对搜索引擎来说要更友好

  5. 对于关键字来说,要保证关键字不要太多,方向大体统一。

统计相关

对于统计来说,我本想用google的统计平台,但苦于没有稳定的翻墙网络,所以就使用了百度统计,功能也很好用,用起来也很顺手,唯一的就是没有能够获得当前所有访客的数据的接口。

对于上面的问题,我也找到了几个解决方案,这个更推荐使用51la统计,虽然界面不是很友好,但数据却很准确,一天真正的做到一个ip只会增加一次。

评论系统

阅读全文 »

实现简单的Map


前几天有想法弄懂HashMap的实现的原理,我自己也YY了一个想法去实现一个简单的Map, 代码如下:

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
public class KeyValuePair<K,V> {

public K Key;
public V Value;

public K getKey() {
return Key;
}

public void setKey(K key) {
Key = key;
}

public V getValue() {
return Value;
}

public void setValue(V value) {
Value = value;
}
}

```
然后使用List作为Container对数据进行存储,主体的内部实现原理如下:

```Java
public class MyMap<K, V> {
private List<KeyValuePair<K, V>> map;

public MyMap() {
map = new ArrayList<KeyValuePair<K, V>>();
}

public V put(K k, V v) {
KeyValuePair<K, V> keyValuePair = new KeyValuePair<K, V>();
keyValuePair.setKey(k);
keyValuePair.setValue(v);
map.add(keyValuePair);
return v;
}

public V get(K k) {
for (KeyValuePair pair : map) {
if (pair.getKey().equals(k)) {
return (V) pair.getValue();
}
}
return null;
}
}

虽然也能实现类似的效果,但我们可以看到这个的map的时间复杂度是O(n),当集合数量很大时,则效率可以的非常的糟糕,下面做一个对比的测试:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
@Test
public void MapTest(){

long start=System.currentTimeMillis();
MyMap<String,String> map =new MyMap();
for (int i=0;i<10000;i++){
map.put("Key"+i,"value"+i);
}
for (int i=0;i<10000;i++){
map.get("Key"+i);
}
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));

start=System.currentTimeMillis();
Map<String,String> hashMap =new HashMap<>();
for (int i=0;i<10000;i++){
hashMap.put("Key"+i,"value"+i);
}
for (int i=0;i<10000;i++){
hashMap.get("Key"+i);
}
end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
```

运行结果如下:

```cte
耗时:1815
耗时:14
```

整整慢了100多倍!

### HashMap的实现原理
----------------------------

对于上面的代码,我们应该知道性能最慢的是查找对应的key值,对于ArrayList来说,可能插入也是很大的性能消耗。在JDK中使用一个数组来存储key,索引是根据Key的Hash值来确定,而每一个key对应数据单元是一个链表。用图表示效果如下:

![HaspMap的原理](/img/assets/22/01.png)

下面我们JDK的原理进行分析:

#### 存值

1. 首先定义一个数组,其类型是一个Key-Value类型

2. 根据key的Hash值来确定当前的索引

3. 根据索引值来判断当前是否有值,如果当前有值则把当前的值插入当前数据之前

#### 取值

1.根据key的Hash值来确定当前的索引,根据索引来找到链表的首节点

2.遍历链表,找到指定的Key对应的节点,取出当前值



具体的实现代码如下(可以利用上面的代码):

```Java
public class KeyValuePair<K,V> {

public K Key;
public V Value;
public KeyValuePair next;

public KeyValuePair getNext() {
return next;
}

public void setNext(KeyValuePair next) {
this.next = next;
}
public KeyValuePair(){

}
public KeyValuePair(K k, V v){
this.Key=k;
this.Value=v;
}
public K getKey() {
return Key;
}

public void setKey(K key) {
Key = key;
}

public V getValue() {
return Value;
}

public void setValue(V value) {
Value = value;
}
}

HashMap的实现:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
public class MyHashMap<K, V> {

private int defalutLength = 16;
private int size;
private KeyValuePair<K, V>[] arr;
public MyHashMap() {
arr = new KeyValuePair[defalutLength];
size = 0;
}

public V put(K k, V v) {
int index = findIndex(k);
//todo:find out of index
if (arr[index] == null) {
arr[index] = new KeyValuePair(k, v);
} else {
KeyValuePair tempPair = arr[index];
arr[index] = new KeyValuePair(k, v);
arr[index].setNext(tempPair);
}
size++;
return v;
}

private int findIndex(K key) {
int index=key.hashCode() % defalutLength;
return index>0?index:(-1)*index;
}

public V get(K k) {
int index = findIndex(k);
if (arr[index] == null) {
return null;
}
KeyValuePair<K, V> current = arr[index];
while (current.next != null) {
if (current.getKey().equals(k)) {
return current.getValue();
}
current = current.next;
}
return null;
}
public int size(){
return this.size;
}

}

```

同样我们修改测试的代码:
```Java
@Test
public void MapTest(){

long start=System.currentTimeMillis();
MyMap<String,String> map =new MyMap();
for (int i=0;i<10000;i++){
map.put("Key"+i,"value"+i);
}
for (int i=0;i<10000;i++){
map.get("Key"+i);
}
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));

start=System.currentTimeMillis();
Map<String,String> hashMap =new HashMap<>();
for (int i=0;i<10000;i++){
hashMap.put("Key"+i,"value"+i);
}
for (int i=0;i<10000;i++){
hashMap.get("Key"+i);
}
end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));



start=System.currentTimeMillis();
MyHashMap<String,String> myhashMap =new MyHashMap<>();
for (int i=0;i<10000;i++){
myhashMap.put("Key"+i,"value"+i);
}
for (int i=0;i<10000;i++){
myhashMap.get("Key"+i);
}
end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));

}
```

运行结果:

```cte
耗时:2337
耗时:26
耗时:337
```

我们看到我们使用的链表在插入数据的时候进行整理,极大的提高了Map的效率,但离Jdk的性能还有很大的差距。


### 优化散列算法
---------------------
对于Map的查找的性能的瓶颈主要在最后的链表的查找,我们可以把Key的数据进行扩大,让Key分布的更加平均,这样就能减少最后链表迭代次数,实现思路:

1. 添加一个报警百分比,当key的使用率长度大于当前的比例,我们对key的数组进行扩容

2. 扩容后对原来的Key进行重新散列

修改后代码如下:

```Java
public class MyHashMap<K, V> {

private int defalutLength = 16;
private final double defaultAlfa = 0.75;
private int size;
private int arrLength;
private KeyValuePair<K, V>[] arr;

public MyHashMap() {
arr = new KeyValuePair[defalutLength];
size = 0;
arrLength=0;
}

public V put(K k, V v) {
int index = findIndex(k);
//todo:find out of index
if(arrLength>defalutLength*defaultAlfa){
extentArr();
}
if (arr[index] == null) {
arr[index] = new KeyValuePair(k, v);
arrLength++;
} else {
KeyValuePair tempPair = arr[index];
arr[index] = new KeyValuePair(k, v);
arr[index].setNext(tempPair);
}
size++;
return v;
}

private int findIndex(K key) {

int index=key.hashCode() % defalutLength;
return index>0?index:(-1)*index;
}
private void extentArr(){
defalutLength=defalutLength*2;
KeyValuePair<K, V>[] newArr=new KeyValuePair[defalutLength];
for (int i=0;i<defalutLength/2;i++){
if(arr[i]!=null){
int index= findIndex(arr[i].getKey());
newArr[index]=arr[i];
}
}
arr=newArr;
}
public V get(K k) {
int index = findIndex(k);
if (arr[index] == null) {
return null;
}

KeyValuePair<K, V> current = arr[index];
while (current.next != null) {
if (current.getKey().equals(k)) {
return current.getValue();
}
current = current.next;
}
return null;
}
public int size(){
return this.size;
}

}

```

最终测试性能结果如下:
```cte
耗时:2263
耗时:23
耗时:33

性能已经很接近了,至于为什么有差异,可能jdk有其它更多的优化(比如当链表长度大于8时,使用红黑树),但本文就讨论到这里。

阅读全文 »

容器的种类

为什么要使用容器? 因为数组不能够满足日常的开发需求,数组有以下弊端:

  1. 长度难以扩充
  2. 数据的类型必须相同
  3. 数组无法获得有多少个真实的数据,只能获得数组的长度。

在Java中有常用的三种类型的容器,分别是List 、Map、Set,基于这个三个基本的类型,派生出很多其它的类型,具体关系如下:

Relation

三者的区别:

  • Set(集):与list都是有Collection类的派生出来, 分辨各个元素的标识是HashCode,所以元素不能有重复
  • List(列表):是一个有序的列表,元素如果有重复,也会一一列出来。
  • Map(映射): Map是我们常说的键值对,有key和Value两个元素

使用方法:

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
 @Test
public void ContainerTest() {
String string[] = {"i", "am", "am", "xiao", "ming"};

List<String> list = new ArrayList<String>();
for (String s : string) {
list.add(s);
}
System.out.println("List执行结果:");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}

//
Set<String> set = new HashSet<String>();
for (String s : string) {
set.add(s);
}
Iterator iterator = set.iterator();
System.out.println("===================");
System.out.println("Set 执行结果:");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}

```

运行结果:

```cte
List执行结果:
i
am
am
xiao
ming
===================
Set 执行结果:
ming
xiao
i
am

三者的区别可以表示如下图:

阅读全文 »

做项目的时候碰到一个问题,就是Tomcat在处理含有|,{,}的字符的Url时候,发现请求没有到达指定的Controller上面,而在Access_log中写入了get null null 400的错误信息,从网上也翻了几个资料最终确定是tomcat的一个问题(个人觉得也是一个缺陷)

问题的由来

Tomcat根据rfc的规范Url中不能有类似|,{,}等不安全字符串,但在实际的操作中有时为了数据完整性和加密的方式都需要有|,{,}出现,这样的话Tomcat会直接告诉客户端Bad Request.

对于这个问题,很多人也提出很多不同的看法:https://bz.apache.org/bugzilla/show_bug.cgi?id=60594,经过修改,最终Tomcat把权限开放出来,通过tomcat.util.http.parser.HttpParser. requestTargetAllow这个配置选项,允许不安全字符的出现。Tomcat详细配置

解决方法

经过几次探索,有以下几个方法能够解决这个问题:

  1. 把请求的Url进行编码,这个对源头进行处理,来规避这个问题,如果是第三方来调用的url就无能无力。

  2. 修改Tomcat的配置文件(Tomcat\conf\catalina.properties),适用tomcat 7以上的版本

    1
    2
    3

    tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}

  3. 使用其它服务器进行中转,比如IIS和Apache

阅读全文 »

在编程中,常常遇到多种类型、多种状态的情况,对于这种可以事先预知的业务我们常常有两种表示方法:

  • 使用DB管理,优点是方便在线维护,缺点是每次加载都要读取数据库,加载的性能取决于数据库的压力 。

  • 使用枚举类型,优点是加载速度依赖于应用服务器,缺点是修改比较麻烦,每次加类型都需要发布代码。

对于Java枚举类型的使用,我们可以总结为以下几个方面:整型值,字符串字段和字符串的相关描述,下面我们就讨论如何方便的在这几个类型中相关转换,对于所有的类型转换可以总结如下:

1
2
3
4
5
6
7
int --> Enum 
Enum--> int
String -->Enum
Enum-->String
Enum-->描述
int -->描述
String -->描述

下面我们对于上面的7种描述来做出对应的转换方法,首先定义一个枚举类型:

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
    public enum EnumDemo {
fail,
success,
unknow;
}
```

为了能够保证相关的value和值能够保存下来,我们需要对枚举类型改成如下:

```Java
public enum EnumDemo {
fail(0, "失败"),
success(1, "成功"),
unknow(2, "未知");

private int value;
private String name;

EnumDemo(int value, String name) {
this.value = value;
this.name = name;
}
public int getValue() {
return this.value;
}
public String getName() {
return this.name;
}
}
```


相关代码如下:

```Java
@Test
public void TestEnum(){
int val=1;
EnumDemo intEnum=EnumDemo.values()[val];//整型转Enum
String enumStr= intEnum.toString();//Enum转字符串
String enumAnno=intEnum.getName();//Enum转描述

System.out.println(enumStr);
System.out.println(enumAnno);


String str="success";
EnumDemo strEnum=EnumDemo.values()[val];//字符转Enum
int enumVal=strEnum.getValue();//字符转int
String enumValAnno=strEnum.getName();//Enum转描述

System.out.println(enumVal);
System.out.println(enumValAnno);
}

运行结果如下:

1
2
3
4
success
成功
1
成功
阅读全文 »

什么是IL语言

中间语言,又称(IL语言)。充当Clr与.net 平台的中间语言,比如用C#编写程序,编译器首先是把C#代码转译成IL语言,最终由Clr解释执行,下面我们学习下IL语言。

如何读懂IL语言

  • 写一个helloworld的.net 程序,编译运行完成。

    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
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    		static void Main(string[] args)
    {
    Console.WriteLine("hello world");
    }
    ```

    - 使用ildasm.exe(C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools)反编译代码,得到IL代码如下:

    ```IL
    .method private hidebysig static void Main(string[] args) cil managed
    {
    .entrypoint
    // 代码大小 13 (0xd)
    .maxstack 8
    IL_0000: nop
    IL_0001: ldstr "hello world"
    IL_0006: call void [mscorlib]System.Console::WriteLine(string)
    IL_000b: nop
    IL_000c: ret
    } // end of method Program::Main
    ```

    - 查找对应的[指令表](http://blog.csdn.net/xiaouncle/article/details/71248830),来确定对应的含义


    <table class="table">
    <thead>
    <tr>
    <th>指令名称</th>
    <th>说明</th>
    </tr>
    </thead>
    <tbody>
    <tr>
    <td>Ldstr</td>
    <td>推送对元数据中存储的字符串的新对象引用。</td>
    </tr>
    <tr>
    <td>Nop</td>
    <td>如果修补操作码,则填充空间。尽管可能消耗处理周期,但未执行任何有意义的操作。</td>
    </tr>
    <tr>
    <td>Call</td>
    <td>调用由传递的方法说明符指示的方法。</td>
    </tr>
    <tr>
    <td>Ret</td>
    <td>从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。</td>
    </tr>
    </tbody>
    </table>

    - 其它几个名词的的解释

    **hidebysig**: 与之对就的是hidebyname,这个是确定使用方法的签名还是使用方法的名称来确定调用哪个方法.


    - 整个的IL语言解释

    ```IL
    .method private hidebysig static void Main(string[] args) cil managed
    {
    .entrypoint //代码入口
    // 代码大小 13 (0xd)
    .maxstack 8 //整个程序的堆栈大小
    IL_0000: nop //无实在意义
    IL_0001: ldstr "hello world" //定义字符
    IL_0006: call void [mscorlib]System.Console::WriteLine(string) //调用WriteLine变量
    IL_000b: nop
    IL_000c: ret //返回
    } // end of method Program::Main
    ```


    ### 更复杂的Demo

    - 添加编写如下C#代码:

    ``` C#
    class Program
    {
    static void Main(string[] args)
    {
    var a = 0;
    var b = 1;
    var c = Add(a, b);
    Console.WriteLine(c.ToString());
    }

    public static int Add(int x,int y)
    {
    return x + y;
    }
    }

    ```

    - 生成相关的IL代码及解释

    ``` IL
    .method private hidebysig static void Main(string[] args) cil managed
    {
    .entrypoint
    // 代码大小 27 (0x1b)
    .maxstack 2
    .locals init ([0] int32 a,
    [1] int32 b,
    [2] int32 c) //定义3个变量
    IL_0000: nop
    IL_0001: ldc.i4.0 //将整数值 0 作为 int32 推送到计算堆栈上。
    IL_0002: stloc.0 //从计算堆栈的顶部弹出当前值并将其存储到索引 0 处的局部变量列表中。
    IL_0003: ldc.i4.1 //将整数值 1 作为 int32 推送到计算堆栈上。
    IL_0004: stloc.1 //从计算堆栈的顶部弹出当前值并将其存储到索引 1 处的局部变量列表中。
    IL_0005: ldloc.0 //将索引 0 处的局部变量加载到计算堆栈上,这里指a。
    IL_0006: ldloc.1 //将索引 1 处的局部变量加载到计算堆栈上,这里指b。
    IL_0007: call int32 ILTest.Program::Add(int32,
    int32) //调用Add方法
    IL_000c: stloc.2 //将索引 2 处的局部变量加载到计算堆栈上,这里指c。
    IL_000d: ldloca.s c //将位于特定索引处的局部变量的地址加载到计算堆栈上(短格式)。
    IL_000f: call instance string [mscorlib]System.Int32::ToString()
    IL_0014: call void [mscorlib]System.Console::WriteLine(string)
    IL_0019: nop
    IL_001a: ret
    } // end of method Program::Main

    ```

    Add方法:

    ``` IL
    .method public hidebysig static int32 Add(int32 x,int32 y) cil managed
    {
    // 代码大小 9 (0x9)
    .maxstack 2
    .locals init ([0] int32 V_0) //创建一个V_0的局部变量
    IL_0000: nop
    IL_0001: ldarg.0 //将索引为 0 的参数加载到计算堆栈上。
    IL_0002: ldarg.1 //将索引为 1 的参数加载到计算堆栈上。
    IL_0003: add //将两个值相加并将结果推送到计算堆栈上。
    IL_0004: stloc.0
    IL_0005: br.s IL_0007 //无条件地将控制转移到目标指令(短格式)
    IL_0007: ldloc.0 //将索引 0 处的局部变量加载到计算堆栈上。
    IL_0008: ret
    } // end of method Program::Add
阅读全文 »

两个同学结婚小明和小六,分别在庐江和宿州,具体详细路途 花桥—苏州—庐江—宿州—蚌埠—花桥,具体路线如下:

road

一路不停的赶路,走了3天才走完这条路,出去走走想法也有了些许的变化。人生在世,唯富贵,学识难求,其余皆是过眼去烟。富贵是不期而遇,唯有学识自己努力才能得到。

这次相聚很短暂,下次又不知道何时才能见面,与老李相约走一趟青藏公路,五年为期,不知道五年后我的博客能不能更新一个青藏公路的文章。

小六结婚,寝室五人全到,老二带老婆夏青,我带老婆慧慧,老李和老苏各一个人,独缺老胡。

愿各位安好,日后当以富贵相见。

阅读全文 »

基本的概述

在一个服务器的集群上面,服务器的CPU长时间居高不下,响应的时间也一直很慢,即使扩容了服务器CPU的下降效果也不是很明显。

对于CPU过高的原因,可以总结到以下原因:

  • 太多的循环或者死循环

  • 加载了过多的数据,导致产生了很多的大对象

  • 产生了过多的对象,GC回收过于频繁(如:字符串拼接)

对于上面的情况,难点不是优化代码,难点在于定位到问题的所在,下面我们就用Dump抓包的方式来定位到问题的所在。介绍这个内容之前,我们要先回顾下.Net中垃圾回收的基础知识和一个工具的准备。


基础知识


垃圾回收触发条件

  • 代码显示调用System.GC的静态方法

  • windows报告低内存情况

  • CLR正在卸载AppDoamin

  • CLR正在关闭

阅读全文 »