-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
267 lines (157 loc) · 99.3 KB
/
atom.xml
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Xia.Blog()</title>
<subtitle>Stay Hungry</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2019-07-07T10:54:33.264Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>Xia Xiang</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>Netty做HTTP server</title>
<link href="http://yoursite.com/2019/06/25/netty/"/>
<id>http://yoursite.com/2019/06/25/netty/</id>
<published>2019-06-25T03:21:00.000Z</published>
<updated>2019-07-07T10:54:33.264Z</updated>
<content type="html"><![CDATA[<h2 id="一、简介"><a href="#一、简介" class="headerlink" title="一、简介"></a>一、简介</h2><h4 id="1、用netty作为通信框架"><a href="#1、用netty作为通信框架" class="headerlink" title="1、用netty作为通信框架"></a>1、用netty作为通信框架</h4><h4 id="2、集成spring-mvc来处理http请求"><a href="#2、集成spring-mvc来处理http请求" class="headerlink" title="2、集成spring mvc来处理http请求"></a>2、集成spring mvc来处理http请求</h4><a id="more"></a><h2 id="二、Server端"><a href="#二、Server端" class="headerlink" title="二、Server端"></a>二、Server端</h2><p>1、server接收端<br>正常启动netty server即可。netty的启动会阻塞后续的执行,所以单独建一个SingleThreadExecutor来执行start,这样不会阻塞主线程,也可以直接对线程池进行控制。</p><p>netty接受到请求后转发给HttpServerHandler处理<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">startBootstrap</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> EventLoopGroup bossGroup = <span class="keyword">new</span> NioEventLoopGroup();</span><br><span class="line"> EventLoopGroup workerGroup = <span class="keyword">new</span> NioEventLoopGroup();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> ServerBootstrap bootstrap = <span class="keyword">new</span> ServerBootstrap();</span><br><span class="line"> bootstrap.group(bossGroup, workerGroup);</span><br><span class="line"> bootstrap.channel(NioServerSocketChannel.class);</span><br><span class="line"> bootstrap.childHandler(<span class="keyword">new</span> ServletChannelInitializer());</span><br><span class="line"> bootstrap.option(ChannelOption.SO_BACKLOG, <span class="number">128</span>);</span><br><span class="line"> bootstrap.childOption(ChannelOption.SO_KEEPALIVE, <span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line"> String[] addressArray = StringUtil.split(httpAddress, <span class="string">":"</span>);</span><br><span class="line"> String ip = addressArray[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">int</span> port = Integer.parseInt(addressArray[<span class="number">1</span>]);</span><br><span class="line"> ChannelFuture future = bootstrap.bind(ip, port).sync();</span><br><span class="line"> LOGGER.info(<span class="string">"HTTP srver started on ip:"</span> + ip + <span class="string">", port :"</span> + port);</span><br><span class="line"> future.channel().closeFuture().sync();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> workerGroup.shutdownGracefully();</span><br><span class="line"> bossGroup.shutdownGracefully();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">initChannel</span><span class="params">(SocketChannel ch)</span> </span>{</span><br><span class="line"> ch.pipeline().addLast(<span class="string">"decoder"</span>, <span class="keyword">new</span> HttpRequestDecoder());</span><br><span class="line"> ch.pipeline().addLast(<span class="string">"aggregator"</span>, <span class="keyword">new</span> HttpObjectAggregator(<span class="number">65536</span>));</span><br><span class="line"> ch.pipeline().addLast(<span class="string">"encoder"</span>, <span class="keyword">new</span> HttpResponseEncoder());</span><br><span class="line"> ch.pipeline().addLast(<span class="string">"chunkedWriter"</span>, <span class="keyword">new</span> ChunkedWriteHandler());</span><br><span class="line"> ch.pipeline().addLast(<span class="string">"httpServerHandler"</span>, <span class="keyword">new</span> HttpServerHandler(dispatcherServlet));</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>HttpServerHandler收到FullHttpRequest后,转换成spring的MockHttpServletRequest(必须是ServletRequest,才可以做后续的解析和转发)<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">MockHttpServletRequest servletRequest = createServletRequest(request);</span><br><span class="line">MockHttpServletResponse servletResponse = <span class="keyword">new</span> MockHttpServletResponse();</span><br><span class="line"></span><br><span class="line"><span class="keyword">this</span>.servlet.service(servletRequest, servletResponse); <span class="comment">//自定义servlet处理request和response</span></span><br><span class="line"></span><br><span class="line">HttpResponseStatus status = HttpResponseStatus.valueOf(servletResponse.getStatus());</span><br><span class="line">DefaultFullHttpResponse response = <span class="keyword">new</span> DefaultFullHttpResponse(HTTP_1_1, status,</span><br><span class="line"> Unpooled.wrappedBuffer(servletResponse.getContentAsByteArray()));</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (String name : servletResponse.getHeaderNames()) {</span><br><span class="line"> <span class="keyword">for</span> (Object value : servletResponse.getHeaderValues(name)) {</span><br><span class="line"> response.headers().add(name, value);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">ChannelFuture writeFuture = ctx.write(response);</span><br><span class="line">writeFuture.addListener(ChannelFutureListener.CLOSE);</span><br></pre></td></tr></table></figure></p><p>Spring配置好扫描路径,在Controller的类和方法中加入SpringMVC的相应注解,</p><p>通过this.servlet.service(servletRequest, servletResponse)将request转发至自定义的dispatcher,处理request后,便能正常转发至Controller中执行。</p>]]></content>
<summary type="html">
<h2 id="一、简介"><a href="#一、简介" class="headerlink" title="一、简介"></a>一、简介</h2><h4 id="1、用netty作为通信框架"><a href="#1、用netty作为通信框架" class="headerlink" title="1、用netty作为通信框架"></a>1、用netty作为通信框架</h4><h4 id="2、集成spring-mvc来处理http请求"><a href="#2、集成spring-mvc来处理http请求" class="headerlink" title="2、集成spring mvc来处理http请求"></a>2、集成spring mvc来处理http请求</h4>
</summary>
<category term="programming" scheme="http://yoursite.com/categories/programming/"/>
<category term="java" scheme="http://yoursite.com/tags/java/"/>
</entry>
<entry>
<title>OOM Killer</title>
<link href="http://yoursite.com/2019/03/01/oom/"/>
<id>http://yoursite.com/2019/03/01/oom/</id>
<published>2019-03-01T15:55:00.000Z</published>
<updated>2019-07-07T10:37:45.211Z</updated>
<content type="html"><![CDATA[<h2 id="宕机判断与预处理"><a href="#宕机判断与预处理" class="headerlink" title="宕机判断与预处理"></a>宕机判断与预处理</h2><h3 id="OOM-Killer原理"><a href="#OOM-Killer原理" class="headerlink" title="OOM Killer原理"></a>OOM Killer原理<a id="more"></a></h3><ul><li>当物理内存和交换空间爆满时,OOM Killer会计算每个进程的oom_score并选择杀死进程。oom_score的值越大就越有可能被 OOM Killer 选中。这里列举两个主要因素:</li></ul><p>进程消耗的内存越大,oom_score越大<br>进程运行消耗的CPU时间越小,oom_score越大</p><ul><li>Windows下没有OOM Killer机制,用户只能强制杀死进程或者重启机器。</li></ul><h4 id="内存溢出OOM判断和dump获取"><a href="#内存溢出OOM判断和dump获取" class="headerlink" title="内存溢出OOM判断和dump获取"></a>内存溢出OOM判断和dump获取</h4><p>内存溢出的导致无响应的主要原因,是由于JVM的内存全部耗光,但是依然需要进一步分配内存。此时JVM就会进行很多次的的Full GC,希望回收不使用的内存。但是由于数据都在使用,因此一直释放不掉内存,于是就一遍遍的进行Full GC。</p><h4 id="按照以下办法判断是因为内存溢出导致的宕机"><a href="#按照以下办法判断是因为内存溢出导致的宕机" class="headerlink" title="按照以下办法判断是因为内存溢出导致的宕机"></a>按照以下办法判断是因为内存溢出导致的宕机</h4><ul><li>利用top命令发现进程的CPU使用率非常高。</li><li>CPU高并不一定是在做Full GC,需要进一步确认。利用jstat -gc pid 2000 2000 这一命令。如下代码块所示,关注FGC一栏,如果数值增加很快,而且FGCT(Full GC 的总时间)数值很大,例如FGC 为20 FGCT已经2000以上,平均一次FGC耗时100s。这里就可以确定是内存溢出了导致的宕机,需要获取dump</li><li>满足上面条件才认为是内存溢出,否则不在这个内存溢出处理范畴内。</li><li>优先通过配置来让JVM自己生产dump,但是如果没有配置的话那就是手动获取。执行“ jmap -dump:format=b,live,file=路径 进程pid”,例如 代码块中获取22374进程的dump。</li><li>如果客户环境没有配置JVM进行dump的参数,需要加上以防万一。配置宕机文件自动生成</li></ul><h3 id="系统的OOM-Killer"><a href="#系统的OOM-Killer" class="headerlink" title="系统的OOM Killer"></a>系统的OOM Killer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">执行下面三个命令</span><br><span class="line">egrep -i <span class="string">'killed process'</span> /var/log/messages</span><br><span class="line">egrep -i <span class="string">'killed process'</span> /var/log/dmesg</span><br><span class="line">egrep -i -r <span class="string">'killed process'</span> /var/log</span><br><span class="line"></span><br><span class="line">如果有下列相关信息</span><br><span class="line"><span class="string">"Out of memory: Kill process 31201 (java) score 783 or sacrifice child"</span></span><br><span class="line"><span class="string">"Killed process 13090 (java)"</span></span><br><span class="line">可以判断发生了OOM Killer,但是还需要通过内存占用和时间来确认与消失的BI进程是否吻合</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">例如</span><br><span class="line">$ egrep -i -r <span class="string">'killed process'</span> /var/log</span><br><span class="line">/var/log/messages-<span class="number">20190421</span>:Apr <span class="number">19</span> <span class="number">00</span>:<span class="number">20</span>:<span class="number">37</span> FineBI6 kernel: Killed process <span class="number">30353</span> (abrt-hook-ccpp) total-vm:<span class="number">49637864</span>kB, anon-rss:<span class="number">31433096</span>kB, file-rss:<span class="number">32</span>kB, shmem-rss:<span class="number">0</span>kB</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">有的系统输出的时间戳,利用下面的工具来转换</span><br><span class="line">https:<span class="comment">//tool.lu/timestamp/</span></span><br></pre></td></tr></table></figure><h4 id="测试方法"><a href="#测试方法" class="headerlink" title="测试方法"></a>测试方法</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// oom_adj=15时,必然会被OOM Killer选中</span></span><br><span class="line">echo <span class="number">15</span> > /proc/[PID]/oom_adj</span><br><span class="line"><span class="comment">// 手动触发OOM Killer</span></span><br><span class="line">echo f > /proc/sysrq-trigger</span><br></pre></td></tr></table></figure><h4 id="监测宕机-(信号量-JVM-Shutdown-Hook)"><a href="#监测宕机-(信号量-JVM-Shutdown-Hook)" class="headerlink" title="监测宕机 (信号量 + JVM Shutdown Hook)"></a>监测宕机 (信号量 + JVM Shutdown Hook)</h4><ul><li><p>信号量<br>利用sun.misc.SignalHandler类,可以监听除了kill -9之外的其他关闭信号。Java对每个信号量都启动一个线程进行处理,注册信号量后会启动”SIGTERM handler” 线程。即便主线程被阻塞,信号依然可以得到处理。</p></li><li><p>JVM Shutdown Hook<br>当程序执行System.exit(0)退出时,信号量方法是无法知道的,这时候需要使用JVM Shutdown Hook。</p></li></ul>]]></content>
<summary type="html">
<h2 id="宕机判断与预处理"><a href="#宕机判断与预处理" class="headerlink" title="宕机判断与预处理"></a>宕机判断与预处理</h2><h3 id="OOM-Killer原理"><a href="#OOM-Killer原理" class="headerlink" title="OOM Killer原理"></a>OOM Killer原理
</summary>
<category term="programming" scheme="http://yoursite.com/categories/programming/"/>
<category term="JVM" scheme="http://yoursite.com/tags/JVM/"/>
</entry>
<entry>
<title>javaassist动态修改注解</title>
<link href="http://yoursite.com/2019/02/11/javaassist/"/>
<id>http://yoursite.com/2019/02/11/javaassist/</id>
<published>2019-02-11T15:55:00.000Z</published>
<updated>2019-07-05T09:50:34.677Z</updated>
<content type="html"><![CDATA[<h4 id="JavaassistAnno-ob变量加上了注解A,需要改成B。"><a href="#JavaassistAnno-ob变量加上了注解A,需要改成B。" class="headerlink" title="JavaassistAnno ob变量加上了注解A,需要改成B。"></a>JavaassistAnno ob变量加上了注解A,需要改成B。</h4><a id="more"></a><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> vm;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> vm.anno.A;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JavaassistAnno</span> </span>{</span><br><span class="line"> <span class="meta">@A</span></span><br><span class="line"> <span class="keyword">private</span> Object ob;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> A {</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> B {</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="直接修改field的属性就可以"><a href="#直接修改field的属性就可以" class="headerlink" title="直接修改field的属性就可以"></a>直接修改field的属性就可以</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">ClassPool classPool = ClassPool.getDefault();</span><br><span class="line">CtClass ctClass = classPool.getCtClass(<span class="string">"vm.JavaassistAnno"</span>);</span><br><span class="line"><span class="keyword">for</span>(CtField ctField : ctClass.getDeclaredFields()){</span><br><span class="line"> FieldInfo fieldInfo = ctField.getFieldInfo();</span><br><span class="line"> ConstPool cp = fieldInfo.getConstPool();</span><br><span class="line"> AnnotationsAttribute att = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);</span><br><span class="line"> AnnotationsAttribute attribute = <span class="keyword">new</span> AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);</span><br><span class="line"> <span class="keyword">for</span> (Annotation ann : att.getAnnotations()){</span><br><span class="line"> <span class="keyword">if</span>(ann.getTypeName().equals(<span class="string">"vm.anno.A"</span>)){</span><br><span class="line"> Annotation annotation = <span class="keyword">new</span> Annotation(<span class="string">"vm.anno.B"</span>, cp);</span><br><span class="line"> attribute.addAnnotation(annotation);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> attribute.addAnnotation(ann);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> fieldInfo.addAttribute(attribute);</span><br><span class="line">}</span><br><span class="line"><span class="comment">//这个默认是调用了Thread.currentThread().getContextClassLoader()的defineClass方法,也有api可以指定classloader,或许取到了hibernate的classloader就可以输出过去。</span></span><br><span class="line">ctClass.toClass();</span><br><span class="line">Class clz = JavaassistAnno.class;</span><br><span class="line">Field field = clz.getDeclaredField(<span class="string">"ob"</span>);</span><br><span class="line"><span class="keyword">for</span> (java.lang.annotation.Annotation annotation : field.getAnnotations()){</span><br><span class="line"> <span class="comment">//可以看到能成功输出@vm.anno.B</span></span><br><span class="line"> System.out.println(annotation);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h4 id="JavaassistAnno-ob变量加上了注解A,需要改成B。"><a href="#JavaassistAnno-ob变量加上了注解A,需要改成B。" class="headerlink" title="JavaassistAnno ob变量加上了注解A,需要改成B。"></a>JavaassistAnno ob变量加上了注解A,需要改成B。</h4>
</summary>
<category term="programming" scheme="http://yoursite.com/categories/programming/"/>
<category term="JVM" scheme="http://yoursite.com/tags/JVM/"/>
</entry>
<entry>
<title>一些Java小技巧</title>
<link href="http://yoursite.com/2019/01/05/tips/"/>
<id>http://yoursite.com/2019/01/05/tips/</id>
<published>2019-01-05T03:21:00.000Z</published>
<updated>2019-07-05T09:59:31.377Z</updated>
<content type="html"><![CDATA[<h2 id="交换判断条件提高性能"><a href="#交换判断条件提高性能" class="headerlink" title="交换判断条件提高性能"></a>交换判断条件提高性能</h2><h3 id="在下面的例子中"><a href="#在下面的例子中" class="headerlink" title="在下面的例子中"></a>在下面的例子中</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (Utils.string2Number((String) value) != <span class="keyword">null</span> || style.getFormat() <span class="keyword">instanceof</span> NumberFormat) {</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (style.getFormat() <span class="keyword">instanceof</span> NumberFormat || Utils.string2Number((String) value) != <span class="keyword">null</span>) {</span><br><span class="line">}</span><br></pre></td></tr></table></figure><a id="more"></a><p>上下两个代码,第一个的判断先转数值类型 第二个则先判断format类型</p><p>第一段代码不管什么类型都会做一次类型的转换,这样的效率是很低的,而判断类型则要快很多</p><p>所以写if条件的时候要遵循的原则是判断简单的条件写在前面<br>不过说一点,最好的方法还是封装好多态,而不是用instanceof判断类型来执行方法</p><h2 id="关于简单对象"><a href="#关于简单对象" class="headerlink" title="关于简单对象"></a>关于简单对象</h2><p>在jdk8以下构造大量简单对象效率也是极差,jdk8速度很快差不多差上千倍,所以能使用基本类型尽量使用基本类型,而不是用一个简单的对象对基本类型进行封装</p><h2 id="大量轻量级计算时不要直接提交到线程池"><a href="#大量轻量级计算时不要直接提交到线程池" class="headerlink" title="大量轻量级计算时不要直接提交到线程池"></a>大量轻量级计算时不要直接提交到线程池</h2><p>比如有80w个计算,每个耗时都很短,排队的时候会浪费大量的等待时间,不如直接分成几组,比如分成8组,每个线程执行10w个计算,以后可以通过其他方式来均衡每个线程的负载</p><p>##有用的网站</p><p><a href="https://gceasy.io/" target="_blank" rel="noopener">分析GC日志</a></p><p><a href="https://fastthread.io" target="_blank" rel="noopener">分析ThreadDump</a></p>]]></content>
<summary type="html">
<h2 id="交换判断条件提高性能"><a href="#交换判断条件提高性能" class="headerlink" title="交换判断条件提高性能"></a>交换判断条件提高性能</h2><h3 id="在下面的例子中"><a href="#在下面的例子中" class="headerlink" title="在下面的例子中"></a>在下面的例子中</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (Utils.string2Number((String) value) != <span class="keyword">null</span> || style.getFormat() <span class="keyword">instanceof</span> NumberFormat) &#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (style.getFormat() <span class="keyword">instanceof</span> NumberFormat || Utils.string2Number((String) value) != <span class="keyword">null</span>) &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</summary>
<category term="programming" scheme="http://yoursite.com/categories/programming/"/>
<category term="JVM" scheme="http://yoursite.com/tags/JVM/"/>
</entry>
<entry>
<title>JVMTI 强制GC</title>
<link href="http://yoursite.com/2018/11/22/JAVA-GC/"/>
<id>http://yoursite.com/2018/11/22/JAVA-GC/</id>
<published>2018-11-22T13:33:00.000Z</published>
<updated>2019-07-05T09:49:15.786Z</updated>
<content type="html"><![CDATA[<h2 id="JVM-TI-JVM-Tool-Interface-是用来开发和监测jvm的编程接口。"><a href="#JVM-TI-JVM-Tool-Interface-是用来开发和监测jvm的编程接口。" class="headerlink" title="JVM TI (JVM Tool Interface) 是用来开发和监测jvm的编程接口。"></a>JVM TI (JVM Tool Interface) 是用来开发和监测jvm的编程接口。<a id="more"></a></h2><p>开发文档:<a href="https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html" target="_blank" rel="noopener">https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html</a></p><p>参考资料:<a href="https://stackoverflow.com/questions/2178296/java-how-do-you-really-force-a-gc-using-jvmtis-forcegargabecollection" target="_blank" rel="noopener">https://stackoverflow.com/questions/2178296/java-how-do-you-really-force-a-gc-using-jvmtis-forcegargabecollection</a></p><p><a href="https://blog.csdn.net/yczz/article/details/39034223" target="_blank" rel="noopener">https://blog.csdn.net/yczz/article/details/39034223</a></p><p><a href="https://blog.csdn.net/ddelphine/article/details/79695947" target="_blank" rel="noopener">https://blog.csdn.net/ddelphine/article/details/79695947</a></p><h3 id="JVM-TI-提供了许多JVM相关的接口,强制GC的接口是:"><a href="#JVM-TI-提供了许多JVM相关的接口,强制GC的接口是:" class="headerlink" title="JVM TI 提供了许多JVM相关的接口,强制GC的接口是:"></a>JVM TI 提供了许多JVM相关的接口,强制GC的接口是:</h3><p>jvmtiError ForceGarbageCollection(jvmtiEnv* env)<br>可以在被监测的测试类中定义原生的native方法,然后用C/C++调用JVM TI提供的接口来实现native方法,最后编译成动态链接库.dll(linux系统为 shared object 文件,.so)</p><h4 id="编写简单的agent"><a href="#编写简单的agent" class="headerlink" title="编写简单的agent"></a>编写简单的agent</h4><p>源码引用头文件#include<jvmti.h>,该头文件在jdk安装目录的include目录下。#include<jni_md.h>,windows系统中该头文件在jdk安装目录include\win32下,linux系统则在相应的include/linux目录下。在编译生成.dll/.so文件时要添加这两个头文件的搜索路径。</p><p>ForceGC.c -fPIC -shared -o forceGC.so<br>具体编译情况依据系统,语言和编译器</p><p>linux环境下 ForceGC.c<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><jvmti.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><jni_md.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"ForceGC.h"</span></span></span><br><span class="line"><span class="comment">/**< 此处定义结构体GobalAgentData用于保存jvmtiEnv指针 */</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>{</span></span><br><span class="line"> jvmtiEnv * jvmti;</span><br><span class="line">}GlobalAgentData;</span><br><span class="line"><span class="keyword">static</span> GlobalAgentData * gdata;</span><br><span class="line"><span class="function">JNIEXPORT jint JNICALL <span class="title">Agent_OnLoad</span><span class="params">(JavaVM *jvm, <span class="keyword">char</span> * options, <span class="keyword">void</span> *reserved)</span></span>{</span><br><span class="line"> jvmtiEnv * jvmti = <span class="literal">NULL</span>;</span><br><span class="line"> jvmtiCapabilities capa;</span><br><span class="line"> jvmtiError error;</span><br><span class="line"> jint result = (*jvm)->GetEnv(jvm,(<span class="keyword">void</span>**)&jvmti, JVMTI_VERSION_1_2);</span><br><span class="line"> <span class="keyword">if</span>(result != JNI_OK){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"ERROR : Unable to access JVMTI! \n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"GC_Agent was loaded success.\n"</span>);</span><br><span class="line"> (<span class="keyword">void</span>)<span class="built_in">memset</span>(&capa,<span class="number">0</span>,<span class="keyword">sizeof</span>(jvmtiCapabilities));</span><br><span class="line"> </span><br><span class="line"> capa.can_tag_objects = <span class="number">1</span>;</span><br><span class="line"> error = (*jvmti)->AddCapabilities(jvmti,&capa);</span><br><span class="line"> <span class="comment">//将jvmti保存到全局变量</span></span><br><span class="line"> gdata = (GlobalAgentData*)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(GlobalAgentData));</span><br><span class="line"> gdata->jvmti = jvmti;</span><br><span class="line"> <span class="keyword">return</span> JNI_OK;</span><br><span class="line"> }</span><br><span class="line"><span class="function">JNIEXPORT <span class="keyword">void</span> JNICALL <span class="title">Java_Test_forceGC</span><span class="params">(JNIEnv* env, jclass thisClass)</span></span>{</span><br><span class="line"> jvmtiError error = (*(gdata->jvmti))->ForceGarbageCollection(gdata->jvmti);</span><br><span class="line"> <span class="keyword">if</span> (error == <span class="literal">NULL</span>){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\nJVM has been forced to GC.\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line">ForceGC.h</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><jni.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> _Included_FinalizeEscapeGC</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _Included_FinalizeEscapeGC</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> __cplusplus</span></span><br><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span> {</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">JNIEXPORT <span class="keyword">void</span> JNICALL Java_Test_forceGC</span><br><span class="line"> (JNIEnv *, jclass);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> __cplusplus</span></span><br><span class="line">}</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">Test.java测试用类</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> {</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Test SAVE_HOOK = null;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">isAlive</span><span class="params">()</span> </span>{</span><br><span class="line">System.out.println(<span class="string">"I am still alive"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">@Override </span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">finalize</span><span class="params">()</span> throws Throwable </span>{</span><br><span class="line">super.finalize();</span><br><span class="line">System.out.println(<span class="string">"finalize method was excuted."</span>);</span><br><span class="line">Test.SAVE_HOOK = <span class="keyword">this</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> native <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">forceGC</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> throws Throwable </span>{</span><br><span class="line">SAVE_HOOK = <span class="keyword">new</span> Test();</span><br><span class="line">SAVE_HOOK = null;</span><br><span class="line"></span><br><span class="line">forceGC();</span><br><span class="line"></span><br><span class="line">Thread.sleep(<span class="number">500</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(SAVE_HOOK != null) {</span><br><span class="line">SAVE_HOOK.isAlive();</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line">System.out.println(<span class="string">"Oh... I am dead."</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">SAVE_HOOK = null;</span><br><span class="line"></span><br><span class="line">System.gc();</span><br><span class="line"></span><br><span class="line">Thread.sleep(<span class="number">500</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(SAVE_HOOK != null) {</span><br><span class="line">SAVE_HOOK.isAlive();</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line">System.out.println(<span class="string">"Oh... I am dead."</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>最后启动agent<br>java -agentpath:./forceGC.so Test</p><p>结果如下:<br><img src="a.png" alt=""></p>]]></content>
<summary type="html">
<h2 id="JVM-TI-JVM-Tool-Interface-是用来开发和监测jvm的编程接口。"><a href="#JVM-TI-JVM-Tool-Interface-是用来开发和监测jvm的编程接口。" class="headerlink" title="JVM TI (JVM Tool Interface) 是用来开发和监测jvm的编程接口。"></a>JVM TI (JVM Tool Interface) 是用来开发和监测jvm的编程接口。
</summary>
<category term="programming" scheme="http://yoursite.com/categories/programming/"/>
<category term="JVM" scheme="http://yoursite.com/tags/JVM/"/>
</entry>
<entry>
<title>Git的undo技巧</title>
<link href="http://yoursite.com/2018/11/22/git/"/>
<id>http://yoursite.com/2018/11/22/git/</id>
<published>2018-11-22T13:33:00.000Z</published>
<updated>2019-07-05T08:41:00.369Z</updated>
<content type="html"><![CDATA[<p>GitHub的<a href="https://github.com/blog/2019-how-to-undo-almost-anything-with-git" target="_blank" rel="noopener">How to undo (almost) anything with Git</a>这篇文章介绍了Git使用中的各种Undo技巧。<a id="more"></a></p><p>任何版本控制系统中最有用的功能之一就是能够<strong>撤销(undo)</strong>你之前的错误。在Git中“undo”功能可能因为场景的不同而有些许的差异。</p><p>当你进行一个新的提交时,Git会保存你在这个特定时间点的快照到本地的仓库中,之后,你可以通过Git来回到你早期的某个版本。</p><p>我们来先看看一些需要你“撤销”的常见场景,你可以尝试使用Git来用最佳的方式来解决它。</p><h2 id="撤销已经推送到远程的变更"><a href="#撤销已经推送到远程的变更" class="headerlink" title="撤销已经推送到远程的变更"></a>撤销已经推送到远程的变更</h2><h4 id="场景:"><a href="#场景:" class="headerlink" title="场景:"></a>场景:</h4><p>你已经执行git push,把你的修改推送到远程的仓库,现在你意识到之前推送的commit中有一个有些错误,想要撤销该commit。</p><h4 id="方案:"><a href="#方案:" class="headerlink" title="方案:"></a>方案:</h4><p>git revert <sha></sha></p><h4 id="原理:"><a href="#原理:" class="headerlink" title="原理:"></a>原理:</h4><p>git revert 会创建一个新的commit,它和指定SHA对应的commit是相反的(或者说是反转的)。如果原型的commit是“物质”,那么新的commit就是“反物质”。</p><p>任何从原来的commit里删除的内容都会再新的commit里被加回去,任何原来的commit中加入的内容都会在新的commit里被删除。</p><p>这是Git中最安全、最基本的撤销场景,因为它并不会改变历史。所以你现在可以git push新的“反转”commit来抵消你错误提交的commit。</p><h2 id="修正最后一个commit的消息"><a href="#修正最后一个commit的消息" class="headerlink" title="修正最后一个commit的消息"></a>修正最后一个commit的消息</h2><h4 id="场景:-1"><a href="#场景:-1" class="headerlink" title="场景:"></a>场景:</h4><p>你在最后一条commit消息里有一个笔误,已经执行了 git commit -m ‘Fixes bug #42’ ,但是在git push之前你意识到这个消息应该是“Fix bug #43”。</p><h4 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h4><p>你可以使用下面的命令:</p><p>git commit –amend<br>或</p><p>git commit –amend -m ‘Fixes bug #43’<br>原理:<br>git commit –amend 会用一个新的commit更新并替换最近的commit,这个新的commit会把任何修改内容和上一个commit的内容结合起来。如果当前没有提出任何修改,这个操作就只会把上次的commit重写一遍。</p><h2 id="撤销“本地的”修改"><a href="#撤销“本地的”修改" class="headerlink" title="撤销“本地的”修改"></a>撤销“本地的”修改</h2><h4 id="场景:-2"><a href="#场景:-2" class="headerlink" title="场景:"></a>场景:</h4><p>一只喵从键盘上走过(在我们家就是儿子小手在键盘上划拉),无意中保存了修改,然后破坏了编辑器。不过,你还没有commit这些修改。你想要恢复被修改文件里的所有内容–就像上次commit的时候一模一样。</p><h4 id="方法:"><a href="#方法:" class="headerlink" title="方法:"></a>方法:</h4><p>git checkout – <bad filename=""><br>原理<br>git checkout会把工作目录中的文件修改到Git之前记录的某个状态。你可以提供你想返回的分支或者特定的SHA,或者在缺省情况下,GIt会认为你希望checkout的是HEAD,当前checkout分支的最后一次commit。</bad></p><p>记住: 你用这种方法“撤销”的任何修改真的会完全消失。因为它们从来没有被提交过,所以之后Git也无法帮助我们恢复它们。你一定要确保自己了解在这个操作中丢掉的东西是什么?(也行可以利用git diff先确认一下)</p><h2 id="重置“本地的”修改"><a href="#重置“本地的”修改" class="headerlink" title="重置“本地的”修改"></a>重置“本地的”修改</h2><p>场景:<br>你在本地提交了一下东西(还没有push),但是所有这些东西都很糟糕,你希望撤销前面的三次提交(就像它们从来没有发生过一样)。</p><h4 id="方法:-1"><a href="#方法:-1" class="headerlink" title="方法:"></a>方法:</h4><p>git reset <last good="" sha=""><br>或</last></p><p>git reset –hard <last good="" sha=""></last></p><h4 id="原理:-1"><a href="#原理:-1" class="headerlink" title="原理:"></a>原理:</h4><p>git reset会把你的代码库历史返回到指定的SHA状态。这样就像是这些提交从来没有发生过。缺省情况下,git reset会保留工作目录。这样,提交是没有了,但是修改内容还在磁盘上。这是一种安全的选择,但通常我们会希望一步就“撤销”提交已经修改内容(这就是–hard选项的功能)。</p><h2 id="在撤销“本地修改”之后再恢复"><a href="#在撤销“本地修改”之后再恢复" class="headerlink" title="在撤销“本地修改”之后再恢复"></a>在撤销“本地修改”之后再恢复</h2><h4 id="场景:-3"><a href="#场景:-3" class="headerlink" title="场景:"></a>场景:</h4><p>你提交了几个commit,然后用git reset –hard撤销了这些修改(见上一段),接着你又意识到:你希望还原这些修改!</p><h4 id="方法:-2"><a href="#方法:-2" class="headerlink" title="方法:"></a>方法:</h4><p>git reflog<br>和</p><p>git reset<br>或</p><p>git checkout</p><h4 id="原理:-2"><a href="#原理:-2" class="headerlink" title="原理:"></a>原理:</h4><p>git reflog对于恢复项目历史是一个超棒的方式。你可以恢复几乎任何(commit过的)东西。</p><p>你可以能熟悉git log命令,它会显示commit的列表。 git reflog也是类似的,不过它显示的是一个HEAD发生改变的时间列表。</p><h2 id="一些注意事项:"><a href="#一些注意事项:" class="headerlink" title="一些注意事项:"></a>一些注意事项:</h2><p>它涉及的只是HEAD的改变。在你切换分支、用git commit进行提交、以及用git reset撤销commit时,HEAD会发生改变,但当你使用git checkout – <bad filename="">撤销时,HEAD并不会发生改变。就像我们在上面说的,这些修改从来没有被提交过,因此reflog也无法帮助我们恢复它们。<br>git reflog不会永远保持。Git会定期清理那些“用不到的”对象。不要指望几个月前的提交还一直躺着那里。<br>你的reflog就是你的,只是你的,你不能用git reflog来恢复另外一个开发者没有push过的commit。<br>git reflog</bad></p><p>那么…你如何来利用reflog来“恢复”之前“撤销”的commit呢?它取决于你想做到的到底是什么。</p><p>如果你希望准确的恢复项目的历史到某个时间点,用git reset –hard <sha><br>如果你希望重建工作目录里的一个或多个文件,让它们恢复到某个时间点的状态,用git checkout <sha> – <filename><br>如果你希望把这些commit里的某个重新提交到你的代码库里,用git cherry-pick <sha><br>利用分支的另一种做法</sha></filename></sha></sha></p><h4 id="场景:-4"><a href="#场景:-4" class="headerlink" title="场景:"></a>场景:</h4><p>你进行了一些提交,然后意识到你开始checkout的是master分支。希望这些提交进入到另外一个特性(feature)分支。</p><h4 id="方法:-3"><a href="#方法:-3" class="headerlink" title="方法:"></a>方法:</h4><p>git branch feature<br>git reset –hard origin/master<br>git checkout feature</p><h4 id="原理:-3"><a href="#原理:-3" class="headerlink" title="原理:"></a>原理:</h4><p>你可能习惯用 git checkout -b <name>创建一个新的分支(这是创建新分支并马上checkout的流行捷径),但是你不希望马上切换分支。这里,git branch feature创建了一个叫做feature的新分支,并指向你最近的commit,但是还是让你checkout在master分支上。</name></p><p>下一步,在提交任何新的commit之前,用git reset –hard 把master分支倒回origin/master。不过别担心,那些commit还在feature分支里。</p><p>最后,用git checkout切换到新的feature分支,并让你最近所有的工作成果都完好无损。</p><p>及时分支,省去繁琐</p><h4 id="场景:-5"><a href="#场景:-5" class="headerlink" title="场景:"></a>场景:</h4><p>你在master分支的基础上创建一个feature分支,但是master分支已经滞后于origin/master很多。现在master分支已经和origin/master同步,你希望在feature上的提交从现在开始,而不是从滞后很多的地方开始。</p><h4 id="方法:-4"><a href="#方法:-4" class="headerlink" title="方法:"></a>方法:</h4><p>git checkout feature<br>和</p><p>git rebase master</p><h4 id="原理:-4"><a href="#原理:-4" class="headerlink" title="原理:"></a>原理:</h4><p>要达到这个效果,你本来可以通过git reset(不加–hard,这样可以再磁盘上保留修改)和git checkout -b <new branch="" name="">然后再重新提交修改,不过这样做的话,你就失去提交历史。我们有更好的办法。</new></p><p>git rebase master会做下面的这些事情:</p><p>首先它会找到你当前checkout的分支和master分支的共同祖先。<br>然后它reset当前checkout的分支到那个共同祖先 ,在一个临时保存区存放所有之前的提交。<br>然后它把当前checkout的分支提到master的末尾部分,并从临时保存区重新把存放的commit提交到master分支的最后 一个commit之后。<br>大量的撤销/恢复<br>场景:<br>你向某个方向开始实现一个特性,但是你半路意识到另一个方案更好。你已经进行了十几次的提交,但是你现在只需要其中的一部分。你希望其他不需要的提交统统消失。</p><h4 id="方案:-1"><a href="#方案:-1" class="headerlink" title="方案:"></a>方案:</h4><p>git rebase -i <carlier sha=""><br>原理:<br>-i 参数会让rebase进入“交互模式”。它开始类似于签名讨论的rebase,但在重新进行任何提交之前,它会暂停下来并允许你详细的修改每一个提交。</carlier></p><p>rebase -i 会打开你缺省的文本编辑器,里面列出候选的提交。</p><p>rebase -i</p><p>前面两列是键: 第一个是选定的命令,对应第二列里的SHA确定的commit。缺省情况下,rebase -i假定每个commit都要通过pick命令被运行。</p><p>要丢弃一个commit,只要在编辑器中删除那一行就行了。如果你不再需要项目里面的那几个错误的提交, 删除你想删除的几行。</p><p>如果你需要暴漏commit的内容,而是对commit消息进行编辑,你可以使用reword命令,将第一列的pick替换成reword(或者直接使用r)。有人会觉得再这里直接重写commit消息就行了,但是这样是不管用的(rebase -i会忽略SHA列前面的任何东西,它后面的文本只是用来帮助我们记住那一串SHA代表什么)。当你完成rebase -i的操作之后,你会被提示输入需要编写的任何commit消息。</p><p>如果你需要把两个commit合并在一起,你可以使用squash或者fixup命令。</p><p>rebase -i</p><p>squash和fixup会“向上”合并(带有这两个命令的commit会被合并到他的签一个commit里)。上面的这个例子里,0835fe2和6943e85会合并成一个commit,38f5c4c和af67f82会被合并成另一个。</p><p>如果你选择了squash,Git会提示我们给新合并的commit一个新的commit消息; fixup则会把合并清单里的第一个commit的消息直接给新合并的commit。</p><p>在你保存并推出编辑器的时候,Git会按从顶部到底部的顺序运用你的commit。你可以通过在保存前修改commit顺序来改变运用的顺序。如果你愿意,你也可以同如下安排把af67f82和0835fe2合并到一起。</p><p>rebase -i</p><h2 id="修复更早期的commit"><a href="#修复更早期的commit" class="headerlink" title="修复更早期的commit"></a>修复更早期的commit</h2><p>场景:<br>你在一个更早期的commit里忘记了加入一个文件,如果更早的commit能包含这个忘记的文件就太棒了。你还没有push,但这个commit不是最近的,所以你还没法用commit –amend</p><h4 id="方法:-5"><a href="#方法:-5" class="headerlink" title="方法:"></a>方法:</h4><p>git commit –squash <sha of="" the="" earlier="" commit=""><br>和</sha></p><p>git rebase –autosquash -i <even earlier="" sha=""></even></p><h4 id="原理:-5"><a href="#原理:-5" class="headerlink" title="原理:"></a>原理:</h4><p>git commit –squash会创建一个新的commit,它带有一个commit消息,类似于squash! Earlier commit。(你也可以手工创建一个带有类似commit消息的commit,但是 commit –squash 可以帮你省下输入的工作)</p><p>如果你不想被提示为新合并的commit输入一条新的commit消息,你也可以利用 git commit –fixup。在这个情况下,你很有可能会用commit –fixup,因为你只是希望在rebase的适合,使用早期commit的commit消息。</p><p>rebase –autosquash -i 会激活一个交互式的rebase编辑器,但是编辑器打开的适合,在commit清单里任何squash!和fixup!的commit都已经配对到目标commit上了。</p><p>rebase –autosquash -i</p><p>在使用 –squash 和 –fixup的适合,你可能不记得想要修正的commit的SHA了(只是记得它是前面的第1个或者第5个commit)。你会发现Git的^和~操作符特别好用。HEAD~是HEAD的前一个commit。HEAD~4是HEAD往前第4个(或者一起算,倒数第5个commit)。</p><h2 id="停止追踪一个文件"><a href="#停止追踪一个文件" class="headerlink" title="停止追踪一个文件"></a>停止追踪一个文件</h2><h4 id="场景:-6"><a href="#场景:-6" class="headerlink" title="场景:"></a>场景:</h4><p>你偶然把application.log加到代码库里了,现在每次你运行应用,Git都会报告在application.log里有未提交的修改。你把 *.log放到了.gitignore文件里,可文件还是在代码库里,你怎样才能让Git“撤销”对这个文件的追踪呢?</p><h4 id="方法:-6"><a href="#方法:-6" class="headerlink" title="方法:"></a>方法:</h4><p>git rm –cached application.log<br>原理:<br>虽然.gitignore会阻止Git追踪文件的修改,甚至不关注文件是否存在,但这只是针对那些以前从来没有追踪过的文件。一旦有个文件被加入并提交了,Git就会持续关注改文件的改变。类似地,如果你利用git add -f来强制或覆盖了.gitignore,Git还会持续追踪改变的情况。之后你就不必用-f来添加这个文件了。</p><p>如果你希望从Git的追踪对象中删除那个本应忽略的文件,git rm –cached会从追踪对象中删除它,但让文件在磁盘上保持原封不动。因为现在它已经被忽略了,你在git status里就不会再看见这个文件,也不回再偶然提交该文件的修改了。</p><p>这就是如何在Git里撤销任何操作的方法。要了解更多关于本文中用到的Git命令,请查看官方文档。</p>]]></content>
<summary type="html">
<p>GitHub的<a href="https://github.com/blog/2019-how-to-undo-almost-anything-with-git" target="_blank" rel="noopener">How to undo (almost) anything with Git</a>这篇文章介绍了Git使用中的各种Undo技巧。
</summary>
<category term="programming" scheme="http://yoursite.com/categories/programming/"/>
<category term="Git" scheme="http://yoursite.com/tags/Git/"/>
</entry>
<entry>
<title>JAVA ClassLoader</title>
<link href="http://yoursite.com/2018/08/08/JAVA-classloader/"/>
<id>http://yoursite.com/2018/08/08/JAVA-classloader/</id>
<published>2018-08-08T04:53:41.000Z</published>
<updated>2019-07-05T09:46:01.913Z</updated>
<content type="html"><![CDATA[<p>本文从虚拟机底层机制说起,然后分析ClassLoader代码结构与特性,最后说明ClassLoader在9.0插件引擎中的应用。<br><a id="more"></a></p><h2 id="Class对象的生命周期"><a href="#Class对象的生命周期" class="headerlink" title="Class对象的生命周期"></a>Class对象的生命周期</h2><p>加载<br>验证<br>准备<br>解析<br>初始化<br>这里简化一下,只关注加载和初始化。加载是指将二进制字节码转化为Class对象的过程,初始化是指执行Class中static方法块,生成static变量等。</p><p><strong><em>触发加载过程的操作有:</em></strong></p><p>显式调用classLoader.loadClass(className)<br>在代码执行过程(比如当前类的初始化)中,使用了其他的类,虚拟机会自动加载(不一定会初始化)<br>ClassLoader在显示调用中的作用不言而喻。在代码执行过程中,如果用到了其他类,虚拟机会首先获取当前类的ClassLoader,再通过这个ClassLoader去查找引用的类,如果没有找到,则会触发加载操作,如果也没加载到,就会抛ClassNotFoundException。</p><p><strong><em>触发初始化的操作有:</em></strong></p><p>显示初始化:Class.forName(className)<br>创建类的实例<br>调用类的静态方法<br>使用类的非常量静态字段<br>初始化某个类的字类<br>通常一个类的加载不会触发其他类的加载,一个类的初始化会触发其他类的加载和初始化。</p><p><strong><em>不同ClassLoader对类的隔离</em></strong></p><p>通过上面Class的初始化过程可以看到,一个类能够依赖什么类,完全是取决于他的ClassLoader能找到哪些类。如果两个ClassLoader没有父子关系,那么他们加载的类是永远也不能相互引用到的。同样,如果两个ClassLoader,A和B,没有父子关系。A首先加载了classC,B中的类在使用过程中,也需要用到classC,则会触发B的加载操作(虽然内存中已经有了classC的Class<?>对象),这时内存中就会存在两个classC的Class<?>对象,这两个并不相等,没有任何关系。这样就保证了A中的类用到的classC都是A自己加载的,B中的类用到的classC都是B加载的,彼此共存又不互相打扰。</p><p>例如:<br><img src="1.png" alt=""></p><p>其中,PluginClassLoaderA是插件更新前的classLoader,PluginClassLoaderB是插件更新后的ClassLoader。在插件停止、删除并更新后,老版本的Class对象会由虚拟机在合适的实际处理掉,也就是说,在一定的时间之内,新版本的Class对象和老版本的Class对象是在内存里面共存的。</p><p>ClassLoader的查找机制保证了新老插件的Class不会相互影响。比如在升级后,右边的Class4触发了com.fr.plugin.A的加载,虚拟机首先拿到Class4的ClassLoader,即PluginClassLoaderB,然后用PluginClassLoaderB去加载com.fr.plugin.A,在查找加载过程中,PluginClassLoaderB首先委托parent,即ParentClassLoader去查找,没有找到则自己从右下角的四个类里面去找,找到则返回,不会访问左下角的四个类,保证了新老插件的隔离。</p><h4 id="ClassLoader的结构"><a href="#ClassLoader的结构" class="headerlink" title="ClassLoader的结构"></a>ClassLoader的结构</h4><p>除去资源加载的方法外,ClassLoader只有一个public的loadClass方法。这个方法返回一个Class对象或抛出ClassNotFoundException。</p><p>//loadClass方法(这里精简了一下)<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Class<?> loadClass(String name) <span class="keyword">throws</span> ClassNotFoundException {</span><br><span class="line"><span class="comment">//首先检查已经是否已经加载过了</span></span><br><span class="line"> Class<?> c = findLoadedClass(name); </span><br><span class="line"> <span class="keyword">if</span> (c == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">//双亲委派</span></span><br><span class="line"> <span class="keyword">if</span> (parent != <span class="keyword">null</span>) {</span><br><span class="line"> c = parent.loadClass(name, <span class="keyword">false</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> c = findBootstrapClassOrNull(name);</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (ClassNotFoundException ignore) {</span><br><span class="line"> }</span><br><span class="line"><span class="comment">//自己没加载过,同时parent也找不到的话</span></span><br><span class="line"> <span class="keyword">if</span> (c == <span class="keyword">null</span>) {</span><br><span class="line"><span class="comment">//尝试查找对应字节码,加载新的Class</span></span><br><span class="line"> c = findClass(name);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">return</span> c;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>这个方法是双亲委派机制的实现,也是sun说的最好不要重写loadClass方法,要重写就写findClass的原因,重写loadClass可能会打破双亲委派机制。</p><p>有了双亲委派机制,就可以通过定义不同ClassLoader之间的关系,来做到不同插件之间共享报表的类,又隔离不同插件之间的类。</p><p>默认的findClass方法是这个样子的:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> Class<?> findClass(String name) <span class="keyword">throws</span> ClassNotFoundException {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> ClassNotFoundException(name);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>相当于是只定义了接口,没有实现。接口的含义就是:通过一个类的全限定名,返回一个Class对象。同时,ClassLoader提供了几个返回Class对象的方法可供调用:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> Class<?> defineClass(String name, <span class="keyword">byte</span>[] b, <span class="keyword">int</span> off, <span class="keyword">int</span> len) <span class="keyword">throws</span> ClassFormatError</span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> Class<?> defineClass(String name, <span class="keyword">byte</span>[] b, <span class="keyword">int</span> off, <span class="keyword">int</span> len,</span><br><span class="line"> ProtectionDomain protectionDomain) <span class="keyword">throws</span> ClassFormatError</span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> Class<?> defineClass(String name, java.nio.ByteBuffer, ProtectionDomain protectionDomain)</span><br></pre></td></tr></table></figure></p><p>这三个方法就是我们用来定义Class的。有了这三个方法, 我们重写findClass的任务就是获取类的字节码而已。这里就是我们可以自由发挥的地方了,可以从文件中读,可以从网络中读,还可以从加密的文件中读。读来字节码之后,还能对字节码进行处理(见3、Javassist的使用)后再调用defineClass。</p>]]></content>
<summary type="html">
<p>本文从虚拟机底层机制说起,然后分析ClassLoader代码结构与特性,最后说明ClassLoader在9.0插件引擎中的应用。<br>
</summary>
<category term="programming" scheme="http://yoursite.com/categories/programming/"/>
<category term="JVM" scheme="http://yoursite.com/tags/JVM/"/>
</entry>
<entry>
<title>关于缓存策略</title>
<link href="http://yoursite.com/2018/05/19/cache/"/>
<id>http://yoursite.com/2018/05/19/cache/</id>
<published>2018-05-19T01:31:00.000Z</published>
<updated>2019-07-05T09:50:04.147Z</updated>
<content type="html"><![CDATA[<h2 id="缓存"><a href="#缓存" class="headerlink" title="缓存"></a>缓存</h2><p>缓存主要用于解决高并发,大数据场景下,热点数据访问的性能问题。提供高性能的数据快速访问。</p><p>缓存的过期策略主要有FIFO、LFU、LRU三种。<br><a id="more"></a></p><ul><li><p>FIFO</p><p> 按照“先进先出(First In,First Out)”的原理淘汰数据,正好符合队列的特性,数据结构上使用队列Queue来实现。</p><ol><li><p>新访问的数据插入FIFO队列尾部,数据在FIFO队列中顺序移动;</p></li><li><p>淘汰FIFO队列头部的数据;</p></li></ol></li></ul><ul><li><p>LRU</p><p> (Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。<br> 最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:</p><ol><li><p>新数据插入到链表头部;</p></li><li><p>每当缓存命中(即缓存数据被访问),则将数据移到链表头部;</p></li><li><p>当链表满的时候,将链表尾部的数据丢弃。</p></li></ol></li><li><p>LFU</p><p> (Least Frequently Used)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。</p><p> LFU的每个数据块都有一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序。</p></li></ul><h2 id="Caffine"><a href="#Caffine" class="headerlink" title="Caffine"></a>Caffine</h2><h2 id="驱逐策略"><a href="#驱逐策略" class="headerlink" title="驱逐策略"></a>驱逐策略</h2><p>缓存的驱逐策略是为了预测哪些数据在短期内最可能被再次用到,从而提升缓存的命中率。由于简洁的实现、高效的运行时表现以及在常规的使用场景下有不错的命中率,LRU策略或许是最流行的驱逐策略。但LRU通过历史数据来预测未来是局限的,它会认为最后到来的数据是最可能被再次访问的,从而给与它最高的优先级。<br>现代缓存扩展了对历史数据的使用,结合就近程度(recency)和访问频次(frequency)来更好的预测数据。其中一种保留历史信息的方式是使用popularity sketch(一种压缩、概率性的数据结构)来从一大堆访问事件中定位频繁的访问者。可以参考CountMin Sketch算法,它由计数矩阵和多个哈希方法实现。发生一次读取时,矩阵中每行对应的计数器增加计数,估算频率时,取数据对应是所有行中计数的最小值。这个方法让我们从空间、效率、以及适配矩阵的长宽引起的哈希碰撞的错误率上做权衡。</p><ul><li><p>CountMin Sketch</p><p> Window TinyLFU(W-TinyLFU)算法将sketch作为过滤器,当新来的数据比要驱逐的数据高频时,这个数据才会被缓存接纳。这个许可窗口给予每个数据项积累热度的机会,而不是立即过滤掉。这避免了持续的未命中,特别是在突然流量暴涨的的场景中,一些短暂的重复流量就不会被长期保留。为了刷新历史数据,一个时间衰减进程被周期性或增量的执行,给所有计数器减半。</p></li></ul><ul><li><p>Window TinyLFU</p><p>对于长期保留的数据,W-TinyLFU使用了分段LRU(Segmented LRU,缩写SLRU)策略。起初,一个数据项存储被存储在试用段(probationary segment)中,在后续被访问到时,它会被提升到保护段(protected segment)中。保护段满后,有的数据会被淘汰回试用段,这也可能级联的触发试用段的淘汰。这套机制确保了访问间隔小的热数据被保存下来,而被重复访问少的冷数据则被回收。</p></li></ul><h2 id="过期策略"><a href="#过期策略" class="headerlink" title="过期策略"></a>过期策略</h2><p>过期的实现里,往往每个数据项拥有不同的过期时间。因为容量的限制,过期后数据需要被懒淘汰,否则这些已过期的脏数据会污染到整个缓存。一般缓存中会启用专有的清扫线程周期性的遍历清理缓存。这个策略相比在每次读写操作时按照过期时间排序的优先队列来清理过期缓存要好,因为后台线程隐藏了的过期数据清除的时间开销。<br>鉴于大多数场景里不同数据项使用的都是固定的过期时长,Caffien采用了统一过期时间的方式。这个限制让用O(1)的有序队列组织数据成为可能。针对数据的写后过期,维护了一个写入顺序队列,针对读后过期,维护了一个读取顺序队列。缓存能复用驱逐策略下的队列以及下面将要介绍的并发机制,让过期的数据项在缓存的维护阶段被抛弃掉。</p><h2 id="并发"><a href="#并发" class="headerlink" title="并发"></a>并发</h2><p>由于在大多数的缓存策略中,数据的读取都会伴随对缓存状态的写操作,并发的缓存读取被视为一个难点问题。传统的解决方式是用同步锁。这可以通过将缓存的数据划成多个分区来进行锁拆分优化。不幸的是热点数据所持有的锁会比其他数据更常的被占有,在这种场景下锁拆分的性能提升也就没那么好了。当单个锁的竞争成为瓶颈后,接下来的经典的优化方式是只更新单个数据的元数据信息,以及使用随机采样、基于FIFO的驱逐策略来减少数据操作。这些策略会带来高性能的读和低性能的写,同时在选择驱逐对象时也比较困难。<br>另一种可行方案来自于数据库理论,通过提交日志的方式来扩展写的性能。写入操作先记入日志中,随后异步的批量执行,而不是立即写入到数据结构中。这种思想可以应用到缓存中,执行哈希表的操作,将操作记录到缓冲区,然后在合适的时机执行缓冲区中的内容。这个策略依然需要同步锁或者tryLock,不同的是把对锁的竞争转移到对缓冲区的追加写上。<br>在Caffeine中,有一组缓冲区被用来记录读写。一次访问首先会被因线程而异的哈希到stripped ring buffer上,当检测到竞争时,缓冲区会自动扩容。一个ring buffer容量满载后,会触发异步的执行操作,而后续的对该ring buffer的写入会被丢弃,直到这个ring buffer可被使用。虽然因为ring buffer容量满而无法被记录该访问,但缓存值依然会返回给调用方。这种策略信息的丢失不会带来大的影响,因为W-TinyLFU能识别出我们希望保存的热点数据。通过使用因线程而异的哈希算法替代在数据项的键上做哈希,缓存避免了瞬时的热点key的竞争问题。<br>写数据时,采用更传统的并发队列,每次变更会引起一次立即的执行。虽然数据的损失是不可接受的,但我们仍然有很多方法可以来优化写缓冲区。所有类型的缓冲区都被多个的线程写入,但却通过单个线程来执行。这种多生产者/单个消费者的模式允许了更简单、高效的算法来实现。<br>缓冲区和细粒度的写带来了单个数据项的操作乱序的竞态条件。插入、读取、更新、删除都可能被各种顺序的重放,如果这个策略控制的不合适,则可能引起悬垂索引。解决方案是通过状态机来定义单个数据项的生命周期。<br>在基准测试中,缓冲区随着哈希表的增长而增长,它的的使用相对更节省资源。读的性能随着CPU的核数线性增长,是哈希表吞吐量的33%。写入有10%的性能损耗,这是因为更新哈希表时的竞争是最主要的开销。</p>]]></content>
<summary type="html">
<h2 id="缓存"><a href="#缓存" class="headerlink" title="缓存"></a>缓存</h2><p>缓存主要用于解决高并发,大数据场景下,热点数据访问的性能问题。提供高性能的数据快速访问。</p>
<p>缓存的过期策略主要有FIFO、LFU、LRU三种。<br>
</summary>
<category term="programming" scheme="http://yoursite.com/categories/programming/"/>
<category term="java" scheme="http://yoursite.com/tags/java/"/>
</entry>
<entry>
<title>ThreadDump分析小结</title>
<link href="http://yoursite.com/2016/09/08/ThreadDump%E5%88%86%E6%9E%90%E5%B0%8F%E7%BB%93/"/>
<id>http://yoursite.com/2016/09/08/ThreadDump分析小结/</id>
<published>2016-09-08T08:13:45.000Z</published>
<updated>2019-07-05T08:43:39.006Z</updated>
<content type="html"><![CDATA[<p>ThreadDump中包含了线程的状态信息,一般有 Runnable,BLOCKED,Wait几种状态。Wait一般都是线程池里面的线程,可以跳过,主要关注点应该放在Runnable和BLOCKED这两种状态。<a id="more"></a></p><p>我们可以根据当前服务器状态来看线程信息。</p><p>一.比如现在服务器cpu很低,内存占用也很低,但是卡,原因基本上有两个</p><p>1.线程阻塞了,下图看出来,报表在关闭的时候,需要等待原来报表计算完成(这个基本上是报表计算时间比较长,大部分可能是sql取数时间很长),这个线程一直在等,所以cpu占用率低。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">"pool-56-thread-10" #669 prio=5 os_prio=0 tid=0x00007fa7f4005800 nid=0x3ab2c waiting for monitor entry [0x00007fa65545a000]</span><br><span class="line">java.lang.Thread.State: BLOCKED (on object monitor)</span><br><span class="line">at com.fr.web.core.ReportSessionIDInfor.clearPageSet(Unknown Source)</span><br><span class="line">- waiting to lock <<span class="number">0x00007fae6cd4cf10</span>> (a com.fr.web.core.ReportSessionIDInfor)</span><br><span class="line">at com.fr.web.core.TemplateSessionIDInfo.release(Unknown Source)</span><br><span class="line">at com.fr.web.core.ReportSessionIDInfor.release(Unknown Source)</span><br><span class="line">at com.fr.web.core.SessionPoolManager.processCloseSession(Unknown Source)</span><br><span class="line">at com.fr.web.core.SessionPoolManager.access$<span class="number">200</span>(Unknown Source)</span><br><span class="line">at com.fr.web.core.SessionPoolManager$<span class="number">5</span>.run(Unknown Source)</span><br><span class="line">at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:<span class="number">511</span>)</span><br><span class="line">at java.util.concurrent.FutureTask.run(FutureTask.java:<span class="number">266</span>)</span><br><span class="line">at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:<span class="number">1149</span>)</span><br><span class="line">at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:<span class="number">624</span>)</span><br><span class="line">at java.lang.Thread.run(Thread.java:<span class="number">748</span>)</span><br><span class="line"></span><br><span class="line"><span class="number">2</span>.报表计算sql取数线程等待数据库返回数据,线程没事做,cpu利用率很低。这个线程状态是Runnable的,从socket缓存区读取数据。主要的特征就是依赖了外部api DruidPooledStatement.executeQuery,如果这种线程比较多,就是取数太慢了。</span><br><span class="line"></span><br><span class="line">"pool-43-thread-1893" #4977 prio=5 os_prio=0 tid=0x00007f624950e000 nid=0x130b7 runnable [0x00007f6150bd3000]</span><br><span class="line">java.lang.Thread.State: RUNNABLE</span><br><span class="line">at java.net.SocketInputStream.socketRead0(Native Method)</span><br><span class="line">at java.net.SocketInputStream.socketRead(SocketInputStream.java:<span class="number">116</span>)</span><br><span class="line">at java.net.SocketInputStream.read(SocketInputStream.java:<span class="number">171</span>)</span><br><span class="line">at java.net.SocketInputStream.read(SocketInputStream.java:<span class="number">141</span>)</span><br><span class="line">at com.sap.db.rte.comm.BasicSocketComm.receiveData(BasicSocketComm.java:<span class="number">494</span>)</span><br><span class="line">at com.sap.db.rte.comm.BasicSocketComm.receive(BasicSocketComm.java:<span class="number">582</span>)</span><br><span class="line">at com.sap.db.rte.comm.JdbcCommunication.execute(JdbcCommunication.java:<span class="number">116</span>)</span><br><span class="line">at com.sap.db.jdbc.ConnectionSapDB.execute(ConnectionSapDB.java:<span class="number">867</span>)</span><br><span class="line">at com.sap.db.jdbc.ConnectionSapDB.execute(ConnectionSapDB.java:<span class="number">820</span>)</span><br><span class="line">at com.sap.db.jdbc.StatementSapDB.sendCommand(StatementSapDB.java:<span class="number">898</span>)</span><br><span class="line">at com.sap.db.jdbc.StatementSapDB.sendSQL(StatementSapDB.java:<span class="number">947</span>)</span><br><span class="line">at com.sap.db.jdbc.StatementSapDB.execute(StatementSapDB.java:<span class="number">256</span>)</span><br><span class="line">at com.sap.db.jdbc.StatementSapDB.executeQuery(StatementSapDB.java:<span class="number">399</span>)</span><br><span class="line">at com.sap.db.jdbc.trace.Statement.executeQuery(Statement.java:<span class="number">184</span>)</span><br><span class="line">at com.fr.third.alibaba.druid.pool.DruidPooledStatement.executeQuery(DruidPooledStatement.java:<span class="number">140</span>)</span><br><span class="line">at com.fr.data.core.db.dialect.base.key.create.executequery.DialectExecuteQueryKey.execute(Unknown Source)</span><br><span class="line">at com.fr.data.core.db.dialect.base.key.create.executequery.DialectExecuteQueryKey.execute(Unknown Source)</span><br><span class="line">at com.fr.data.core.db.dialect.AbstractDialect.execute(Unknown Source)</span><br><span class="line">at com.fr.data.core.db.dialect.DefaultDialect.executeQuery(Unknown Source)</span><br><span class="line">at com.fr.data.impl.AbstractDBDataModel$<span class="number">1</span>.call(Unknown Source)</span><br><span class="line">at com.fr.data.impl.AbstractDBDataModel$<span class="number">1</span>.call(Unknown Source)</span><br><span class="line">at java.util.concurrent.FutureTask.run(FutureTask.java:<span class="number">266</span>)</span><br><span class="line">at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:<span class="number">1142</span>)</span><br><span class="line">at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:<span class="number">617</span>)</span><br><span class="line">at java.lang.Thread.run(Thread.java:<span class="number">745</span>)</span><br></pre></td></tr></table></figure></p><p>二.比如现在cpu很高,导致很卡,我们就需要关注一下,Runnable状态的线程。</p><p>如下图,一个线程在读取模板的时候,线程一直在runnable。那么这个Runnable和上面一个数据库Runnable有什么区别呢,主要区别就是这个是不涉及IO操作(数据库访问和redis访问都可以认为是IO操作,IO操作会导致cpu空闲)。</p><p>该线程一直在scanAttributeValue,cpu占满,卡顿。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">"Timer-7" #109 daemon prio=5 os_prio=0 tid=0x00007f8d7cba1000 nid=0x13b4 runnable [0x00007f8d400ef000]</span><br><span class="line">java.lang.Thread.State: RUNNABLE</span><br><span class="line">at com.fr.third.javax.xml.stream.XMLScanner.scanAttributeValue(XMLScanner.java:<span class="number">548</span>)</span><br><span class="line">at com.fr.third.javax.xml.stream.XMLNSDocumentScannerImpl.scanAttribute(XMLNSDocumentScannerImpl.java:<span class="number">499</span>)</span><br><span class="line">at com.fr.third.javax.xml.stream.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:<span class="number">298</span>)</span><br><span class="line">at com.fr.third.javax.xml.stream.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:<span class="number">1121</span>)</span><br><span class="line">at com.fr.third.javax.xml.stream.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:<span class="number">133</span>)</span><br><span class="line">at com.fr.third.javax.xml.stream.XMLReaderImpl.next(XMLReaderImpl.java:<span class="number">358</span>)</span><br><span class="line">at com.fr.third.javax.xml.stream.XMLEventReaderImpl.nextEvent(XMLEventReaderImpl.java:<span class="number">56</span>)</span><br><span class="line">at com.fr.stable.xml.XMLableReader.readXMLObject(Unknown Source)</span><br><span class="line">at com.fr.data.impl.CustomDictionary.readXML(Unknown Source)</span><br><span class="line">at com.fr.stable.xml.XMLableReader.readXMLObject(Unknown Source)</span><br><span class="line">at com.fr.data.core.TableDataXmlUtils.readXMLDictionary(Unknown Source)</span><br><span class="line">at com.fr.data.core.DataCoreXmlUtils.readXMLDictionary(Unknown Source)</span><br><span class="line">at com.fr.base.present.DictPresent.readXML(Unknown Source)</span><br><span class="line">at com.fr.stable.xml.XMLableReader.readXMLObject(Unknown Source)</span><br><span class="line">at com.fr.xml.ReportXMLUtils.readPresent(Unknown Source)</span><br><span class="line">at com.fr.report.cell.AbstractDynamicCellElement.readXML(Unknown Source)</span><br><span class="line">at com.fr.report.cell.AbstractInsertCellElement.readXML(Unknown Source)</span><br><span class="line">at com.fr.report.cell.AbstractExpandCellElement.readXML(Unknown Source)</span><br><span class="line">at com.fr.report.cell.DefaultTemplateCellElement.readXML(Unknown Source)</span><br><span class="line">at com.fr.stable.xml.XMLableReader.readXMLObject(Unknown Source)</span><br><span class="line">at com.fr.report.elementcase.AbstractElementCase$<span class="number">1</span>.readXML(Unknown Source)</span><br><span class="line">at com.fr.stable.xml.XMLableReader.readXMLObject(Unknown Source)</span><br><span class="line">at com.fr.report.elementcase.AbstractElementCase.readXML(Unknown Source)</span><br><span class="line">at com.fr.report.report.AbstractECReport.readXML(Unknown Source)</span><br><span class="line">at com.fr.report.worksheet.WorkSheet.readXML(Unknown Source)</span><br><span class="line">at com.fr.stable.xml.XMLableReader.readXMLObject(Unknown Source)</span><br><span class="line">at com.fr.main.AbstractFineBook.readReportXML(Unknown Source)</span><br><span class="line">at com.fr.main.AbstractFineBook.readXML(Unknown Source)</span><br><span class="line">at com.fr.main.AbstractTemplateWorkBook.readXML(Unknown Source)</span><br><span class="line">at com.fr.stable.xml.XMLableReader.readXMLObject(Unknown Source)</span><br><span class="line">at com.fr.main.impl.WorkBook.readStream(Unknown Source)</span><br><span class="line">at com.fr.main.impl.WorkBook.readStream(Unknown Source)</span><br><span class="line">at com.fr.io.TemplateWorkBookIO.readTemplateWorkBook(Unknown Source)</span><br><span class="line">at com.fr.io.TemplateWorkBookIO.readTemplateWorkBook(Unknown Source)</span><br><span class="line">at com.fr.fs.web.service.favoriteparams.FavoriteParamsJob.readTempalteId(Unknown Source)</span><br><span class="line">at com.fr.fs.web.service.favoriteparams.FavoriteParamsJob.getAllFileTemplateId(Unknown Source)</span><br><span class="line">at com.fr.fs.web.service.favoriteparams.FavoriteParamsJob.getAllFileTemplateId(Unknown Source)</span><br><span class="line">at com.fr.fs.web.service.favoriteparams.FavoriteParamsJob.clearDatabaseData(Unknown Source)</span><br><span class="line">at com.fr.fs.web.service.favoriteparams.FavoriteParamsJob.access$<span class="number">200</span>(Unknown Source)</span><br><span class="line">at com.fr.fs.web.service.favoriteparams.FavoriteParamsJob$<span class="number">2</span>.run(Unknown Source)</span><br><span class="line">at java.util.TimerThread.mainLoop(Timer.java:<span class="number">555</span>)</span><br><span class="line">at java.util.TimerThread.run(Timer.java:<span class="number">505</span>)</span><br></pre></td></tr></table></figure></p><p>三.有一些Runnable状态的线程可以忽略<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">"http-nio-10030-ClientPoller-0" #122 daemon prio=5 os_prio=0 tid=0x00007f8de038b800 nid=0x13c1 runnable [0x00007f8d3f8e9000]</span><br><span class="line">java.lang.Thread.State: RUNNABLE</span><br><span class="line">at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)</span><br><span class="line">at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:<span class="number">269</span>)</span><br><span class="line">at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:<span class="number">93</span>)</span><br><span class="line">at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:<span class="number">86</span>)</span><br><span class="line">- locked <<span class="number">0x00000000829a3bb8</span>> (a sun.nio.ch.Util$<span class="number">3</span>)</span><br><span class="line">- locked <<span class="number">0x00000000829a3ba8</span>> (a java.util.Collections$UnmodifiableSet)</span><br><span class="line">- locked <<span class="number">0x00000000829a38c8</span>> (a sun.nio.ch.EPollSelectorImpl)</span><br><span class="line">at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:<span class="number">97</span>)</span><br><span class="line">at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:<span class="number">825</span>)</span><br><span class="line">at java.lang.Thread.run(Thread.java:<span class="number">748</span>)</span><br></pre></td></tr></table></figure></p><p>这个是nio的EpollWait,可以忽略</p>]]></content>
<summary type="html">
<p>ThreadDump中包含了线程的状态信息,一般有 Runnable,BLOCKED,Wait几种状态。Wait一般都是线程池里面的线程,可以跳过,主要关注点应该放在Runnable和BLOCKED这两种状态。
</summary>
<category term="programming" scheme="http://yoursite.com/categories/programming/"/>
</entry>
<entry>
<title>无锡</title>
<link href="http://yoursite.com/2016/06/28/%E6%97%A0%E9%94%A1/"/>
<id>http://yoursite.com/2016/06/28/无锡/</id>
<published>2016-06-28T09:37:01.000Z</published>
<updated>2019-07-07T10:19:31.423Z</updated>
<content type="html"><![CDATA[<h2 id="一座小城,无事可记"><a href="#一座小城,无事可记" class="headerlink" title="一座小城,无事可记 = ="></a>一座小城,无事可记 = =</h2>]]></content>
<summary type="html">
<h2 id="一座小城,无事可记"><a href="#一座小城,无事可记" class="headerlink" title="一座小城,无事可记 = ="></a>一座小城,无事可记 = =</h2>
</summary>
<category term="life" scheme="http://yoursite.com/categories/life/"/>
<category term="日常" scheme="http://yoursite.com/tags/%E6%97%A5%E5%B8%B8/"/>
</entry>
<entry>
<title>RSA加密解密以及秘钥的生成</title>
<link href="http://yoursite.com/2016/01/14/rsa/"/>
<id>http://yoursite.com/2016/01/14/rsa/</id>
<published>2016-01-14T01:31:00.000Z</published>
<updated>2019-07-05T09:47:30.688Z</updated>
<content type="html"><![CDATA[<a id="more"></a><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.crypto.Cipher;</span><br><span class="line"><span class="keyword">import</span> java.security.KeyPair;</span><br><span class="line"><span class="keyword">import</span> java.security.KeyPairGenerator;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Demo</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String... args)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> d();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">d</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//生成密钥对</span></span><br><span class="line"> KeyPairGenerator keyGen = KeyPairGenerator.getInstance(<span class="string">"RSA"</span>);</span><br><span class="line"> keyGen.initialize(<span class="number">2048</span>);</span><br><span class="line"> KeyPair key = keyGen.generateKeyPair();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 公钥</span></span><br><span class="line"> <span class="keyword">byte</span>[] pub = key.getPublic().getEncoded();</span><br><span class="line"> <span class="comment">// 私钥</span></span><br><span class="line"> <span class="keyword">byte</span>[] pri = key.getPrivate().getEncoded();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 原文</span></span><br><span class="line"> <span class="keyword">byte</span>[] plainText = <span class="string">"你看看"</span>.getBytes();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//加密工具</span></span><br><span class="line"> Cipher c1 = Cipher.getInstance(<span class="string">"RSA"</span>);</span><br><span class="line"> c1.init(Cipher.ENCRYPT_MODE, key.getPrivate());</span><br><span class="line"> <span class="keyword">byte</span>[] cipherText = c1.doFinal(plainText);</span><br><span class="line"> </span><br><span class="line"> c1.init(Cipher.DECRYPT_MODE, key.getPublic());</span><br><span class="line"> <span class="keyword">byte</span>[] output = c1.doFinal(cipherText);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<a id="more"></a>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</spa
</summary>
<category term="programming" scheme="http://yoursite.com/categories/programming/"/>
<category term="encrypt" scheme="http://yoursite.com/tags/encrypt/"/>
</entry>
</feed>