<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[你假笨]]></title>
  <link href="http://nijiaben.github.io/atom.xml" rel="self"/>
  <link href="http://nijiaben.github.io/"/>
  <updated>2016-11-10T09:42:31+08:00</updated>
  <id>http://nijiaben.github.io/</id>
  <author>
    <name><![CDATA[你假笨]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之String.intern()导致的YGC不断变长]]></title>
    <link href="http://nijiaben.github.io/blog/2016/11/06/string-intern/"/>
    <updated>2016-11-06T09:37:18+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/11/06/string-intern</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>之所以想写这篇文章，是因为YGC过程对我们来说太过于黑盒，如果对YGC过程不是很熟悉，这类问题基本很难定位，我们就算开了GC日志，也最多能看到类似下面的日志</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[GC (Allocation Failure) [ParNew: 91807K-&gt;10240K(92160K), 0.0538384 secs] 91807K-&gt;21262K(2086912K), 0.0538680 secs] [Times: user=0.16 sys=0.06, real=0.06 secs] 
</span></code></pre></td></tr></table></div></figure>


<p>只知道耗了多长时间，但是具体耗在了哪个阶段，是基本看不出来的，所以要么就是靠经验来定位，要么就是对代码相当熟悉，脑袋里过一遍整个过程，看哪个阶段最可能，今天要讲的这个大家可以当做今后排查这类问题的一个经验来使，这个当然不是唯一导致YGC过长的一个原因，但却是最近我帮忙定位碰到的发生相对来说比较多的一个场景</p>

<!--more-->


<p>具体的定位是通过在JVM代码里进行了日志埋点确定的，这个问题其实最早的时候，是帮助毕玄毕大师定位到这块的问题，他也在公众号里对这个问题写了相关的一篇文章<a href="" title="http://mp.weixin.qq.com/s?__biz=MjM5MzYzMzkyMQ==&amp;mid=2649826309&amp;idx=1&amp;sn=13518f7f693d78fd19f8dc5d21c7eb4b&amp;scene=0#wechat_redirect">YGC越来越慢，为什么</a>，大家可以关注下毕大师的公众号<code>HelloJava</code>，经常会发一些在公司碰到的诡异问题的排查，相信会让你涨姿势的，当然如果你还没有关注我的公众号<code>你假笨</code>，欢迎关注下，后续会时不时写点或许正巧你感兴趣的JVM系列文章。</p>

<h2>Demo</h2>

<p>先上一个demo，来描述下问题的情况，代码很简单，就是不断创建UUID，其实就是一个字符串，并将这个字符串调用下intern方法</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>import java.util.UUID;
</span><span class='line'>
</span><span class='line'>public class StringTableTest {
</span><span class='line'>  public static void main(String args[]) {
</span><span class='line'>      for (int i = 0; i &lt; 10000000; i++) {
</span><span class='line'>          uuid();
</span><span class='line'>      }
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>  public static void uuid() {
</span><span class='line'>      UUID.randomUUID().toString().intern();
</span><span class='line'>  }
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>我们使用的JVM参数如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>-XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -Xmx2G -Xms2G -Xmn100M</span></code></pre></td></tr></table></div></figure>


<p>这里特意将新生代设置比较小，老生代设置比较大，让代码在执行过程中更容易突出问题来，大量做ygc，期间不做CMS GC，于是我们得到的输出结果类似下面的</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[GC (Allocation Failure) [ParNew: 81920K-&gt;9887K(92160K), 0.0096027 secs] 81920K-&gt;9887K(2086912K), 0.0096428 secs] [Times: user=0.06 sys=0.01, real=0.01 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 91807K-&gt;10240K(92160K), 0.0538384 secs] 91807K-&gt;21262K(2086912K), 0.0538680 secs] [Times: user=0.16 sys=0.06, real=0.06 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10240K(92160K), 0.0190535 secs] 103182K-&gt;32655K(2086912K), 0.0190965 secs] [Times: user=0.12 sys=0.01, real=0.02 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10240K(92160K), 0.0198259 secs] 114575K-&gt;44124K(2086912K), 0.0198558 secs] [Times: user=0.13 sys=0.01, real=0.02 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10240K(92160K), 0.0213643 secs] 126044K-&gt;55592K(2086912K), 0.0213930 secs] [Times: user=0.14 sys=0.01, real=0.02 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10240K(92160K), 0.0234291 secs] 137512K-&gt;67061K(2086912K), 0.0234625 secs] [Times: user=0.16 sys=0.01, real=0.03 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10238K(92160K), 0.0243691 secs] 148981K-&gt;78548K(2086912K), 0.0244041 secs] [Times: user=0.15 sys=0.01, real=0.03 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0235310 secs] 160468K-&gt;89998K(2086912K), 0.0235587 secs] [Times: user=0.17 sys=0.01, real=0.02 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10240K(92160K), 0.0255960 secs] 171918K-&gt;101466K(2086912K), 0.0256264 secs] [Times: user=0.18 sys=0.01, real=0.03 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10238K(92160K), 0.0287876 secs] 183386K-&gt;113770K(2086912K), 0.0288188 secs] [Times: user=0.20 sys=0.01, real=0.03 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0298405 secs] 195690K-&gt;125267K(2086912K), 0.0298823 secs] [Times: user=0.20 sys=0.01, real=0.03 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0310182 secs] 207187K-&gt;136742K(2086912K), 0.0311156 secs] [Times: user=0.22 sys=0.01, real=0.03 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10239K(92160K), 0.0321647 secs] 218662K-&gt;148210K(2086912K), 0.0321938 secs] [Times: user=0.22 sys=0.01, real=0.03 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92159K-&gt;10238K(92160K), 0.0338090 secs] 230130K-&gt;159686K(2086912K), 0.0338446 secs] [Times: user=0.24 sys=0.01, real=0.03 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0326612 secs] 241606K-&gt;171159K(2086912K), 0.0326912 secs] [Times: user=0.23 sys=0.01, real=0.03 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0350578 secs] 253079K-&gt;182627K(2086912K), 0.0351077 secs] [Times: user=0.26 sys=0.01, real=0.04 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0346946 secs] 264547K-&gt;194096K(2086912K), 0.0347274 secs] [Times: user=0.25 sys=0.01, real=0.04 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10239K(92160K), 0.0384091 secs] 276016K-&gt;205567K(2086912K), 0.0384401 secs] [Times: user=0.27 sys=0.01, real=0.04 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92159K-&gt;10238K(92160K), 0.0394017 secs] 287487K-&gt;217035K(2086912K), 0.0394312 secs] [Times: user=0.29 sys=0.01, real=0.04 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0411447 secs] 298955K-&gt;228504K(2086912K), 0.0411748 secs] [Times: user=0.30 sys=0.01, real=0.04 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0393449 secs] 310424K-&gt;239972K(2086912K), 0.0393743 secs] [Times: user=0.29 sys=0.01, real=0.04 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0444541 secs] 321892K-&gt;251441K(2086912K), 0.0444887 secs] [Times: user=0.32 sys=0.01, real=0.05 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10239K(92160K), 0.0449196 secs] 333361K-&gt;262910K(2086912K), 0.0449557 secs] [Times: user=0.33 sys=0.01, real=0.05 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92159K-&gt;10238K(92160K), 0.0497517 secs] 344830K-&gt;274382K(2086912K), 0.0497946 secs] [Times: user=0.34 sys=0.01, real=0.05 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0475741 secs] 356302K-&gt;285851K(2086912K), 0.0476130 secs] [Times: user=0.35 sys=0.01, real=0.04 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0461098 secs] 367771K-&gt;297320K(2086912K), 0.0461421 secs] [Times: user=0.34 sys=0.01, real=0.05 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0508071 secs] 379240K-&gt;308788K(2086912K), 0.0508428 secs] [Times: user=0.38 sys=0.01, real=0.05 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10239K(92160K), 0.0494472 secs] 390708K-&gt;320257K(2086912K), 0.0494938 secs] [Times: user=0.36 sys=0.01, real=0.05 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92159K-&gt;10238K(92160K), 0.0531527 secs] 402177K-&gt;331725K(2086912K), 0.0531845 secs] [Times: user=0.39 sys=0.01, real=0.05 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0543701 secs] 413645K-&gt;343194K(2086912K), 0.0544025 secs] [Times: user=0.41 sys=0.01, real=0.05 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0528003 secs] 425114K-&gt;354663K(2086912K), 0.0528283 secs] [Times: user=0.39 sys=0.01, real=0.06 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10239K(92160K), 0.0565080 secs] 436583K-&gt;366131K(2086912K), 0.0565394 secs] [Times: user=0.42 sys=0.01, real=0.06 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92159K-&gt;10238K(92160K), 0.0597181 secs] 448051K-&gt;377600K(2086912K), 0.0597653 secs] [Times: user=0.44 sys=0.01, real=0.06 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0606671 secs] 459520K-&gt;389068K(2086912K), 0.0607423 secs] [Times: user=0.46 sys=0.01, real=0.06 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0590389 secs] 470988K-&gt;400539K(2086912K), 0.0590679 secs] [Times: user=0.43 sys=0.01, real=0.05 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0600462 secs] 482459K-&gt;412008K(2086912K), 0.0600757 secs] [Times: user=0.44 sys=0.01, real=0.06 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10239K(92160K), 0.0608772 secs] 493928K-&gt;423476K(2086912K), 0.0609170 secs] [Times: user=0.45 sys=0.01, real=0.06 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92159K-&gt;10238K(92160K), 0.0622107 secs] 505396K-&gt;434945K(2086912K), 0.0622391 secs] [Times: user=0.46 sys=0.00, real=0.06 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0626555 secs] 516865K-&gt;446413K(2086912K), 0.0626872 secs] [Times: user=0.47 sys=0.01, real=0.07 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0647713 secs] 528333K-&gt;457882K(2086912K), 0.0648013 secs] [Times: user=0.47 sys=0.00, real=0.07 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0747113 secs] 539802K-&gt;469353K(2086912K), 0.0747446 secs] [Times: user=0.51 sys=0.01, real=0.07 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10239K(92160K), 0.0727498 secs] 551273K-&gt;480832K(2086912K), 0.0727899 secs] [Times: user=0.52 sys=0.01, real=0.07 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92159K-&gt;10238K(92160K), 0.0734084 secs] 562752K-&gt;492300K(2086912K), 0.0734402 secs] [Times: user=0.54 sys=0.01, real=0.08 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0766368 secs] 574220K-&gt;503769K(2086912K), 0.0766673 secs] [Times: user=0.55 sys=0.01, real=0.07 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0778940 secs] 585689K-&gt;515237K(2086912K), 0.0779250 secs] [Times: user=0.56 sys=0.01, real=0.08 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0815513 secs] 597157K-&gt;526712K(2086912K), 0.0815824 secs] [Times: user=0.57 sys=0.01, real=0.08 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10239K(92160K), 0.0812080 secs] 608632K-&gt;538181K(2086912K), 0.0812406 secs] [Times: user=0.58 sys=0.01, real=0.08 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92159K-&gt;10238K(92160K), 0.0818790 secs] 620101K-&gt;549651K(2086912K), 0.0819155 secs] [Times: user=0.60 sys=0.01, real=0.08 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0840677 secs] 631571K-&gt;561122K(2086912K), 0.0841000 secs] [Times: user=0.61 sys=0.01, real=0.08 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0842462 secs] 643042K-&gt;572593K(2086912K), 0.0842785 secs] [Times: user=0.61 sys=0.01, real=0.08 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0875011 secs] 654513K-&gt;584076K(2086912K), 0.0875416 secs] [Times: user=0.62 sys=0.01, real=0.08 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10240K(92160K), 0.0887645 secs] 665996K-&gt;595532K(2086912K), 0.0887956 secs] [Times: user=0.64 sys=0.01, real=0.09 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10240K(92160K), 0.0921844 secs] 677452K-&gt;607001K(2086912K), 0.0922153 secs] [Times: user=0.65 sys=0.01, real=0.09 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10238K(92160K), 0.0930053 secs] 688921K-&gt;618471K(2086912K), 0.0930380 secs] [Times: user=0.67 sys=0.01, real=0.10 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0955379 secs] 700391K-&gt;629942K(2086912K), 0.0955873 secs] [Times: user=0.69 sys=0.01, real=0.10 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0919127 secs] 711862K-&gt;641411K(2086912K), 0.0919528 secs] [Times: user=0.68 sys=0.01, real=0.09 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0942291 secs] 723331K-&gt;652879K(2086912K), 0.0942611 secs] [Times: user=0.70 sys=0.00, real=0.09 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10239K(92160K), 0.0951904 secs] 734799K-&gt;664348K(2086912K), 0.0952265 secs] [Times: user=0.71 sys=0.00, real=0.10 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92159K-&gt;10238K(92160K), 0.0963585 secs] 746268K-&gt;675816K(2086912K), 0.0963909 secs] [Times: user=0.72 sys=0.01, real=0.10 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0969504 secs] 757736K-&gt;687285K(2086912K), 0.0969843 secs] [Times: user=0.72 sys=0.01, real=0.09 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.0999066 secs] 769205K-&gt;698753K(2086912K), 0.0999376 secs] [Times: user=0.75 sys=0.01, real=0.10 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10240K(92160K), 0.0998371 secs] 780673K-&gt;710222K(2086912K), 0.0998835 secs] [Times: user=0.75 sys=0.00, real=0.10 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92160K-&gt;10239K(92160K), 0.1024616 secs] 792142K-&gt;721691K(2086912K), 0.1024927 secs] [Times: user=0.77 sys=0.01, real=0.10 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92159K-&gt;10238K(92160K), 0.1015041 secs] 803611K-&gt;733159K(2086912K), 0.1015378 secs] [Times: user=0.76 sys=0.01, real=0.10 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.1058214 secs] 815079K-&gt;744628K(2086912K), 0.1058532 secs] [Times: user=0.80 sys=0.01, real=0.10 secs] 
</span><span class='line'>[GC (Allocation Failure) [ParNew: 92158K-&gt;10238K(92160K), 0.1061273 secs] 826548K-&gt;756096K(2086912K), 0.1061620 secs] [Times: user=0.80 sys=0.01, real=0.10 secs] </span></code></pre></td></tr></table></div></figure>


<p>有没有发现YGC不断发生，并且发生的时间不断在增长，从10ms慢慢增长到了100ms，甚至还会继续涨下去</p>

<h2>String.intern方法</h2>

<p>从上面的demo我们能挖掘到的可能就是intern这个方法了，那我们先来了解下intern方法的实现，这是String提供的一个方法，jvm提供这个方法的目的是希望对于某个同名字符串使用非常多的场景，在jvm里只保留一份，比如我们不断new String(&ldquo;a&rdquo;)，其实在java heap里会有多个String的对象，并且值都是a，如果我们只希望内存里只保留一个a，或者希望我接下来用到的地方都返回同一个a，那就可以用String.intern这个方法了，用法如下</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>String a = "a".intern();
</span><span class='line'>...
</span><span class='line'>String b = a.intern();
</span></code></pre></td></tr></table></div></figure>


<p>这样b和a都是指向内存里的同一个String对象，那JVM里到底怎么做到的呢？</p>

<p>我们看到intern这个方法其实是一个native方法，具体对应到JVM里的逻辑是</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>
</span><span class='line'>oop StringTable::intern(oop string, TRAPS)
</span><span class='line'>{
</span><span class='line'>  if (string == NULL) return NULL;
</span><span class='line'>  ResourceMark rm(THREAD);
</span><span class='line'>  int length;
</span><span class='line'>  Handle h_string (THREAD, string);
</span><span class='line'>  jchar* chars = java_lang_String::as_unicode_string(string, length);
</span><span class='line'>  oop result = intern(h_string, chars, length, CHECK_NULL);
</span><span class='line'>  return result;
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>oop StringTable::intern(Handle string_or_null, jchar* name,
</span><span class='line'>                        int len, TRAPS) {
</span><span class='line'>  unsigned int hashValue = hash_string(name, len);
</span><span class='line'>  int index = the_table()-&gt;hash_to_index(hashValue);
</span><span class='line'>  oop found_string = the_table()-&gt;lookup(index, name, len, hashValue);
</span><span class='line'>
</span><span class='line'>  // Found
</span><span class='line'>  if (found_string != NULL) return found_string;
</span><span class='line'>
</span><span class='line'>  debug_only(StableMemoryChecker smc(name, len * sizeof(name[0])));
</span><span class='line'>  assert(!Universe::heap()-&gt;is_in_reserved(name) || GC_locker::is_active(),
</span><span class='line'>         "proposed name of symbol must be stable");
</span><span class='line'>
</span><span class='line'>  Handle string;
</span><span class='line'>  // try to reuse the string if possible
</span><span class='line'>  if (!string_or_null.is_null() && (!JavaObjectsInPerm || string_or_null()-&gt;is_perm())) {
</span><span class='line'>    string = string_or_null;
</span><span class='line'>  } else {
</span><span class='line'>    string = java_lang_String::create_tenured_from_unicode(name, len, CHECK_NULL);
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>  // Grab the StringTable_lock before getting the_table() because it could
</span><span class='line'>  // change at safepoint.
</span><span class='line'>  MutexLocker ml(StringTable_lock, THREAD);
</span><span class='line'>
</span><span class='line'>  // Otherwise, add to symbol to table
</span><span class='line'>  return the_table()-&gt;basic_add(index, string, name, len,
</span><span class='line'>                                hashValue, CHECK_NULL);
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>也就是说是其实在JVM里存在一个叫做StringTable的数据结构，这个数据结构是一个Hashtable，在我们调用String.intern的时候其实就是先去这个StringTable里查找是否存在一个同名的项，如果存在就直接返回对应的对象，否则就往这个table里插入一项，指向这个String对象，那么再下次通过intern再来访问同名的String对象的时候，就会返回上次插入的这一项指向的String对象</p>

<p>至此大家应该知道其原理了，另外我这里还想说个题外话，记得几年前tomcat里爆发的一个HashMap导致的hash碰撞的问题，这里其实也是一个Hashtable，所以也还是存在类似的风险，不过JVM里提供一个参数专门来控制这个table的size，<code>-XX:StringTableSize</code>，这个参数的默认值如下</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>product(uintx, StringTableSize, NOT_LP64(1009) LP64_ONLY(60013),          \
</span><span class='line'>          "Number of buckets in the interned String table")                 \</span></code></pre></td></tr></table></div></figure>


<p>另外JVM还会根据hash碰撞的情况来决定是否做rehash，比如你从这个StringTable里查找某个字符串是否存在，如果对其对应的桶挨个遍历，超过了100个还是没有找到对应的同名的项，那就会设置一个flag，让下次进入到safepoint的时候做一次rehash动作，尽量减少碰撞的发生，但是当恶化到一定程度的时候，其实也没啥办法啦，因为你的数据量实在太大，桶子数就那么多，那每个桶再怎么均匀也会带着一个很长的链表，所以此时我们通过修改上面的StringTableSize将桶数变大，可能会一定程度上缓解，但是如果是java代码的问题导致泄露，那就只能定位到具体的代码进行改造了。</p>

<h2>StringTable为什么会影响YGC</h2>

<p>YGC的过程我不打算再这篇文章里细说，因为我希望尽量保持每篇文章的内容不过于臃肿，有机会可以单独写篇文章来介绍，我这里将列出ygc过程里StringTable这块的具体代码</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>  if (!_process_strong_tasks-&gt;is_task_claimed(SH_PS_StringTable_oops_do)) {
</span><span class='line'>    if (so & SO_Strings || (!collecting_perm_gen && !JavaObjectsInPerm)) {
</span><span class='line'>      StringTable::oops_do(roots);
</span><span class='line'>    }
</span><span class='line'>    if (JavaObjectsInPerm) {
</span><span class='line'>      // Verify the string table contents are in the perm gen
</span><span class='line'>      NOT_PRODUCT(StringTable::oops_do(&assert_is_perm_closure));
</span><span class='line'>    }
</span><span class='line'>  }</span></code></pre></td></tr></table></div></figure>


<p>因为YGC过程不涉及到对perm做回收，因此<code>collecting_perm_gen</code>是false，而<code>JavaObjectsInPerm</code>默认情况下也是false，表示String.intern返回的字符串是不是在perm里分配，如果是false，表示是在heap里分配的，因此StringTable指向的字符串是在heap里分配的，所以ygc过程需要对StringTable做扫描，以保证处于新生代的String代码不会被回收掉</p>

<p>至此大家应该明白了为什么YGC过程会对StringTable扫描</p>

<p>有了这一层意思之后，YGC的时间长短和扫描StringTable有关也可以理解了，设想一下如果StringTable非常庞大，那是不是意味着YGC过程扫描的时间也会变长呢</p>

<h2>YGC过程扫描StringTable对CPU影响大吗</h2>

<p>这个问题其实是我写这文章的时候突然问自己的一个问题，于是稍微想了下来跟大家解释下，因为大家也可能会问这么个问题</p>

<p>要回答这个问题我首先得问你们的机器到底有多少个核，如果核数很多的话，其实影响不是很大，因为这个扫描的过程是单个GC线程来做的，所以最多消耗一个核，因此看起来对于核数很多的情况，基本不算什么</p>

<h2>StringTable什么时候清理</h2>

<p>YGC过程不会对StringTable做清理，这也就是我们demo里的情况会让Stringtable越来越大，因为到目前为止还只看到YGC过程，但是在Full GC或者CMS GC过程会对StringTable做清理，具体验证很简单，执行下<code>jmap -histo:live &lt;pid&gt;</code>，你将会发现YGC的时候又降下去了</p>

<h2>本文写作时间</h2>

<p>利用午饭前的一点时间写下 2016/11/06 12:00~12:40</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之不保证顺序的Class.getMethods]]></title>
    <link href="http://nijiaben.github.io/blog/2016/11/02/class-getmethods/"/>
    <updated>2016-11-02T09:39:29+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/11/02/class-getmethods</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>本文要说的内容是今天公司有个线上系统踩了一个坑，并且貌似还造成了一定的影响，后来系统相关的人定位到了是<code>java.lang.Class.getMethods</code>返回的顺序可能不同机器不一样，有问题的机器和没问题的机器这个返回的方法列表是不一样的，后面他们就来找到我求证是否jdk里有这潜规则</p>

<p>本来这个问题简单一句话就可以说明白，所以在晚上推送的消息里也将这个事实告诉了大家，大家知道就好，以后不要再掉到坑里去了，但是这个要细说起来其实也值得一说，于是在消息就附加了征求大家意见的内容，看大家是否有兴趣或者是否踩到过此坑，没想到有这么多人响应，表示对这个话题很感兴趣，并且总结了大家问得最多的两个问题是</p>

<ul>
<li>为什么有代码需要依赖这个顺序</li>
<li>jvm里为什么不保证顺序</li>
</ul>


<p>那这篇文章主要就针对这两个问题展开说一下，另外以后针对此类可写可不写的文章先征求下大家的意见再来写可能效果会更好点，一来可以回答大家的一些疑问（当然有些问题我也可能回答不上来，不过我尽量去通读代码回答好大家），二来希望对我公众号里的文章继续保持<code>不求最多，只求最精</code>的态度。</p>

<p>为了不辜负大家的热情，我连夜赶写了这篇文章，如果大家觉得我写的这些文章对大家有帮助，希望您能将文章分享出去，同时将我的公众号<code>你假笨</code>推荐给您身边更多的技术人，能帮助到更多的人去了解更多的细节，在下在此先谢过。</p>

<!--more-->


<h2>依赖顺序的场景</h2>

<p>如果大家看过或者实现过序列化反序列化的代码，这个问题就不难回答了，今天碰到的这个问题其实是发生在大家可能最常用的<code>fastjson</code>库里的，所以如果大家在使用这个库，请务必检查下你的代码，以免踩到此坑</p>

<h3>对象序列化</h3>

<p>大家都知道当我们序列化好一个对象之后，要反序列回来，那问题就来了，就拿这个json序列化来说吧，我们要将对象序列化成json串，那意味着我们要先取出这个对象的属性，然后写成键值对的形式，那取值就意味着我们要遵循java bean的规范通过getter方法来取，那其实getter方法有两种，一种是boolean类型的，一种是其他类型的，如果是boolean类型的，那我们通常是<code>isXXX()</code>这样的方法，如果是其他类型的，一般是<code>getXXX()</code>这样的方法。那假如说我们的类里针对某个属性a，同时存在两个方法<code>isA()</code>和<code>getA()</code>，那究竟我们会调用哪个来取值？这个就取决于具体的序列化框架实现了，比如导致我们这篇文章诞生的<code>fastjson</code>，就是利用我们这篇文章的主角<code>java.lang.Class.getMethods</code>返回的数组，然后挨个遍历，先找到哪个就是哪个，如果我们的这个数组正好因为jvm本身实现没有保证顺序，那么可能先找到<code>isA()</code>，也可能先找到<code>getA()</code>，如果两个方法都是返回a这个属性其实问题也不大，假如正好是这两个方法返回不同的内容呢？</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>private A a;
</span><span class='line'>
</span><span class='line'>public A getA(){
</span><span class='line'>  return a;
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>public boolean isA(){
</span><span class='line'>  return false;
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>public void setA(A a){
</span><span class='line'>  this.a=a;
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>如果是上面的内容，那可能就会悲剧了，如果选了<code>isA()</code>，那其实是返回一个boolean类型的，将这个boolean写入到json串里，如果是选了<code>getA()</code>，那就是将A这个类型的对象写到json串里</p>

<h3>对象反序列化</h3>

<p>在完成了序列化过程之后，需要将这个字符串进行反序列化了，于是就会去找json串里对应字段的setter方法，比如上面的<code>setA(A a)</code>，假如我们之前选了<code>isA()</code>序列化好内容，那我们此时的值是一个boolean值false，那就无法通过<code>setA</code>来赋值还原对象了。</p>

<h3>解决方案</h3>

<p>相信大家看完我上面的描述，知道这个问题所在了，要避免类似的问题，方案其实也挺多，比如对方法进行先排序，又比如说优先使用<code>isXXX()</code>方法，不过这种需要和开发者达成共识，和setter要对应得起来</p>

<h2>jvm里为什么不保证顺序</h2>

<p>JDK层面的代码我就暂时不说了，大家都能看到代码，从<code>java.lang.Class.getMethods</code>一层层走下去，相信大家细心点还是能抓住整个脉络的，我这里主要想说大家可能比较难看到的一些实现，比如JVM里的具体实现</p>

<p>正常情况下大家跟代码能跟到调用了<code>java.lang.Class.getDeclaredMethods0</code>这个native方法，其具体实现如下</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredMethods(JNIEnv *env, jclass ofClass, jboolean publicOnly))
</span><span class='line'>{
</span><span class='line'>  JVMWrapper("JVM_GetClassDeclaredMethods");
</span><span class='line'>  return get_class_declared_methods_helper(env, ofClass, publicOnly,
</span><span class='line'>                                           /*want_constructor*/ false,
</span><span class='line'>                                           SystemDictionary::reflect_Method_klass(), THREAD);
</span><span class='line'>}
</span><span class='line'>JVM_END</span></code></pre></td></tr></table></div></figure>


<p>其主要调用了<code>get_class_declared_methods_helper</code>方法</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>static jobjectArray get_class_declared_methods_helper(
</span><span class='line'>                                  JNIEnv *env,
</span><span class='line'>                                  jclass ofClass, jboolean publicOnly,
</span><span class='line'>                                  bool want_constructor,
</span><span class='line'>                                  Klass* klass, TRAPS) {
</span><span class='line'>
</span><span class='line'>  JvmtiVMObjectAllocEventCollector oam;
</span><span class='line'>
</span><span class='line'>  // Exclude primitive types and array types
</span><span class='line'>  if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass))
</span><span class='line'>      || java_lang_Class::as_Klass(JNIHandles::resolve_non_null(ofClass))-&gt;oop_is_array()) {
</span><span class='line'>    // Return empty array
</span><span class='line'>    oop res = oopFactory::new_objArray(klass, 0, CHECK_NULL);
</span><span class='line'>    return (jobjectArray) JNIHandles::make_local(env, res);
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>  instanceKlassHandle k(THREAD, java_lang_Class::as_Klass(JNIHandles::resolve_non_null(ofClass)));
</span><span class='line'>
</span><span class='line'>  // Ensure class is linked
</span><span class='line'>  k-&gt;link_class(CHECK_NULL);
</span><span class='line'>
</span><span class='line'>  Array&lt;Method*&gt;* methods = k-&gt;methods();
</span><span class='line'>  int methods_length = methods-&gt;length();
</span><span class='line'>
</span><span class='line'>  // Save original method_idnum in case of redefinition, which can change
</span><span class='line'>  // the idnum of obsolete methods.  The new method will have the same idnum
</span><span class='line'>  // but if we refresh the methods array, the counts will be wrong.
</span><span class='line'>  ResourceMark rm(THREAD);
</span><span class='line'>  GrowableArray&lt;int&gt;* idnums = new GrowableArray&lt;int&gt;(methods_length);
</span><span class='line'>  int num_methods = 0;
</span><span class='line'>
</span><span class='line'>  for (int i = 0; i &lt; methods_length; i++) {
</span><span class='line'>    methodHandle method(THREAD, methods-&gt;at(i));
</span><span class='line'>    if (select_method(method, want_constructor)) {
</span><span class='line'>      if (!publicOnly || method-&gt;is_public()) {
</span><span class='line'>        idnums-&gt;push(method-&gt;method_idnum());
</span><span class='line'>        ++num_methods;
</span><span class='line'>      }
</span><span class='line'>    }
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>  // Allocate result
</span><span class='line'>  objArrayOop r = oopFactory::new_objArray(klass, num_methods, CHECK_NULL);
</span><span class='line'>  objArrayHandle result (THREAD, r);
</span><span class='line'>
</span><span class='line'>  // Now just put the methods that we selected above, but go by their idnum
</span><span class='line'>  // in case of redefinition.  The methods can be redefined at any safepoint,
</span><span class='line'>  // so above when allocating the oop array and below when creating reflect
</span><span class='line'>  // objects.
</span><span class='line'>  for (int i = 0; i &lt; num_methods; i++) {
</span><span class='line'>    methodHandle method(THREAD, k-&gt;method_with_idnum(idnums-&gt;at(i)));
</span><span class='line'>    if (method.is_null()) {
</span><span class='line'>      // Method may have been deleted and seems this API can handle null
</span><span class='line'>      // Otherwise should probably put a method that throws NSME
</span><span class='line'>      result-&gt;obj_at_put(i, NULL);
</span><span class='line'>    } else {
</span><span class='line'>      oop m;
</span><span class='line'>      if (want_constructor) {
</span><span class='line'>        m = Reflection::new_constructor(method, CHECK_NULL);
</span><span class='line'>      } else {
</span><span class='line'>        m = Reflection::new_method(method, UseNewReflection, false, CHECK_NULL);
</span><span class='line'>      }
</span><span class='line'>      result-&gt;obj_at_put(i, m);
</span><span class='line'>    }
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>  return (jobjectArray) JNIHandles::make_local(env, result());
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>从上面的<code>k-&gt;method_with_idnum(idnums-&gt;at(i))</code>,我们基本知道方法主要是从klass里来的</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Method* InstanceKlass::method_with_idnum(int idnum) {
</span><span class='line'>  Method* m = NULL;
</span><span class='line'>  if (idnum &lt; methods()-&gt;length()) {
</span><span class='line'>    m = methods()-&gt;at(idnum);
</span><span class='line'>  }
</span><span class='line'>  if (m == NULL || m-&gt;method_idnum() != idnum) {
</span><span class='line'>    for (int index = 0; index &lt; methods()-&gt;length(); ++index) {
</span><span class='line'>      m = methods()-&gt;at(index);
</span><span class='line'>      if (m-&gt;method_idnum() == idnum) {
</span><span class='line'>        return m;
</span><span class='line'>      }
</span><span class='line'>    }
</span><span class='line'>    // None found, return null for the caller to handle.
</span><span class='line'>    return NULL;
</span><span class='line'>  }
</span><span class='line'>  return m;
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>因此InstanceKlass里的methods是关键，而这个methods的创建是在类解析的时候发生的</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
</span><span class='line'>                                                    ClassLoaderData* loader_data,
</span><span class='line'>                                                    Handle protection_domain,
</span><span class='line'>                                                    KlassHandle host_klass,
</span><span class='line'>                                                    GrowableArray&lt;Handle&gt;* cp_patches,
</span><span class='line'>                                                    TempNewSymbol& parsed_name,
</span><span class='line'>                                                    bool verify,
</span><span class='line'>                                                    TRAPS) {
</span><span class='line'>                                                    
</span><span class='line'>                                                    
</span><span class='line'>...
</span><span class='line'> Array&lt;Method*&gt;* methods = parse_methods(access_flags.is_interface(),
</span><span class='line'>                                            &promoted_flags,
</span><span class='line'>                                            &has_final_method,
</span><span class='line'>                                            &declares_default_methods,
</span><span class='line'>
</span><span class='line'>...                                            CHECK_(nullHandle));
</span><span class='line'>// sort methods
</span><span class='line'>intArray* method_ordering = sort_methods(methods); 
</span><span class='line'>...
</span><span class='line'>this_klass-&gt;set_methods(_methods);
</span><span class='line'>...
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>上面的<code>parse_methods</code>就是从class文件里挨个解析出method，并存到_methods字段里，但是接下来做了一次<code>sort_methods</code>的动作，这个动作会对解析出来的方法做排序</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>intArray* ClassFileParser::sort_methods(Array&lt;Method*&gt;* methods) {
</span><span class='line'>  int length = methods-&gt;length();
</span><span class='line'>  // If JVMTI original method ordering or sharing is enabled we have to
</span><span class='line'>  // remember the original class file ordering.
</span><span class='line'>  // We temporarily use the vtable_index field in the Method* to store the
</span><span class='line'>  // class file index, so we can read in after calling qsort.
</span><span class='line'>  // Put the method ordering in the shared archive.
</span><span class='line'>  if (JvmtiExport::can_maintain_original_method_order() || DumpSharedSpaces) {
</span><span class='line'>    for (int index = 0; index &lt; length; index++) {
</span><span class='line'>      Method* m = methods-&gt;at(index);
</span><span class='line'>      assert(!m-&gt;valid_vtable_index(), "vtable index should not be set");
</span><span class='line'>      m-&gt;set_vtable_index(index);
</span><span class='line'>    }
</span><span class='line'>  }
</span><span class='line'>  // Sort method array by ascending method name (for faster lookups & vtable construction)
</span><span class='line'>  // Note that the ordering is not alphabetical, see Symbol::fast_compare
</span><span class='line'>  Method::sort_methods(methods);
</span><span class='line'>
</span><span class='line'>  intArray* method_ordering = NULL;
</span><span class='line'>  // If JVMTI original method ordering or sharing is enabled construct int
</span><span class='line'>  // array remembering the original ordering
</span><span class='line'>  if (JvmtiExport::can_maintain_original_method_order() || DumpSharedSpaces) {
</span><span class='line'>    method_ordering = new intArray(length);
</span><span class='line'>    for (int index = 0; index &lt; length; index++) {
</span><span class='line'>      Method* m = methods-&gt;at(index);
</span><span class='line'>      int old_index = m-&gt;vtable_index();
</span><span class='line'>      assert(old_index &gt;= 0 && old_index &lt; length, "invalid method index");
</span><span class='line'>      method_ordering-&gt;at_put(index, old_index);
</span><span class='line'>      m-&gt;set_vtable_index(Method::invalid_vtable_index);
</span><span class='line'>    }
</span><span class='line'>  }
</span><span class='line'>  return method_ordering;
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>// This is only done during class loading, so it is OK to assume method_idnum matches the methods() array
</span><span class='line'>// default_methods also uses this without the ordering for fast find_method
</span><span class='line'>void Method::sort_methods(Array&lt;Method*&gt;* methods, bool idempotent, bool set_idnums) {
</span><span class='line'>  int length = methods-&gt;length();
</span><span class='line'>  if (length &gt; 1) {
</span><span class='line'>    {
</span><span class='line'>      No_Safepoint_Verifier nsv;
</span><span class='line'>      QuickSort::sort&lt;Method*&gt;(methods-&gt;data(), length, method_comparator, idempotent);
</span><span class='line'>    }
</span><span class='line'>    // Reset method ordering
</span><span class='line'>    if (set_idnums) {
</span><span class='line'>      for (int i = 0; i &lt; length; i++) {
</span><span class='line'>        Method* m = methods-&gt;at(i);
</span><span class='line'>        m-&gt;set_method_idnum(i);
</span><span class='line'>        m-&gt;set_orig_method_idnum(i);
</span><span class='line'>      }
</span><span class='line'>    }
</span><span class='line'>  }
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>从上面的<code>Method::sort_methods</code>可以看出其实具体的排序算法是<code>method_comparator</code></p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>// Comparer for sorting an object array containing
</span><span class='line'>// Method*s.
</span><span class='line'>static int method_comparator(Method* a, Method* b) {
</span><span class='line'>  return a-&gt;name()-&gt;fast_compare(b-&gt;name());
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>比较的是两个方法的名字，但是这个名字不是一个字符串，而是一个Symbol对象，每个类或者方法名字都会对应一个Symbol对象，在这个名字第一次使用的时候构建，并且不是在java heap里分配的，比如jdk7里就是在c heap里通过malloc来分配的，jdk8里会在metaspace里分配</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>// Note: this comparison is used for vtable sorting only; it doesn't matter
</span><span class='line'>// what order it defines, as long as it is a total, time-invariant order
</span><span class='line'>// Since Symbol*s are in C_HEAP, their relative order in memory never changes,
</span><span class='line'>// so use address comparison for speed
</span><span class='line'>int Symbol::fast_compare(Symbol* other) const {
</span><span class='line'> return (((uintptr_t)this &lt; (uintptr_t)other) ? -1
</span><span class='line'>   : ((uintptr_t)this == (uintptr_t) other) ? 0 : 1);
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>从上面的fast_compare方法知道，其实对比的是地址的大小，因为Symbol对象是通过malloc来分配的，因此新分配的Symbol对象的地址就不一定比后分配的Symbol对象地址小，也不一定大，因为期间存在内存free的动作，那地址是不会一直线性变化的，之所以不按照字母排序，主要还是为了速度考虑，根据地址排序是最快的。</p>

<p>综上所述，一个类里的方法经过排序之后，顺序可能会不一样，取决于方法名对应的Symbol对象的地址的先后顺序</p>

<h3>JVM为什么要对方法排序</h3>

<p>其实这个问题很简单，就是为了快速找到方法呢，当我们要找某个名字的方法的时候，根据对应的Symbol对象，能根据对象的地址使用二分排序的算法快速定位到具体的方法。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之Metaspace解密]]></title>
    <link href="http://nijiaben.github.io/blog/2016/10/29/metaspace/"/>
    <updated>2016-10-29T15:38:00+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/10/29/metaspace</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>metaspace，顾名思义，元数据空间，专门用来存元数据的，它是jdk8里特有的数据结构用来替代perm，这块空间很有自己的特点，前段时间公司这块的问题太多了，主要是因为升级了中间件所致，看到大家讨论来讨论去，看得出很多人对metaspace还是模棱两可，不是很了解它，因此我觉得有必要写篇文章来介绍一下它，解开它神秘的面纱，当我们再次碰到它的相关问题的时候不会再感到束手无策。</p>

<p>通过这篇文章，你将可以了解到</p>

<ul>
<li>为什么会有metaspace</li>
<li>metaspace的组成</li>
<li>metaspace的VM参数</li>
<li>jstat里我们应该关注metaspace的哪些值</li>
</ul>


<!--more-->


<h2>为什么会有metaspace</h2>

<p>metaspace的由来民间已有很多传说，不过我这里只谈我自己的理解，因为我不是oracle参与这块的开发者，所以对其真正的由来不怎么了解。</p>

<p>我们都知道jdk8之前有perm这一整块内存来存klass等信息，我们的参数里也必不可少地会配置-XX:PermSize以及-XX:MaxPermSize来控制这块内存的大小，jvm在启动的时候会根据这些配置来分配一块连续的内存块，但是随着动态类加载的情况越来越多，这块内存我们变得不太可控，到底设置多大合适是每个开发者要考虑的问题，如果设置太小了，系统运行过程中就容易出现内存溢出，设置大了又总感觉浪费，尽管不会实质分配这么大的物理内存。基于这么一个可能的原因，于是metaspace出现了，希望内存的管理不再受到限制，也不要怎么关注元数据这块的OOM问题，虽然到目前来看，也并没有完美地解决这个问题。</p>

<p>或许从JVM代码里也能看出一些端倪来，比如<code>MaxMetaspaceSize</code>默认值很大，<code>CompressedClassSpaceSize</code>默认也有1G，从这些参数我们能猜到metaspace的作者不希望出现它相关的OOM问题。</p>

<h2>metaspace的组成</h2>

<p>metaspace其实由两大部分组成</p>

<ul>
<li>Klass Metaspace</li>
<li>NoKlass Metaspace</li>
</ul>


<p>Klass Metaspace就是用来存klass的，klass是我们熟知的class文件在jvm里的运行时数据结构，不过有点要提的是我们看到的类似A.class其实是存在heap里的，是java.lang.Class的一个对象实例。这块内存是紧接着Heap的，和我们之前的perm一样，这块内存大小可通过<code>-XX:CompressedClassSpaceSize</code>参数来控制，这个参数前面提到了默认是1G，但是这块内存也可以没有，假如没有开启压缩指针就不会有这块内存，这种情况下klass都会存在NoKlass Metaspace里，另外如果我们把-Xmx设置大于32G的话，其实也是没有这块内存的，因为会这么大内存会关闭压缩指针开关。还有就是这块内存最多只会存在一块。</p>

<p>NoKlass Metaspace专门来存klass相关的其他的内容，比如method，constantPool等，这块内存是由多块内存组合起来的，所以可以认为是不连续的内存块组成的。这块内存是必须的，虽然叫做NoKlass Metaspace，但是也其实可以存klass的内容，上面已经提到了对应场景。</p>

<p>Klass Metaspace和NoKlass Mestaspace都是所有classloader共享的，所以类加载器们要分配内存，但是每个类加载器都有一个SpaceManager，来管理属于这个类加载的内存小块。如果Klass Metaspace用完了，那就会OOM了，不过一般情况下不会，NoKlass Mestaspace是由一块块内存慢慢组合起来的，在没有达到限制条件的情况下，会不断加长这条链，让它可以持续工作。</p>

<h2>metaspace的几个参数</h2>

<p>如果我们要改变metaspace的一些行为，我们一般会对其相关的一些参数做调整，因为metaspace的参数本身不是很多，所以我这里将涉及到的所有参数都做一个介绍，也许好些参数大家都是有误解的</p>

<ul>
<li>UseLargePagesInMetaspace</li>
<li>InitialBootClassLoaderMetaspaceSize</li>
<li>MetaspaceSize</li>
<li>MaxMetaspaceSize</li>
<li>CompressedClassSpaceSize</li>
<li>MinMetaspaceExpansion</li>
<li>MaxMetaspaceExpansion</li>
<li>MinMetaspaceFreeRatio</li>
<li>MaxMetaspaceFreeRatio</li>
</ul>


<h3>UseLargePagesInMetaspace</h3>

<p>默认false，这个参数是说是否在metaspace里使用LargePage，一般情况下我们使用4KB的page size，这个参数依赖于UseLargePages这个参数开启，不过这个参数我们一般不开。</p>

<h3>InitialBootClassLoaderMetaspaceSize</h3>

<p>64位下默认4M，32位下默认2200K，metasapce前面已经提到主要分了两大块，Klass Metaspace以及NoKlass Metaspace，而NoKlass Metaspace是由一块块内存组合起来的，这个参数决定了NoKlass Metaspace的第一个内存Block的大小，即2*InitialBootClassLoaderMetaspaceSize，同时为bootstrapClassLoader的第一块内存chunk分配了InitialBootClassLoaderMetaspaceSize的大小</p>

<h3>MetaspaceSize</h3>

<p>默认20.8M左右(x86下开启c2模式)，主要是控制metaspaceGC发生的初始阈值，也是最小阈值，但是触发metaspaceGC的阈值是不断变化的，与之对比的主要是指Klass Metaspace与NoKlass Metaspace两块committed的内存和。</p>

<h3>MaxMetaspaceSize</h3>

<p>默认基本是无穷大，但是我还是建议大家设置这个参数，因为很可能会因为没有限制而导致metaspace被无止境使用(一般是内存泄漏)而被OS Kill。这个参数会限制metaspace(包括了Klass Metaspace以及NoKlass Metaspace)被committed的内存大小，会保证committed的内存不会超过这个值，一旦超过就会触发GC，这里要注意和MaxPermSize的区别，MaxMetaspaceSize并不会在jvm启动的时候分配一块这么大的内存出来，而MaxPermSize是会分配一块这么大的内存的。</p>

<h3>CompressedClassSpaceSize</h3>

<p>默认1G，这个参数主要是设置Klass Metaspace的大小，不过这个参数设置了也不一定起作用，前提是能开启压缩指针，假如-Xmx超过了32G，压缩指针是开启不来的。如果有Klass Metaspace，那这块内存是和Heap连着的。</p>

<h3>MinMetaspaceExpansion</h3>

<p>MinMetaspaceExpansion和MaxMetaspaceExpansion这两个参数或许和大家认识的并不一样，也许很多人会认为这两个参数不就是内存不够的时候，然后扩容的最小大小吗？其实不然</p>

<p>这两个参数和扩容其实并没有直接的关系，也就是并不是为了增大committed的内存，而是为了增大触发metaspace GC的阈值</p>

<p>这两个参数主要是在比较特殊的场景下救急使用，比如gcLocker或者<code>should_concurrent_collect</code>的一些场景，因为这些场景下接下来会做一次GC，相信在接下来的GC中可能会释放一些metaspace的内存，于是先临时扩大下metaspace触发GC的阈值，而有些内存分配失败其实正好是因为这个阈值触顶导致的，于是可以通过增大阈值暂时绕过去</p>

<p>默认332.8K，增大触发metaspace GC阈值的最小要求。假如我们要救急分配的内存很小，没有达到MinMetaspaceExpansion，但是我们会将这次触发metaspace GC的阈值提升MinMetaspaceExpansion，之所以要大于这次要分配的内存大小主要是为了防止别的线程也有类似的请求而频繁触发相关的操作，不过如果要分配的内存超过了MaxMetaspaceExpansion，那MinMetaspaceExpansion将会是要分配的内存大小基础上的一个增量</p>

<h3>MaxMetaspaceExpansion</h3>

<p>默认5.2M，增大触发metaspace GC阈值的最大要求。假如说我们要分配的内存超过了MinMetaspaceExpansion但是低于MaxMetaspaceExpansion，那增量是MaxMetaspaceExpansion，如果超过了MaxMetaspaceExpansion，那增量是MinMetaspaceExpansion加上要分配的内存大小</p>

<p>注：每次分配只会给对应的线程一次扩展触发metaspace GC阈值的机会，如果扩展了，但是还不能分配，那就只能等着做GC了</p>

<h3>MinMetaspaceFreeRatio</h3>

<p>MinMetaspaceFreeRatio和下面的MaxMetaspaceFreeRatio，主要是影响触发metaspaceGC的阈值</p>

<p>默认40，表示每次GC完之后，假设我们允许接下来metaspace可以继续被commit的内存占到了被commit之后总共committed的内存量的MinMetaspaceFreeRatio%，如果这个总共被committed的量比当前触发metaspaceGC的阈值要大，那么将尝试做扩容，也就是增大触发metaspaceGC的阈值，不过这个增量至少是MinMetaspaceExpansion才会做，不然不会增加这个阈值</p>

<p>这个参数主要是为了避免触发metaspaceGC的阈值和gc之后committed的内存的量比较接近，于是将这个阈值进行扩大</p>

<p>一般情况下在gc完之后，如果被committed的量还是比较大的时候，换个说法就是离触发metaspaceGC的阈值比较接近的时候，这个调整会比较明显</p>

<p>注：这里不用gc之后used的量来算，主要是担心可能出现committed的量超过了触发metaspaceGC的阈值，这种情况一旦发生会很危险，会不断做gc，这应该是jdk8在某个版本之后才修复的bug</p>

<h3>MaxMetaspaceFreeRatio</h3>

<p>默认70，这个参数和上面的参数基本是相反的，是为了避免触发metaspaceGC的阈值过大，而想对这个值进行缩小。这个参数在gc之后committed的内存比较小的时候并且离触发metaspaceGC的阈值比较远的时候，调整会比较明显</p>

<h2>jstat里的metaspace字段</h2>

<p>我们看GC是否异常，除了通过GC日志来做分析之外，我们还可以通过jstat这样的工具展示的数据来分析，前面我公众号里有篇文章介绍了jstat这块的实现，有兴趣的可以到我的公众号<code>你假笨</code>里去翻阅下jstat的这篇文章。</p>

<p>我们通过jstat可以看到metaspace相关的这么一些指标，分别是<code>M</code>，<code>CCS</code>，<code>MC</code>，<code>MU</code>，<code>CCSC</code>，<code>CCSU</code>，<code>MCMN</code>，<code>MCMX</code>，<code>CCSMN</code>，<code>CCSMX</code></p>

<p>它们的定义如下：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^M^&quot;</span>  <span class="cm">/* Metaspace - Percent Used */</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">(</span><span class="mi">1</span><span class="o">-((</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">metaspace</span><span class="o">.</span><span class="na">capacity</span> <span class="o">-</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">metaspace</span><span class="o">.</span><span class="na">used</span><span class="o">)/</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">metaspace</span><span class="o">.</span><span class="na">capacity</span><span class="o">))</span> <span class="o">*</span> <span class="mi">100</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">6</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">raw</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.00&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^CCS^&quot;</span>    <span class="cm">/* Compressed Class Space - Percent Used */</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">(</span><span class="mi">1</span><span class="o">-((</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">compressedclassspace</span><span class="o">.</span><span class="na">capacity</span> <span class="o">-</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">compressedclassspace</span><span class="o">.</span><span class="na">used</span><span class="o">)/</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">compressedclassspace</span><span class="o">.</span><span class="na">capacity</span><span class="o">))</span> <span class="o">*</span> <span class="mi">100</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">6</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">raw</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.00&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^MC^&quot;</span> <span class="cm">/* Metaspace Capacity - Current */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">metaspace</span><span class="o">.</span><span class="na">capacity</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">center</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">6</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">K</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.0&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^MU^&quot;</span> <span class="cm">/* Metaspae Used */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">metaspace</span><span class="o">.</span><span class="na">used</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">center</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">6</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">K</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.0&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>   <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^CCSC^&quot;</span>   <span class="cm">/* Compressed Class Space Capacity - Current */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">compressedclassspace</span><span class="o">.</span><span class="na">capacity</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">8</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">K</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.0&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^CCSU^&quot;</span>   <span class="cm">/* Compressed Class Space Used */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">compressedclassspace</span><span class="o">.</span><span class="na">used</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">8</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">K</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.0&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^MCMN^&quot;</span>   <span class="cm">/* Metaspace Capacity - Minimum */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">metaspace</span><span class="o">.</span><span class="na">minCapacity</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">K</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">8</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.0&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^MCMX^&quot;</span>   <span class="cm">/* Metaspace Capacity - Maximum */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">metaspace</span><span class="o">.</span><span class="na">maxCapacity</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">K</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">8</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.0&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^CCSMN^&quot;</span>    <span class="cm">/* Compressed Class Space Capacity - Minimum */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">compressedclassspace</span><span class="o">.</span><span class="na">minCapacity</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">K</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">8</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.0&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^CCSMX^&quot;</span>  <span class="cm">/* Compressed Class Space Capacity - Maximum */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">compressedclassspace</span><span class="o">.</span><span class="na">maxCapacity</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">K</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">8</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.0&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>我这里对这些字段分类介绍下</p>

<h3>MC &amp; MU &amp; CCSC &amp; CCSU</h3>

<ul>
<li><p>MC表示Klass Metaspace以及NoKlass Metaspace两者总共committed的内存大小，单位是KB，虽然从上面的定义里我们看到了是capacity，但是实质上计算的时候并不是capacity，而是committed，这个是要注意的</p></li>
<li><p>MU这个无可厚非，说的就是Klass Metaspace以及NoKlass Metaspace两者已经使用了的内存大小</p></li>
<li><p>CCSC表示的是Klass Metaspace的已经被commit的内存大小，单位也是KB</p></li>
<li><p>CCSU表示Klass Metaspace的已经被使用的内存大小</p></li>
</ul>


<h3>M &amp; CCS</h3>

<ul>
<li><p>M表示的是Klass Metaspace以及NoKlass Metaspace两者总共的使用率，其实可以根据上面的四个指标算出来，即(CCSU+MU)/(CCSC+MC)</p></li>
<li><p>CCS表示的是NoKlass Metaspace的使用率，也就是CCSU/CCSC算出来的</p></li>
</ul>


<p>PS：所以我们有时候看到M的值达到了90%以上，其实这个并不一定说明metaspace用了很多了，因为内存是慢慢commit的，所以我们的分母是慢慢变大的，不过当我们committed到一定量的时候就不会再增长了</p>

<h3>MCMN &amp; MCMX &amp; CCSMN &amp; CCSMX</h3>

<ul>
<li><p>MCMN和CCSMN这两个值大家可以忽略，一直都是0</p></li>
<li><p>MCMX表示Klass Metaspace以及NoKlass Metaspace两者总共的reserved的内存大小，比如默认情况下Klass Metaspace是通过CompressedClassSpaceSize这个参数来reserved 1G的内存，NoKlass Metaspace默认reserved的内存大小是2* InitialBootClassLoaderMetaspaceSize</p></li>
<li><p>CCSMX表示Klass Metaspace reserved的内存大小</p></li>
</ul>


<p>综上所述，其实看metaspace最主要的还是看<code>MC</code>，<code>MU</code>，<code>CCSC</code>，<code>CCSU</code>这几个具体的大小来判断metaspace到底用了多少更靠谱</p>

<p>本来还想写metaspace内存分配和GC的内容，不过那块说起来又是一个比较大的话题，因为那块大家看起来可能会比较枯燥，有机会再写</p>

<p>PS:本文最先发布在听云博客</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之临门一脚的OutOfMemoryError完全解读]]></title>
    <link href="http://nijiaben.github.io/blog/2016/08/29/oom/"/>
    <updated>2016-08-29T15:35:09+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/08/29/oom</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>OutOfMemoryError，说的是java.lang.OutOfMemoryError，是JDK里自带的异常，顾名思义，说的就是内存溢出，当我们的系统内存严重不足的时候就会抛出这个异常(PS:注意这是一个Error，不是一个Exception，所以当我们要catch异常的时候要注意哦)，这个异常说常见也常见，说不常见其实也见得不多，不过作为Java程序员至少应该都听过吧，如果你对jvm不是很熟，或者对OutOfMemoryError这个异常了解不是很深的话，这篇文章肯定还是可以给你带来一些惊喜的，通过这篇文章你至少可以了解到如下几点：</p>

<ul>
<li>OutOfMemoryError一定会被加载吗</li>
<li>什么时候抛出OutOfMemoryError</li>
<li>会创建无数OutOfMemoryError实例吗</li>
<li>为什么大部分OutOfMemoryError异常是无堆栈的</li>
<li>我们如何去分析这样的异常</li>
</ul>


<!--more-->


<h2>OutOfMemoryError类加载</h2>

<p>既然要说OutOfMemoryError，那就得从这个类的加载说起来，那这个类什么时候被加载呢？你或许会不假思索地说，根据java类的延迟加载机制，这个类一般情况下不会被加载，除非当我们抛出OutOfMemoryError这个异常的时候才会第一次被加载，如果我们的系统一直不抛出这个异常，那这个类将一直不会被加载。说起来好像挺对，不过我这里首先要纠正这个说法，要明确的告诉你这个类在jvm启动的时候就已经被加载了，不信你就执行<code>java -verbose:class -version</code>打印JDK版本看看，看是否有OutOfMemoryError这个类被加载，再输出里你将能找到下面的内容：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="o">[</span><span class="n">Loaded</span> <span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">OutOfMemoryError</span> <span class="n">from</span> <span class="o">/</span><span class="n">Library</span><span class="o">/</span><span class="n">Java</span><span class="o">/</span><span class="n">JavaVirtualMachines</span><span class="o">/</span><span class="n">jdk1</span><span class="o">.</span><span class="mf">7.0</span><span class="n">_79</span><span class="o">.</span><span class="na">jdk</span><span class="o">/</span><span class="n">Contents</span><span class="o">/</span><span class="n">Home</span><span class="o">/</span><span class="n">jre</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">rt</span><span class="o">.</span><span class="na">jar</span><span class="o">]</span>
</span></code></pre></td></tr></table></div></figure>


<p>这意味着这个类其实在vm启动的时候就已经被加载了，那JVM里到底在哪里进行加载的呢，且看下面的方法:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">bool</span> <span class="nf">universe_post_init</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="o">...</span>
</span><span class='line'>  
</span><span class='line'>  
</span><span class='line'>  <span class="c1">// Setup preallocated OutOfMemoryError errors</span>
</span><span class='line'>    <span class="n">k</span> <span class="o">=</span> <span class="nl">SystemDictionary:</span><span class="o">:</span><span class="n">resolve_or_fail</span><span class="o">(</span><span class="nl">vmSymbols:</span><span class="o">:</span><span class="n">java_lang_OutOfMemoryError</span><span class="o">(),</span> <span class="kc">true</span><span class="o">,</span> <span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="n">k_h</span> <span class="o">=</span> <span class="n">instanceKlassHandle</span><span class="o">(</span><span class="n">THREAD</span><span class="o">,</span> <span class="n">k</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_java_heap</span> <span class="o">=</span> <span class="n">k_h</span><span class="o">-&gt;</span><span class="n">allocate_instance</span><span class="o">(</span><span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_metaspace</span> <span class="o">=</span> <span class="n">k_h</span><span class="o">-&gt;</span><span class="n">allocate_instance</span><span class="o">(</span><span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_class_metaspace</span> <span class="o">=</span> <span class="n">k_h</span><span class="o">-&gt;</span><span class="n">allocate_instance</span><span class="o">(</span><span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_array_size</span> <span class="o">=</span> <span class="n">k_h</span><span class="o">-&gt;</span><span class="n">allocate_instance</span><span class="o">(</span><span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_gc_overhead_limit</span> <span class="o">=</span>
</span><span class='line'>      <span class="n">k_h</span><span class="o">-&gt;</span><span class="n">allocate_instance</span><span class="o">(</span><span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_realloc_objects</span> <span class="o">=</span> <span class="n">k_h</span><span class="o">-&gt;</span><span class="n">allocate_instance</span><span class="o">(</span><span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>  <span class="o">...</span>
</span><span class='line'>  
</span><span class='line'>  
</span><span class='line'>  <span class="k">if</span> <span class="o">(!</span><span class="n">DumpSharedSpaces</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="c1">// These are the only Java fields that are currently set during shared space dumping.</span>
</span><span class='line'>    <span class="c1">// We prefer to not handle this generally, so we always reinitialize these detail messages.</span>
</span><span class='line'>    <span class="n">Handle</span> <span class="n">msg</span> <span class="o">=</span> <span class="nl">java_lang_String:</span><span class="o">:</span><span class="n">create_from_str</span><span class="o">(</span><span class="s">&quot;Java heap space&quot;</span><span class="o">,</span> <span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">java_lang_Throwable:</span><span class="o">:</span><span class="n">set_message</span><span class="o">(</span><span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_java_heap</span><span class="o">,</span> <span class="n">msg</span><span class="o">());</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">msg</span> <span class="o">=</span> <span class="nl">java_lang_String:</span><span class="o">:</span><span class="n">create_from_str</span><span class="o">(</span><span class="s">&quot;Metaspace&quot;</span><span class="o">,</span> <span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">java_lang_Throwable:</span><span class="o">:</span><span class="n">set_message</span><span class="o">(</span><span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_metaspace</span><span class="o">,</span> <span class="n">msg</span><span class="o">());</span>
</span><span class='line'>    <span class="n">msg</span> <span class="o">=</span> <span class="nl">java_lang_String:</span><span class="o">:</span><span class="n">create_from_str</span><span class="o">(</span><span class="s">&quot;Compressed class space&quot;</span><span class="o">,</span> <span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">java_lang_Throwable:</span><span class="o">:</span><span class="n">set_message</span><span class="o">(</span><span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_class_metaspace</span><span class="o">,</span> <span class="n">msg</span><span class="o">());</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">msg</span> <span class="o">=</span> <span class="nl">java_lang_String:</span><span class="o">:</span><span class="n">create_from_str</span><span class="o">(</span><span class="s">&quot;Requested array size exceeds VM limit&quot;</span><span class="o">,</span> <span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">java_lang_Throwable:</span><span class="o">:</span><span class="n">set_message</span><span class="o">(</span><span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_array_size</span><span class="o">,</span> <span class="n">msg</span><span class="o">());</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">msg</span> <span class="o">=</span> <span class="nl">java_lang_String:</span><span class="o">:</span><span class="n">create_from_str</span><span class="o">(</span><span class="s">&quot;GC overhead limit exceeded&quot;</span><span class="o">,</span> <span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">java_lang_Throwable:</span><span class="o">:</span><span class="n">set_message</span><span class="o">(</span><span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_gc_overhead_limit</span><span class="o">,</span> <span class="n">msg</span><span class="o">());</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">msg</span> <span class="o">=</span> <span class="nl">java_lang_String:</span><span class="o">:</span><span class="n">create_from_str</span><span class="o">(</span><span class="s">&quot;Java heap space: failed reallocation of scalar replaced objects&quot;</span><span class="o">,</span> <span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">java_lang_Throwable:</span><span class="o">:</span><span class="n">set_message</span><span class="o">(</span><span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_realloc_objects</span><span class="o">,</span> <span class="n">msg</span><span class="o">());</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">msg</span> <span class="o">=</span> <span class="nl">java_lang_String:</span><span class="o">:</span><span class="n">create_from_str</span><span class="o">(</span><span class="s">&quot;/ by zero&quot;</span><span class="o">,</span> <span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="nl">java_lang_Throwable:</span><span class="o">:</span><span class="n">set_message</span><span class="o">(</span><span class="nl">Universe:</span><span class="o">:</span><span class="n">_arithmetic_exception_instance</span><span class="o">,</span> <span class="n">msg</span><span class="o">());</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Setup the array of errors that have preallocated backtrace</span>
</span><span class='line'>    <span class="n">k</span> <span class="o">=</span> <span class="nl">Universe:</span><span class="o">:</span><span class="n">_out_of_memory_error_java_heap</span><span class="o">-&gt;</span><span class="n">klass</span><span class="o">();</span>
</span><span class='line'>    <span class="k">assert</span><span class="o">(</span><span class="n">k</span><span class="o">-&gt;</span><span class="n">name</span><span class="o">()</span> <span class="o">==</span> <span class="nl">vmSymbols:</span><span class="o">:</span><span class="n">java_lang_OutOfMemoryError</span><span class="o">(),</span> <span class="s">&quot;should be out of memory error&quot;</span><span class="o">);</span>
</span><span class='line'>    <span class="n">k_h</span> <span class="o">=</span> <span class="n">instanceKlassHandle</span><span class="o">(</span><span class="n">THREAD</span><span class="o">,</span> <span class="n">k</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">int</span> <span class="n">len</span> <span class="o">=</span> <span class="o">(</span><span class="n">StackTraceInThrowable</span><span class="o">)</span> <span class="o">?</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span><span class="n">PreallocatedOutOfMemoryErrorCount</span> <span class="o">:</span> <span class="mi">0</span><span class="o">;</span>
</span><span class='line'>    <span class="nl">Universe:</span><span class="o">:</span><span class="n">_preallocated_out_of_memory_error_array</span> <span class="o">=</span> <span class="nl">oopFactory:</span><span class="o">:</span><span class="n">new_objArray</span><span class="o">(</span><span class="n">k_h</span><span class="o">(),</span> <span class="n">len</span><span class="o">,</span> <span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>    <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="o">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">len</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span><span class='line'>      <span class="n">oop</span> <span class="n">err</span> <span class="o">=</span> <span class="n">k_h</span><span class="o">-&gt;</span><span class="n">allocate_instance</span><span class="o">(</span><span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>      <span class="n">Handle</span> <span class="n">err_h</span> <span class="o">=</span> <span class="n">Handle</span><span class="o">(</span><span class="n">THREAD</span><span class="o">,</span> <span class="n">err</span><span class="o">);</span>
</span><span class='line'>      <span class="nl">java_lang_Throwable:</span><span class="o">:</span><span class="n">allocate_backtrace</span><span class="o">(</span><span class="n">err_h</span><span class="o">,</span> <span class="n">CHECK_false</span><span class="o">);</span>
</span><span class='line'>      <span class="nl">Universe:</span><span class="o">:</span><span class="n">preallocated_out_of_memory_errors</span><span class="o">()-&gt;</span><span class="n">obj_at_put</span><span class="o">(</span><span class="n">i</span><span class="o">,</span> <span class="n">err_h</span><span class="o">());</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>    <span class="nl">Universe:</span><span class="o">:</span><span class="n">_preallocated_out_of_memory_error_avail_count</span> <span class="o">=</span> <span class="o">(</span><span class="n">jint</span><span class="o">)</span><span class="n">len</span><span class="o">;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>上面的代码其实就是在vm启动过程中加载了OutOfMemoryError这个类，并且创建了好几个OutOfMemoryError对象，每个OutOfMemoryError对象代表了一种内存溢出的场景，比如说<code>Java heap space</code>不足导致的OutOfMemoryError，抑或<code>Metaspace</code>不足导致的OutOfMemoryError，上面的代码来源于JDK8，所以能看到metaspace的内容，如果是JDK8之前，你将看到Perm的OutOfMemoryError，不过本文metaspace不是重点，所以不展开讨论，如果大家有兴趣，可以专门写一篇文章来介绍metsapce来龙去脉，说来这个坑填起来还挺大的。</p>

<h3>能通过agent拦截到这个类加载吗</h3>

<p>熟悉字节码增强的人，可能会条件反射地想到是否可以拦截到这个类的加载呢，这样我们就可以做一些譬如内存溢出的监控啥的，哈哈，我要告诉你的是<code>NO WAY</code>，因为通过agent的方式来监听类加载过程是在vm初始化完成之后才开始的，而这个类的加载是在vm初始化过程中，因此不可能拦截到这个类的加载，于此类似的还有<code>java.lang.Object</code>,<code>java.lang.Class</code>等。</p>

<h3>为什么要在vm启动过程中加载这个类</h3>

<p>这个问题或许看了后面的内容你会有所体会，先卖个关子。包括为什么要预先创建这几个实例对象后面也会解释。</p>

<h2>何时抛出OutOfMemoryError</h2>

<p>要抛出OutOfMemoryError，那肯定是有地方需要进行内存分配，可能是heap里，也可能是metsapce里（如果是在JDK8之前的会是Perm里），不同地方的分配，其策略也不一样，简单来说就是尝试分配，实在没办法就gc，gc还是不能分配就抛出异常。</p>

<p>不过还是以Heap里的分配为例说一下具体的过程：</p>

<p>正确情况下对象创建需要分配的内存是来自于Heap的Eden区域里，当Eden内存不够用的时候，某些情况下会尝试到Old里进行分配(比如说要分配的内存很大)，如果还是没有分配成功，于是会触发一次ygc的动作，而ygc完成之后我们会再次尝试分配，如果仍不足以分配此时的内存，那会接着做一次full gc(不过此时的soft reference不会被强制回收)，将老生代也回收一下，接着再做一次分配，仍然不够分配那会做一次强制将soft reference也回收的full gc，如果还是不能分配，那这个时候就不得不抛出OutOfMemoryError了。这就是Heap里分配内存抛出OutOfMemoryError的具体过程了。</p>

<h2>OutOfMemoryError对象可能会很多吗</h2>

<p>想象有这么一种场景，我们的代码写得足够烂，并且存在内存泄漏，这意味着系统跑到一定程度之后，只要我们创建对象要分配内存的时候就会进行gc，但是gc没啥效果，进而抛出OutOfMemoryError的异常，那意味着每发生此类情况就应该创建一个OutOfMemoryError对象，并且抛出来，也就是说我们会看到一个带有堆栈的OutOfMemoryError异常被抛出，那事实是如此吗？如果真是如此，那为什么在VM启动的时候会创建那几个OutOfMemoryError对象呢？</p>

<h3>抛出异常的java代码位置需要我们关心吗</h3>

<p>这个问题或许你仔细想想就清楚了，如果没想清楚，请在这里停留一分钟仔细想想再往后面看。</p>

<p>抛出OutOfMemoryError异常的java方法其实只是临门一脚而已，导致内存泄漏的不一定就是这个方法，当然也不排除可能是这个方法，不过这种情况的可能性真的非常小。所以你大可不必去关心抛出这个异常的堆栈。</p>

<p>既然可以不关心其异常堆栈，那意味着这个异常其实没必要每次都创建一个不一样的了，因为不需要堆栈的话，其他的东西都可以完全相同，这样一来回到我们前面提到的那个问题，<code>为什么要在vm启动过程中加载这个类</code>，或许你已经有答案了，在vm启动过程中我们把类加载起来，并创建几个没有堆栈的对象缓存起来，只需要设置下不同的提示信息即可，当需要抛出特定类型的OutOfMemoryError异常的时候，就直接拿出缓存里的这几个对象就可以了。</p>

<p>所以OutOfMemoryError的对象其实并不会太多，哪怕你代码写得再烂，当然，如果你代码里要不断<code>new OutOfMemoryError()</code>，那我就无话可说啦。</p>

<h2>为什么我们有时候还是可以看到有堆栈的OutOfMemoryError</h2>

<p>如果都是用jvm启动的时候创建的那几个OutOfMemoryError对象，那不应该再出现有堆栈的OutOfMemoryError异常，但是实际上我们偶尔还是能看到有堆栈的异常，如果你细心点的话，可能会总结出一个规律，发现最多出现4次有堆栈的OutOfMemoryError异常，当4次过后，你都将看到无堆栈的OutOfMemoryError异常。</p>

<p>这个其实在我们上面贴的代码里也有体现，最后有一个for循环，这个循环里会创建几个OutOfMemoryError对象，如果我们将<code>StackTraceInThrowable</code>设置为true的话(默认就是true的)，意味着我们抛出来的异常正确情况下都将是有堆栈的，那根据<code>PreallocatedOutOfMemoryErrorCount</code>这个参数来决定预先创建几个OutOfMemoryError异常对象，但是这个参数除非在debug版本下可以被设置之外，正常release出来的版本其实是无法设置这个参数的，它会是一个常量，值为4，因此在jvm启动的时候会预先创建4个OutOfMemoryError异常对象，但是这几个异常对象的堆栈，是可以动态设置的，比如说某个地方要抛出OutOfMemoryError异常了，于是先从预存的OutOfMemoryError里取出一个（其他是预存的对象还有），将此时的堆栈填上，然后抛出来，并且这个对象的使用是一次性的，也就是这个对象被抛出之后将不会再次被利用，直到预设的这几个OutOfMemoryError对象被用完了，那接下来抛出的异常都将是一开始缓存的那几个无栈的OutOfMemoryError对象。</p>

<p>这就是我们看到的最多出现4次有堆栈的OutOfMemoryError异常及大部分情况下都将看到没有堆栈的OutOfMemoryError对象的原因。</p>

<h2>如何分析OutOfMemoryError异常</h2>

<p>既然看堆栈也没什么意义，那只能从提示上入手了，我们看到这类异常，首先要确定的到底是哪块内存何种情况导致的内存溢出，比如说是Perm导致的，那抛出来的异常信息里会带有<code>Perm</code>的关键信息，那我们应该重点看Perm的大小，以及Perm里的内容；如果是Heap的，那我们就必须做内存Dump，然后分析为什么会发生这样的情况，内存里到底存了什么对象，至于内存分析的最佳的分析工具自然是MAT啦，不了解的请google之。</p>

<p>PS:本文最先发布在听云博客</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之Jstat工具原理完全解读]]></title>
    <link href="http://nijiaben.github.io/blog/2016/07/20/jstat/"/>
    <updated>2016-07-20T15:32:00+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/07/20/jstat</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>jstat是hotspot自带的工具，和java一样也位于<code>JAVA_HOME/bin</code>下面，我们通过该工具可以实时了解当前进程的gc，compiler，class，memory等相关的情况，具体我们可以通过jstat -options来看我们到底支持哪些类型的数据，譬如JDK8下的结果是：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="o">-</span><span class="kd">class</span>
</span><span class='line'><span class="err">-</span><span class="nc">compiler</span>
</span><span class='line'><span class="o">-</span><span class="n">gc</span>
</span><span class='line'><span class="o">-</span><span class="n">gccapacity</span>
</span><span class='line'><span class="o">-</span><span class="n">gccause</span>
</span><span class='line'><span class="o">-</span><span class="n">gcmetacapacity</span>
</span><span class='line'><span class="o">-</span><span class="n">gcnew</span>
</span><span class='line'><span class="o">-</span><span class="n">gcnewcapacity</span>
</span><span class='line'><span class="o">-</span><span class="n">gcold</span>
</span><span class='line'><span class="o">-</span><span class="n">gcoldcapacity</span>
</span><span class='line'><span class="o">-</span><span class="n">gcutil</span>
</span><span class='line'><span class="o">-</span><span class="n">printcompilation</span>
</span></code></pre></td></tr></table></div></figure>


<!--more-->


<h2>jstat的输出</h2>

<p>jstat大家用得其实挺多的，最常见的用法是jstat -gcutil，输出如下：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="o">~</span> <span class="n">ᐅ</span> <span class="n">jstat</span> <span class="o">-</span><span class="n">gcutil</span> <span class="mi">692</span> <span class="mi">1000</span>
</span><span class='line'>  <span class="n">S0</span>     <span class="n">S1</span>     <span class="n">E</span>      <span class="n">O</span>      <span class="n">M</span>     <span class="n">CCS</span>    <span class="n">YGC</span>     <span class="n">YGCT</span>    <span class="n">FGC</span>    <span class="n">FGCT</span>     <span class="n">GCT</span>
</span><span class='line'>  <span class="mf">0.00</span>  <span class="mf">41.49</span>  <span class="mf">59.79</span>  <span class="mf">83.66</span>  <span class="mf">89.92</span>  <span class="mf">78.74</span>    <span class="mi">295</span>    <span class="mf">5.436</span>    <span class="mi">10</span>    <span class="mf">3.855</span>    <span class="mf">9.291</span>
</span><span class='line'>  <span class="mf">0.00</span>  <span class="mf">41.49</span>  <span class="mf">59.80</span>  <span class="mf">83.66</span>  <span class="mf">89.92</span>  <span class="mf">78.74</span>    <span class="mi">295</span>    <span class="mf">5.436</span>    <span class="mi">10</span>    <span class="mf">3.855</span>    <span class="mf">9.291</span>
</span><span class='line'>  <span class="mf">0.00</span>  <span class="mf">41.49</span>  <span class="mf">59.80</span>  <span class="mf">83.66</span>  <span class="mf">89.92</span>  <span class="mf">78.74</span>    <span class="mi">295</span>    <span class="mf">5.436</span>    <span class="mi">10</span>    <span class="mf">3.855</span>    <span class="mf">9.291</span>
</span><span class='line'>  <span class="mf">0.00</span>  <span class="mf">41.49</span>  <span class="mf">59.80</span>  <span class="mf">83.66</span>  <span class="mf">89.92</span>  <span class="mf">78.74</span>    <span class="mi">295</span>    <span class="mf">5.436</span>    <span class="mi">10</span>    <span class="mf">3.855</span>    <span class="mf">9.291</span>
</span><span class='line'>  <span class="mf">0.00</span>  <span class="mf">41.49</span>  <span class="mf">59.80</span>  <span class="mf">83.66</span>  <span class="mf">89.92</span>  <span class="mf">78.74</span>    <span class="mi">295</span>    <span class="mf">5.436</span>    <span class="mi">10</span>    <span class="mf">3.855</span>    <span class="mf">9.291</span>
</span></code></pre></td></tr></table></div></figure>


<p>那每一列是怎么定义，怎么计算的呢，其实在tools.jar里存在一个文件叫做jstat_options，这个文件里定义了上面的每种类型的输出结果，比如说gcutil</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">option</span> <span class="n">gcutil</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^S0^&quot;</span> <span class="cm">/* Survivor 0 Space - Percent Used */</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">(</span><span class="mi">1</span><span class="o">-((</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="na">capacity</span> <span class="o">-</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="na">used</span><span class="o">)/</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="na">capacity</span><span class="o">))</span> <span class="o">*</span> <span class="mi">100</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">raw</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">6</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.00&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^S1^&quot;</span> <span class="cm">/* Survivor 1 Space - Percent Used */</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">(</span><span class="mi">1</span><span class="o">-((</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">2</span><span class="o">.</span><span class="na">capacity</span> <span class="o">-</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">2</span><span class="o">.</span><span class="na">used</span><span class="o">)/</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">2</span><span class="o">.</span><span class="na">capacity</span><span class="o">))</span> <span class="o">*</span> <span class="mi">100</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">raw</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">6</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.00&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^E^&quot;</span>  <span class="cm">/* Eden Space - Percent Used */</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">(</span><span class="mi">1</span><span class="o">-((</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">capacity</span> <span class="o">-</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">used</span><span class="o">)/</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">capacity</span><span class="o">))</span> <span class="o">*</span> <span class="mi">100</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">raw</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">6</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.00&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^O^&quot;</span>  <span class="cm">/* Old Space - Percent Used */</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">(</span><span class="mi">1</span><span class="o">-((</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">capacity</span> <span class="o">-</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">used</span><span class="o">)/</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">generation</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="na">space</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">capacity</span><span class="o">))</span> <span class="o">*</span> <span class="mi">100</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">raw</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">6</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.00&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^M^&quot;</span>  <span class="cm">/* Metaspace Space - Percent Used */</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">(</span><span class="mi">1</span><span class="o">-((</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">metaspace</span><span class="o">.</span><span class="na">capacity</span> <span class="o">-</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">metaspace</span><span class="o">.</span><span class="na">used</span><span class="o">)/</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">metaspace</span><span class="o">.</span><span class="na">capacity</span><span class="o">))</span> <span class="o">*</span> <span class="mi">100</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">6</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">raw</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.00&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^CCS^&quot;</span>    <span class="cm">/* Compressed Class Space Space - Percent Used */</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">(</span><span class="mi">1</span><span class="o">-((</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">compressedclassspace</span><span class="o">.</span><span class="na">capacity</span> <span class="o">-</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">compressedclassspace</span><span class="o">.</span><span class="na">used</span><span class="o">)/</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">compressedclassspace</span><span class="o">.</span><span class="na">capacity</span><span class="o">))</span> <span class="o">*</span> <span class="mi">100</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">6</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">raw</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.00&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^YGC^&quot;</span>    <span class="cm">/* Young Generation Collections */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">collector</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">invocations</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">6</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^YGCT^&quot;</span>   <span class="cm">/* Young Generation Collection Time */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">collector</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">time</span><span class="o">/</span><span class="n">sun</span><span class="o">.</span><span class="na">os</span><span class="o">.</span><span class="na">hrt</span><span class="o">.</span><span class="na">frequency</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">sec</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">8</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.000&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^FGC^&quot;</span>    <span class="cm">/* Full Collections */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">collector</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="na">invocations</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">5</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">raw</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^FGCT^&quot;</span>   <span class="cm">/* Full Collection Time */</span>
</span><span class='line'>    <span class="n">data</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">collector</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="na">time</span><span class="o">/</span><span class="n">sun</span><span class="o">.</span><span class="na">os</span><span class="o">.</span><span class="na">hrt</span><span class="o">.</span><span class="na">frequency</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">sec</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">8</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.000&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">column</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">header</span> <span class="s">&quot;^GCT^&quot;</span>    <span class="cm">/* Total Garbage Collection Time */</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">(</span><span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">collector</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="na">time</span> <span class="o">+</span> <span class="n">sun</span><span class="o">.</span><span class="na">gc</span><span class="o">.</span><span class="na">collector</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="na">time</span><span class="o">)/</span><span class="n">sun</span><span class="o">.</span><span class="na">os</span><span class="o">.</span><span class="na">hrt</span><span class="o">.</span><span class="na">frequency</span>
</span><span class='line'>    <span class="n">align</span> <span class="n">right</span>
</span><span class='line'>    <span class="n">width</span> <span class="mi">8</span>
</span><span class='line'>    <span class="n">scale</span> <span class="n">sec</span>
</span><span class='line'>    <span class="n">format</span> <span class="s">&quot;0.000&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>从上面的定义我们知道gcutil的每一列是什么意思，怎么计算出来的，其中类似<code>sun.gc.generation.0.space.0.capacity</code>这样的一些变量是jvm里创建并实时更新的值</p>

<h2>jstat如何获取到这些变量的值</h2>

<p>变量值显然是从目标进程里获取来的，但是是怎样来的？local socket还是memory share？其实是从一个共享文件里来的，这个文件叫PerfData，主要指的是/tmp/hsperfdata_\<user>/\<pid>这个文件</p>

<h3>PerfData文件</h3>

<h4>文件创建</h4>

<p>这个文件是否存在取决于两个参数，一个UsePerfData，另一个是PerfDisableSharedMem，如果设置了-XX:+PerfDisableSharedMem或者-XX:-UsePerfData，那这个文件是不会存在的，默认情况下PerfDisableSharedMem是关闭的，UsePerfData是打开的，所以默认情况下PerfData文件是存在的。对于UsePerfData和PerfDisableSharedMem这两个参数，这里着重讲一下：</p>

<ul>
<li>UsePerfData：如果关闭了UsePerfData这个参数，那么jvm启动过程中perf memory都不会被创建，默认情况是是打开的</li>
<li>PerfDisableSharedMem：该参数决定了存储PerfData的内存是不是可以被共享，也就是说不管这个参数设置没设置，jvm在启动的时候都会分配一块内存来存PerfData，只是说这个PerfData是不是其他进程可见的问题，如果设置了这个参数，说明不能被共享，此时其他进程将访问不了该内存，这样一来，譬如我们jps，jstat等都无法工作。默认这个参数是关闭的，也就是默认支持共享的方式</li>
</ul>


<p>具体代码在PerfMemory::create_memory_region里</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'>  <span class="k">if</span> <span class="o">(</span><span class="n">PerfDisableSharedMem</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="c1">// do not share the memory for the performance data.</span>
</span><span class='line'>    <span class="n">_start</span> <span class="o">=</span> <span class="n">create_standard_memory</span><span class="o">(</span><span class="n">size</span><span class="o">);</span>
</span><span class='line'>  <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">_start</span> <span class="o">=</span> <span class="n">create_shared_memory</span><span class="o">(</span><span class="n">size</span><span class="o">);</span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">_start</span> <span class="o">==</span> <span class="n">NULL</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>      <span class="c1">// creation of the shared memory region failed, attempt</span>
</span><span class='line'>      <span class="c1">// to create a contiguous, non-shared memory region instead.</span>
</span><span class='line'>      <span class="c1">//</span>
</span><span class='line'>      <span class="k">if</span> <span class="o">(</span><span class="n">PrintMiscellaneous</span> <span class="o">&amp;&amp;</span> <span class="n">Verbose</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">warning</span><span class="o">(</span><span class="s">&quot;Reverting to non-shared PerfMemory region.\n&quot;</span><span class="o">);</span>
</span><span class='line'>      <span class="o">}</span>
</span><span class='line'>      <span class="n">PerfDisableSharedMem</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>      <span class="n">_start</span> <span class="o">=</span> <span class="n">create_standard_memory</span><span class="o">(</span><span class="n">size</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>  <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h4>文件删除</h4>

<p>那这个文件什么时候删除？正常情况下当进程退出的时候会自动删除，但是某些极端情况下，比如kill -9，这种信号jvm是不能捕获的，所以导致进程直接退出了，而没有做一些收尾性的工作，这个时候你会发现进程虽然没了，但是这个文件其实还是存在的，那这个文件是不是就一直留着，只能等待人为的删除呢，jvm里考虑到了这种情况，会在当前用户接下来的任何一个java进程(比如说我们执行jps)起来的时候会去做一个判断，看/tmp/hsperfdata_\<user>下的进程是不是还存在，如果不存在了就直接删除该文件，判断是否存在的具体操作其实就是发一个kill -0的信号看是否有异常。</p>

<h4>文件更新</h4>

<p>由于这个文件是通过mmap的方式映射到了内存里，而jstat是直接通过DirectByteBuffer的方式从PerfData里读取的，所以只要内存里的值变了，那我们从jstat看到的值就会发生变化，内存里的值什么时候变，取决于-XX:PerfDataSamplingInterval这个参数，默认是50ms，也就是说50ms更新一次值，基本上可以认为是实时的了。</p>

<h4>PerfData其他相关VM参数</h4>

<ul>
<li>-XX:PerfDataMemorySize：指定/tmp/hsperfdata_\<user>下perfData文件的大小，默认是32KB，如果用户设置了该值，jvm里会自动和os的page size对齐，比如linux下pagesize默认是4KB，那如果你设置了31KB，那自动会分配32KB</li>
<li>-XX:+PerfDataSaveToFile：是否在进程退出的时候讲PerfData里的数据保存到一个特定的文件里，文件路径由下面的参数指定，否则就在当前目录下</li>
<li>-XX:PerfDataSaveFile：指定保存PerfData文件的路径</li>
<li>-XX:PerfDataSamplingInterval：指定perfData的采样间隔，默认是50ms，基本算实时采样了</li>
</ul>


<h2>jstat里的坑</h2>

<p>本人暂时想到的两大坑：</p>

<ul>
<li><p>一次正常的Background CMS GC之后，发现FGC的值加了2次，后面发现主要原因是CMS有init mark和remark两个会暂停应用的阶段，同时因为是对old做gc，因此算了两次</p></li>
<li><p>JDK8下metaspace的使用情况不准确，比如说CCSC的值表示的是 Compressed Class Space Capacity，但是发现这个值的计算却不是reserve的值，所以我们可能会发现metaspace其实用了非常少，但是通过jstat看起使用率已经非常大了，因此这种情况最好是通过jmx的方式去取那些值做一个计算</p></li>
</ul>


<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">size_t</span> <span class="nl">CompressedClassSpaceCounters:</span><span class="o">:</span><span class="n">capacity</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nl">MetaspaceAux:</span><span class="o">:</span><span class="n">committed_bytes</span><span class="o">(</span><span class="nl">Metaspace:</span><span class="o">:</span><span class="n">ClassType</span><span class="o">);</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>



]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之不可控的堆外内存]]></title>
    <link href="http://nijiaben.github.io/blog/2016/06/29/ooc-offheap/"/>
    <updated>2016-06-29T14:33:51+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/06/29/ooc-offheap</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>之前写过篇文章，关于堆外内存的，<a href="http://mp.weixin.qq.com/s?__biz=MzIzNjI1ODc2OA==&amp;mid=403168235&amp;idx=1&amp;sn=ecc804ba7231996d43c05138566d074a&amp;scene=19#wechat_redirect">JVM源码分析之堆外内存完全解读</a>，里面重点讲了DirectByteBuffer的原理，但是今天碰到一个比较奇怪的问题，在设置了-XX:MaxDirectMemorySize=1G的前提下，然后统计所有DirectByteBuffer对象后面占用的内存达到了7G，远远超出阈值，这个问题很诡异，于是好好查了下原因，虽然最终发现是我们统计的问题，但是期间发现的其他一些问题还是值得分享一下的。</p>

<!--more-->


<h2>不得不提的DirectByteBuffer构造函数</h2>

<p>打开DirectByteBuffer这个类，我们会发现有5个构造函数</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">DirectByteBuffer</span><span class="o">(</span><span class="kt">int</span> <span class="n">cap</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'><span class="n">DirectByteBuffer</span><span class="o">(</span><span class="kt">long</span> <span class="n">addr</span><span class="o">,</span> <span class="kt">int</span> <span class="n">cap</span><span class="o">,</span> <span class="n">Object</span> <span class="n">ob</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'><span class="kd">private</span> <span class="nf">DirectByteBuffer</span><span class="o">(</span><span class="kt">long</span> <span class="n">addr</span><span class="o">,</span> <span class="kt">int</span> <span class="n">cap</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'><span class="kd">protected</span> <span class="nf">DirectByteBuffer</span><span class="o">(</span><span class="kt">int</span> <span class="n">cap</span><span class="o">,</span> <span class="kt">long</span> <span class="n">addr</span><span class="o">,</span><span class="n">FileDescriptor</span> <span class="n">fd</span><span class="o">,</span><span class="n">Runnable</span> <span class="n">unmapper</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'><span class="n">DirectByteBuffer</span><span class="o">(</span><span class="n">DirectBuffer</span> <span class="n">db</span><span class="o">,</span> <span class="kt">int</span> <span class="n">mark</span><span class="o">,</span> <span class="kt">int</span> <span class="n">pos</span><span class="o">,</span> <span class="kt">int</span> <span class="n">lim</span><span class="o">,</span> <span class="kt">int</span> <span class="n">cap</span><span class="o">,</span><span class="kt">int</span> <span class="n">off</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>我们从java层面创建DirectByteBuffer对象，一般都是通过ByteBuffer的allocateDirect方法</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">static</span> <span class="n">ByteBuffer</span> <span class="nf">allocateDirect</span><span class="o">(</span><span class="kt">int</span> <span class="n">capacity</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="k">new</span> <span class="nf">DirectByteBuffer</span><span class="o">(</span><span class="n">capacity</span><span class="o">);</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>也就是会使用上面提到的第一个构造函数，即</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">DirectByteBuffer</span><span class="o">(</span><span class="kt">int</span> <span class="n">cap</span><span class="o">)</span> <span class="o">{</span>                   <span class="c1">// package-private</span>
</span><span class='line'>
</span><span class='line'>        <span class="kd">super</span><span class="o">(-</span><span class="mi">1</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">cap</span><span class="o">,</span> <span class="n">cap</span><span class="o">);</span>
</span><span class='line'>        <span class="kt">boolean</span> <span class="n">pa</span> <span class="o">=</span> <span class="n">VM</span><span class="o">.</span><span class="na">isDirectMemoryPageAligned</span><span class="o">();</span>
</span><span class='line'>        <span class="kt">int</span> <span class="n">ps</span> <span class="o">=</span> <span class="n">Bits</span><span class="o">.</span><span class="na">pageSize</span><span class="o">();</span>
</span><span class='line'>        <span class="kt">long</span> <span class="n">size</span> <span class="o">=</span> <span class="n">Math</span><span class="o">.</span><span class="na">max</span><span class="o">(</span><span class="mi">1L</span><span class="o">,</span> <span class="o">(</span><span class="kt">long</span><span class="o">)</span><span class="n">cap</span> <span class="o">+</span> <span class="o">(</span><span class="n">pa</span> <span class="o">?</span> <span class="n">ps</span> <span class="o">:</span> <span class="mi">0</span><span class="o">));</span>
</span><span class='line'>        <span class="n">Bits</span><span class="o">.</span><span class="na">reserveMemory</span><span class="o">(</span><span class="n">size</span><span class="o">,</span> <span class="n">cap</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>        <span class="kt">long</span> <span class="n">base</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
</span><span class='line'>        <span class="k">try</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">base</span> <span class="o">=</span> <span class="n">unsafe</span><span class="o">.</span><span class="na">allocateMemory</span><span class="o">(</span><span class="n">size</span><span class="o">);</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">OutOfMemoryError</span> <span class="n">x</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">Bits</span><span class="o">.</span><span class="na">unreserveMemory</span><span class="o">(</span><span class="n">size</span><span class="o">,</span> <span class="n">cap</span><span class="o">);</span>
</span><span class='line'>            <span class="k">throw</span> <span class="n">x</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>        <span class="n">unsafe</span><span class="o">.</span><span class="na">setMemory</span><span class="o">(</span><span class="n">base</span><span class="o">,</span> <span class="n">size</span><span class="o">,</span> <span class="o">(</span><span class="kt">byte</span><span class="o">)</span> <span class="mi">0</span><span class="o">);</span>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">pa</span> <span class="o">&amp;&amp;</span> <span class="o">(</span><span class="n">base</span> <span class="o">%</span> <span class="n">ps</span> <span class="o">!=</span> <span class="mi">0</span><span class="o">))</span> <span class="o">{</span>
</span><span class='line'>            <span class="c1">// Round up to page boundary</span>
</span><span class='line'>            <span class="n">address</span> <span class="o">=</span> <span class="n">base</span> <span class="o">+</span> <span class="n">ps</span> <span class="o">-</span> <span class="o">(</span><span class="n">base</span> <span class="o">&amp;</span> <span class="o">(</span><span class="n">ps</span> <span class="o">-</span> <span class="mi">1</span><span class="o">));</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">address</span> <span class="o">=</span> <span class="n">base</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>        <span class="n">cleaner</span> <span class="o">=</span> <span class="n">Cleaner</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="k">new</span> <span class="nf">Deallocator</span><span class="o">(</span><span class="n">base</span><span class="o">,</span> <span class="n">size</span><span class="o">,</span> <span class="n">cap</span><span class="o">));</span>
</span><span class='line'>        <span class="n">att</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>    <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>而这个构造函数里的<code>Bits.reserveMemory(size, cap)</code>方法会做堆外内存的阈值check</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">reserveMemory</span><span class="o">(</span><span class="kt">long</span> <span class="n">size</span><span class="o">,</span> <span class="kt">int</span> <span class="n">cap</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="kd">synchronized</span> <span class="o">(</span><span class="n">Bits</span><span class="o">.</span><span class="na">class</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="k">if</span> <span class="o">(!</span><span class="n">memoryLimitSet</span> <span class="o">&amp;&amp;</span> <span class="n">VM</span><span class="o">.</span><span class="na">isBooted</span><span class="o">())</span> <span class="o">{</span>
</span><span class='line'>                <span class="n">maxMemory</span> <span class="o">=</span> <span class="n">VM</span><span class="o">.</span><span class="na">maxDirectMemory</span><span class="o">();</span>
</span><span class='line'>                <span class="n">memoryLimitSet</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>            <span class="c1">// -XX:MaxDirectMemorySize limits the total capacity rather than the</span>
</span><span class='line'>            <span class="c1">// actual memory usage, which will differ when buffers are page</span>
</span><span class='line'>            <span class="c1">// aligned.</span>
</span><span class='line'>            <span class="k">if</span> <span class="o">(</span><span class="n">cap</span> <span class="o">&lt;=</span> <span class="n">maxMemory</span> <span class="o">-</span> <span class="n">totalCapacity</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>                <span class="n">reservedMemory</span> <span class="o">+=</span> <span class="n">size</span><span class="o">;</span>
</span><span class='line'>                <span class="n">totalCapacity</span> <span class="o">+=</span> <span class="n">cap</span><span class="o">;</span>
</span><span class='line'>                <span class="n">count</span><span class="o">++;</span>
</span><span class='line'>                <span class="k">return</span><span class="o">;</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">gc</span><span class="o">();</span>
</span><span class='line'>        <span class="k">try</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="mi">100</span><span class="o">);</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">InterruptedException</span> <span class="n">x</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="c1">// Restore interrupt status</span>
</span><span class='line'>            <span class="n">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">interrupt</span><span class="o">();</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>        <span class="kd">synchronized</span> <span class="o">(</span><span class="n">Bits</span><span class="o">.</span><span class="na">class</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="k">if</span> <span class="o">(</span><span class="n">totalCapacity</span> <span class="o">+</span> <span class="n">cap</span> <span class="o">&gt;</span> <span class="n">maxMemory</span><span class="o">)</span>
</span><span class='line'>                <span class="k">throw</span> <span class="k">new</span> <span class="nf">OutOfMemoryError</span><span class="o">(</span><span class="s">&quot;Direct buffer memory&quot;</span><span class="o">);</span>
</span><span class='line'>            <span class="n">reservedMemory</span> <span class="o">+=</span> <span class="n">size</span><span class="o">;</span>
</span><span class='line'>            <span class="n">totalCapacity</span> <span class="o">+=</span> <span class="n">cap</span><span class="o">;</span>
</span><span class='line'>            <span class="n">count</span><span class="o">++;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>因此当我们已经分配的内存超过阈值的时候会触发一次gc动作，并重新做一次分配，如果还是超过阈值，那将会抛出OOM，因此分配动作会失败。</p>

<p>所以从这一切看来，只要设置了<code>-XX:MaxDirectMemorySize=1G</code>是不会出现超过这个阈值的情况的，会看到不断的做GC。</p>

<h2>构造函数再探</h2>

<p>那其他的构造函数主要是用在什么情况下的呢？</p>

<p>我们知道DirectByteBuffer回收靠的是里面有个cleaner的属性，但是我们发现有几个构造函数里cleaner这个属性却是null，那这种情况下他们怎么被回收呢？</p>

<p>那下面请大家先看下DirectByteBuffer里的这两个函数：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'>  <span class="kd">public</span> <span class="n">ByteBuffer</span> <span class="nf">slice</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'>        <span class="kt">int</span> <span class="n">pos</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">position</span><span class="o">();</span>
</span><span class='line'>        <span class="kt">int</span> <span class="n">lim</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">limit</span><span class="o">();</span>
</span><span class='line'>        <span class="k">assert</span> <span class="o">(</span><span class="n">pos</span> <span class="o">&lt;=</span> <span class="n">lim</span><span class="o">);</span>
</span><span class='line'>        <span class="kt">int</span> <span class="n">rem</span> <span class="o">=</span> <span class="o">(</span><span class="n">pos</span> <span class="o">&lt;=</span> <span class="n">lim</span> <span class="o">?</span> <span class="n">lim</span> <span class="o">-</span> <span class="n">pos</span> <span class="o">:</span> <span class="mi">0</span><span class="o">);</span>
</span><span class='line'>        <span class="kt">int</span> <span class="n">off</span> <span class="o">=</span> <span class="o">(</span><span class="n">pos</span> <span class="o">&lt;&lt;</span> <span class="mi">0</span><span class="o">);</span>
</span><span class='line'>        <span class="k">assert</span> <span class="o">(</span><span class="n">off</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="o">);</span>
</span><span class='line'>        <span class="k">return</span> <span class="k">new</span> <span class="nf">DirectByteBuffer</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="o">-</span><span class="mi">1</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">rem</span><span class="o">,</span> <span class="n">rem</span><span class="o">,</span> <span class="n">off</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">public</span> <span class="n">ByteBuffer</span> <span class="nf">duplicate</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="k">new</span> <span class="nf">DirectByteBuffer</span><span class="o">(</span><span class="k">this</span><span class="o">,</span>
</span><span class='line'>                                              <span class="k">this</span><span class="o">.</span><span class="na">markValue</span><span class="o">(),</span>
</span><span class='line'>                                              <span class="k">this</span><span class="o">.</span><span class="na">position</span><span class="o">(),</span>
</span><span class='line'>                                              <span class="k">this</span><span class="o">.</span><span class="na">limit</span><span class="o">(),</span>
</span><span class='line'>                                              <span class="k">this</span><span class="o">.</span><span class="na">capacity</span><span class="o">(),</span>
</span><span class='line'>                                              <span class="mi">0</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>从名字和实现上基本都能猜出是干什么的了，slice其实是从一块已知的内存里取出剩下的一部分，用一个新的DirectByteBuffer对象指向它，而duplicate就是创建一个现有DirectByteBuffer的全新副本，各种指针都一样。</p>

<p>因此从这个实现来看，后面关联的堆外内存其实是同一块，所以如果我们做统计的时候如果仅仅将所有DirectByteBuffer对象的capacity加起来，那可能会导致算出来的结果偏大不少，这其实也是我查的那个问题，本来设置了阈值1G，但是发现达到了7G的效果。所以这种情况下使用的构造函数，可以让cleaner为null，回收靠原来的那个DirectByteBuffer对象被回收。</p>

<h2>被遗忘的检查</h2>

<p>但是还有种情况，也是本文要讲的重点，在jvm里可以通过jni方法回调上面的DirectByteBuffer构造函数，这个构造函数是</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">private</span> <span class="nf">DirectByteBuffer</span><span class="o">(</span><span class="kt">long</span> <span class="n">addr</span><span class="o">,</span> <span class="kt">int</span> <span class="n">cap</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">super</span><span class="o">(-</span><span class="mi">1</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">cap</span><span class="o">,</span> <span class="n">cap</span><span class="o">);</span>
</span><span class='line'>    <span class="n">address</span> <span class="o">=</span> <span class="n">addr</span><span class="o">;</span>
</span><span class='line'>    <span class="n">cleaner</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
</span><span class='line'>    <span class="n">att</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>而调用这个构造函数的jni方法是<code>jni_NewDirectByteBuffer</code></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">extern</span> <span class="s">&quot;C&quot;</span> <span class="n">jobject</span> <span class="n">JNICALL</span> <span class="nf">jni_NewDirectByteBuffer</span><span class="o">(</span><span class="n">JNIEnv</span> <span class="o">*</span><span class="n">env</span><span class="o">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">address</span><span class="o">,</span> <span class="n">jlong</span> <span class="n">capacity</span><span class="o">)</span>
</span><span class='line'><span class="o">{</span>
</span><span class='line'>  <span class="c1">// thread_from_jni_environment() will block if VM is gone.</span>
</span><span class='line'>  <span class="n">JavaThread</span><span class="o">*</span> <span class="n">thread</span> <span class="o">=</span> <span class="nl">JavaThread:</span><span class="o">:</span><span class="n">thread_from_jni_environment</span><span class="o">(</span><span class="n">env</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">JNIWrapper</span><span class="o">(</span><span class="s">&quot;jni_NewDirectByteBuffer&quot;</span><span class="o">);</span>
</span><span class='line'><span class="err">#</span><span class="n">ifndef</span> <span class="n">USDT2</span>
</span><span class='line'>  <span class="nf">DTRACE_PROBE3</span><span class="o">(</span><span class="n">hotspot_jni</span><span class="o">,</span> <span class="n">NewDirectByteBuffer__entry</span><span class="o">,</span> <span class="n">env</span><span class="o">,</span> <span class="n">address</span><span class="o">,</span> <span class="n">capacity</span><span class="o">);</span>
</span><span class='line'><span class="err">#</span><span class="k">else</span> <span class="cm">/* USDT2 */</span>
</span><span class='line'> <span class="n">HOTSPOT_JNI_NEWDIRECTBYTEBUFFER_ENTRY</span><span class="o">(</span>
</span><span class='line'>                                       <span class="n">env</span><span class="o">,</span> <span class="n">address</span><span class="o">,</span> <span class="n">capacity</span><span class="o">);</span>
</span><span class='line'><span class="err">#</span><span class="n">endif</span> <span class="cm">/* USDT2 */</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">if</span> <span class="o">(!</span><span class="n">directBufferSupportInitializeEnded</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(!</span><span class="n">initializeDirectBufferSupport</span><span class="o">(</span><span class="n">env</span><span class="o">,</span> <span class="n">thread</span><span class="o">))</span> <span class="o">{</span>
</span><span class='line'><span class="err">#</span><span class="n">ifndef</span> <span class="n">USDT2</span>
</span><span class='line'>      <span class="nf">DTRACE_PROBE1</span><span class="o">(</span><span class="n">hotspot_jni</span><span class="o">,</span> <span class="n">NewDirectByteBuffer__return</span><span class="o">,</span> <span class="n">NULL</span><span class="o">);</span>
</span><span class='line'><span class="err">#</span><span class="k">else</span> <span class="cm">/* USDT2 */</span>
</span><span class='line'>      <span class="n">HOTSPOT_JNI_NEWDIRECTBYTEBUFFER_RETURN</span><span class="o">(</span>
</span><span class='line'>                                             <span class="n">NULL</span><span class="o">);</span>
</span><span class='line'><span class="err">#</span><span class="n">endif</span> <span class="cm">/* USDT2 */</span>
</span><span class='line'>      <span class="k">return</span> <span class="n">NULL</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// Being paranoid about accidental sign extension on address</span>
</span><span class='line'>  <span class="n">jlong</span> <span class="n">addr</span> <span class="o">=</span> <span class="o">(</span><span class="n">jlong</span><span class="o">)</span> <span class="o">((</span><span class="n">uintptr_t</span><span class="o">)</span> <span class="n">address</span><span class="o">);</span>
</span><span class='line'>  <span class="c1">// NOTE that package-private DirectByteBuffer constructor currently</span>
</span><span class='line'>  <span class="c1">// takes int capacity</span>
</span><span class='line'>  <span class="n">jint</span>  <span class="n">cap</span>  <span class="o">=</span> <span class="o">(</span><span class="n">jint</span><span class="o">)</span>  <span class="n">capacity</span><span class="o">;</span>
</span><span class='line'>  <span class="n">jobject</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">env</span><span class="o">-&gt;</span><span class="n">NewObject</span><span class="o">(</span><span class="n">directByteBufferClass</span><span class="o">,</span> <span class="n">directByteBufferConstructor</span><span class="o">,</span> <span class="n">addr</span><span class="o">,</span> <span class="n">cap</span><span class="o">);</span>
</span><span class='line'><span class="err">#</span><span class="n">ifndef</span> <span class="n">USDT2</span>
</span><span class='line'>  <span class="nf">DTRACE_PROBE1</span><span class="o">(</span><span class="n">hotspot_jni</span><span class="o">,</span> <span class="n">NewDirectByteBuffer__return</span><span class="o">,</span> <span class="n">ret</span><span class="o">);</span>
</span><span class='line'><span class="err">#</span><span class="k">else</span> <span class="cm">/* USDT2 */</span>
</span><span class='line'>  <span class="n">HOTSPOT_JNI_NEWDIRECTBYTEBUFFER_RETURN</span><span class="o">(</span>
</span><span class='line'>                                         <span class="n">ret</span><span class="o">);</span>
</span><span class='line'><span class="err">#</span><span class="n">endif</span> <span class="cm">/* USDT2 */</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">ret</span><span class="o">;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>想象这么种情况，我们写了一个native方法，里面分配了一块内存，同时通过上面这个方法和一个DirectByteBuffer对象关联起来，那从java层面来看这个DirectByteBuffer确实是一个有效的占有不少native内存的对象，但是这个对象后面关联的内存完全绕过了MaxDirectMemorySize的check，所以也可能给你造成这种现象，明明设置了MaxDirectMemorySize，但是发现DirectByteBuffer关联的堆外内存其实是大于它的。</p>

<h2>欢迎各位关注个人微信公众号，主要围绕JVM写一系列的原理性，性能调优的文章</h2>

<p><img src="http://nijiaben.github.io/images/gzh.jpg" width="200" height="200"></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[YGC前后新生代变大？]]></title>
    <link href="http://nijiaben.github.io/blog/2016/05/18/ygc-worse/"/>
    <updated>2016-05-18T18:59:44+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/05/18/ygc-worse</id>
    <content type="html"><![CDATA[<h2>问题描述</h2>

<p>我们都知道gc是为了释放内存，但是你是否碰到过ygc前后新生代反增不减的情况呢？gc日志效果类似下面的：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="mi">2016</span><span class="o">-</span><span class="mo">05</span><span class="o">-</span><span class="mi">18</span><span class="nl">T15</span><span class="p">:</span><span class="mo">06</span><span class="o">:</span><span class="mf">13.011</span><span class="o">+</span><span class="mi">0800</span><span class="o">:</span> <span class="p">[</span><span class="n">GC</span> <span class="p">[</span><span class="n">ParNew</span> <span class="p">(</span><span class="n">promotion</span> <span class="n">failed</span><span class="p">)</span><span class="o">:</span> <span class="mi">636088</span><span class="n">K</span><span class="o">-&gt;</span><span class="mi">690555</span><span class="n">K</span><span class="p">(</span><span class="mi">707840</span><span class="n">K</span><span class="p">),</span> <span class="mf">0.2958900</span> <span class="n">secs</span><span class="p">][</span><span class="nl">CMS</span><span class="p">:</span> <span class="mi">1019739</span><span class="n">K</span><span class="o">-&gt;</span><span class="mi">1019733</span><span class="n">K</span><span class="p">(</span><span class="mi">1310720</span><span class="n">K</span><span class="p">),</span> <span class="mf">2.6208600</span> <span class="n">secs</span><span class="p">]</span> <span class="mi">1655820</span><span class="n">K</span><span class="o">-&gt;</span><span class="mi">1655820</span><span class="n">K</span><span class="p">(</span><span class="mi">2018560</span><span class="n">K</span><span class="p">),</span> <span class="p">[</span><span class="n">CMS</span> <span class="nl">Perm</span> <span class="p">:</span> <span class="mi">205486</span><span class="n">K</span><span class="o">-&gt;</span><span class="mi">205486</span><span class="n">K</span><span class="p">(</span><span class="mi">262144</span><span class="n">K</span><span class="p">)],</span> <span class="mf">2.9174390</span> <span class="n">secs</span><span class="p">]</span> <span class="p">[</span><span class="nl">Times</span><span class="p">:</span> <span class="n">user</span><span class="o">=</span><span class="mf">3.74</span> <span class="n">sys</span><span class="o">=</span><span class="mf">0.01</span><span class="p">,</span> <span class="n">real</span><span class="o">=</span><span class="mf">2.91</span> <span class="n">secs</span><span class="p">]</span>
</span></code></pre></td></tr></table></div></figure>


<p>从上面的gc日志来看，我们新生代使用的是ParNew，而老生代用的是CMS GC，我们注意到ParNew的效果是新生代从636088K新增到了690555K，这是什么情况？</p>

<!--more-->


<h2>原理分析</h2>

<p>要解释这个问题，我们先要弄清楚YGC的过程，parNew是新生代的gc算法，简单来说从gc roots开始扫描对象，当扫到一个只要是属于新生代的对象就将其挪到to space，但是老的对象还不会做释放，直到gc完成之后再看是否释放老的对象(比如说上面我们看到了<code>promotion failed</code>的关键字，意味着晋升失败了，也就是说to和old都装不下新生代晋升来的对象，那么在这种情况下其实是不会对eden和from里的老对象做释放的，尽管to space里已经可能存在一份副本了)，但是在gc前后不管是否晋升成功，都会对from space和to space做一个对换，也就是原来的from变成to，原来的to变成from，再来看看打印gc前后内存变化的代码</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="kt">void</span> <span class="n">GenCollectedHeap</span><span class="o">::</span><span class="n">print_heap_change</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">prev_used</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">PrintGCDetails</span> <span class="o">&amp;&amp;</span> <span class="n">Verbose</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">gclog_or_tty</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="s">&quot; &quot;</span>  <span class="n">SIZE_FORMAT</span>
</span><span class='line'>                        <span class="s">&quot;-&gt;&quot;</span> <span class="n">SIZE_FORMAT</span>
</span><span class='line'>                        <span class="s">&quot;(&quot;</span>  <span class="n">SIZE_FORMAT</span> <span class="s">&quot;)&quot;</span><span class="p">,</span>
</span><span class='line'>                        <span class="n">prev_used</span><span class="p">,</span> <span class="n">used</span><span class="p">(),</span> <span class="n">capacity</span><span class="p">());</span>
</span><span class='line'>  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">gclog_or_tty</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="s">&quot; &quot;</span>  <span class="n">SIZE_FORMAT</span> <span class="s">&quot;K&quot;</span>
</span><span class='line'>                        <span class="s">&quot;-&gt;&quot;</span> <span class="n">SIZE_FORMAT</span> <span class="s">&quot;K&quot;</span>
</span><span class='line'>                        <span class="s">&quot;(&quot;</span>  <span class="n">SIZE_FORMAT</span> <span class="s">&quot;K)&quot;</span><span class="p">,</span>
</span><span class='line'>                        <span class="n">prev_used</span> <span class="o">/</span> <span class="n">K</span><span class="p">,</span> <span class="n">used</span><span class="p">()</span> <span class="o">/</span> <span class="n">K</span><span class="p">,</span> <span class="n">capacity</span><span class="p">()</span> <span class="o">/</span> <span class="n">K</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kt">size_t</span> <span class="n">GenCollectedHeap</span><span class="o">::</span><span class="n">used</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span>
</span><span class='line'>  <span class="kt">size_t</span> <span class="n">res</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'>  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_n_gens</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">res</span> <span class="o">+=</span> <span class="n">_gens</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-&gt;</span><span class="n">used</span><span class="p">();</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kt">size_t</span> <span class="n">DefNewGeneration</span><span class="o">::</span><span class="n">used</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">eden</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">used</span><span class="p">()</span>
</span><span class='line'>       <span class="o">+</span> <span class="n">from</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">used</span><span class="p">();</span>      <span class="c1">// to() is only used during scavenge</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>从上面代码我们知道，gc之后的内存情况是used()方法返回的，其中新生代的used方法返回的是eden+from的内存，同样的上面的prev_used也是这么计算的，只是发生在gc之前，这样一来，根据我上面提到的情况，在gc之后不管是否成功都会做一次from和to的swap，那么gc之前新生代的使用大小，其实是gc之前eden+from的使用大小，而gc之后的新生代的使用大小，其实是eden+原来的to现在是使用的大小，原来的to现在使用的大小其实就是在gc过程中将eden和from拷贝过来的对象所占的大小。</p>

<p>综上分析你应该知道为什么会出现这种情况了，其实是一种特殊情况，只有在出现<code>promotion failed</code>的情况下才会发生这样的情况，因为在这个情况下存在to里新增对象，而from和eden不会变化的情况</p>

<h1>欢迎各位关注个人微信公众号，主要围绕JVM写一系列的原理性，性能调优的文章</h1>

<p><img src="http://nijiaben.github.io/images/gzh.jpg" width="200" height="200"></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之JDK8下的僵尸(无法回收)类加载器]]></title>
    <link href="http://nijiaben.github.io/blog/2016/04/24/classloader-unload/"/>
    <updated>2016-04-24T11:21:24+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/04/24/classloader-unload</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>这篇文章基于最近在排查的一个问题，花了我们团队不少时间来排查这个问题，现象是有一些类加载器是作为key放到WeakHashMap里的，但是经历过多次full gc之后，依然坚挺地存在内存里，但是从代码上来说这些类加载器是应该被回收的，因为没有任何强引用可以到达这些类加载器了，于是我们做了内存dump，分析了下内存，发现除了一个WeakHashMap外并没有别的GC ROOT途径达到这些类加载器了，那这样一来经过多次FULL GC肯定是可以被回收的，但是事实却不是这样，为了让这个问题听起来更好理解，还是照例先上个Demo，完全模拟了这种场景。</p>

<!--more-->


<h2>Demo</h2>

<p>首先我们创建两个类AAA和AAB，分别打包到两个不同jar里，比如AAA.jar和AAB.jar，这两个类之间是有关系的，AAA里有个属性是AAB类型的，注意这两个jar不要放到classpath里让appClassLoader加载到：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="n">public</span> <span class="n">class</span> <span class="n">AAA</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">private</span> <span class="n">AAB</span> <span class="n">aab</span><span class="p">;</span>
</span><span class='line'>        <span class="n">public</span> <span class="nf">AAA</span><span class="p">(){</span>
</span><span class='line'>                <span class="n">aab</span><span class="o">=</span><span class="n">new</span> <span class="n">AAB</span><span class="p">();</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>        <span class="n">public</span> <span class="kt">void</span> <span class="nf">clear</span><span class="p">(){</span>
</span><span class='line'>                <span class="n">aab</span><span class="o">=</span><span class="n">null</span><span class="p">;</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">public</span> <span class="n">class</span> <span class="n">AAB</span> <span class="p">{}</span>
</span></code></pre></td></tr></table></div></figure>


<p>接着我们创建一个类加载TestLoader，里面存一个WeakHashMap，专门来存TestLoader的，并且复写loadClass方法，如果是加载AAB这个类，就创建一个新的TestLoader来从AAB.jar里加载这个类</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="n">import</span> <span class="n">java</span><span class="p">.</span><span class="n">net</span><span class="p">.</span><span class="n">URL</span><span class="p">;</span>
</span><span class='line'><span class="n">import</span> <span class="n">java</span><span class="p">.</span><span class="n">net</span><span class="p">.</span><span class="n">URLClassLoader</span><span class="p">;</span>
</span><span class='line'><span class="n">import</span> <span class="n">java</span><span class="p">.</span><span class="n">util</span><span class="p">.</span><span class="n">WeakHashMap</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="n">public</span> <span class="n">class</span> <span class="n">TestLoader</span> <span class="n">extends</span> <span class="n">URLClassLoader</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">public</span> <span class="k">static</span> <span class="n">WeakHashMap</span><span class="o">&lt;</span><span class="n">TestLoader</span><span class="p">,</span><span class="n">Object</span><span class="o">&gt;</span> <span class="n">map</span><span class="o">=</span><span class="n">new</span> <span class="n">WeakHashMap</span><span class="o">&lt;</span><span class="n">TestLoader</span><span class="p">,</span><span class="n">Object</span><span class="o">&gt;</span><span class="p">();</span>
</span><span class='line'>        <span class="n">private</span> <span class="k">static</span> <span class="kt">int</span> <span class="n">count</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
</span><span class='line'>        <span class="n">public</span> <span class="nf">TestLoader</span><span class="p">(</span><span class="n">URL</span><span class="p">[]</span> <span class="n">urls</span><span class="p">){</span>
</span><span class='line'>                <span class="n">super</span><span class="p">(</span><span class="n">urls</span><span class="p">);</span>
</span><span class='line'>                <span class="n">map</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">new</span> <span class="n">Object</span><span class="p">());</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>        <span class="err">@</span><span class="n">SuppressWarnings</span><span class="p">(</span><span class="s">&quot;resource&quot;</span><span class="p">)</span>
</span><span class='line'>        <span class="n">public</span> <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">loadClass</span><span class="p">(</span><span class="n">String</span> <span class="n">name</span><span class="p">)</span> <span class="n">throws</span> <span class="n">ClassNotFoundException</span> <span class="p">{</span>
</span><span class='line'>                <span class="k">if</span><span class="p">(</span><span class="n">name</span><span class="p">.</span><span class="n">equals</span><span class="p">(</span><span class="s">&quot;AAB&quot;</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">count</span><span class="o">==</span><span class="mi">0</span><span class="p">){</span>
</span><span class='line'>                        <span class="n">try</span> <span class="p">{</span>
</span><span class='line'>                                <span class="n">count</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>
</span><span class='line'>                    <span class="n">URL</span><span class="p">[]</span> <span class="n">urls</span> <span class="o">=</span> <span class="n">new</span> <span class="n">URL</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span><span class='line'>                    <span class="n">urls</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">new</span> <span class="n">URL</span><span class="p">(</span><span class="s">&quot;file:///home/nijiaben/tmp/AAB.jar&quot;</span><span class="p">);</span>
</span><span class='line'>                    <span class="k">return</span> <span class="n">new</span> <span class="nf">TestLoader</span><span class="p">(</span><span class="n">urls</span><span class="p">).</span><span class="n">loadClass</span><span class="p">(</span><span class="s">&quot;AAB&quot;</span><span class="p">);</span>
</span><span class='line'>                <span class="p">}</span><span class="n">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">e</span><span class="p">){</span>
</span><span class='line'>                    <span class="n">e</span><span class="p">.</span><span class="n">printStackTrace</span><span class="p">();</span>
</span><span class='line'>                <span class="p">}</span>
</span><span class='line'>                <span class="p">}</span><span class="k">else</span><span class="p">{</span>
</span><span class='line'>                        <span class="k">return</span> <span class="n">super</span><span class="p">.</span><span class="n">loadClass</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>
</span><span class='line'>                <span class="p">}</span>
</span><span class='line'>                <span class="k">return</span> <span class="n">null</span><span class="p">;</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>再看我们的主类TTest，一些说明都写在类里了：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="n">import</span> <span class="n">java</span><span class="p">.</span><span class="n">lang</span><span class="p">.</span><span class="n">reflect</span><span class="p">.</span><span class="n">Method</span><span class="p">;</span>
</span><span class='line'><span class="n">import</span> <span class="n">java</span><span class="p">.</span><span class="n">net</span><span class="p">.</span><span class="n">URL</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * Created by nijiaben on 4/22/16.</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="n">public</span> <span class="n">class</span> <span class="n">TTest</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">private</span> <span class="n">Object</span> <span class="n">aaa</span><span class="p">;</span>
</span><span class='line'>    <span class="n">public</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="p">(</span><span class="n">String</span> <span class="n">args</span><span class="p">[]){</span>
</span><span class='line'>        <span class="n">try</span> <span class="p">{</span>
</span><span class='line'>            <span class="n">TTest</span> <span class="n">tt</span> <span class="o">=</span> <span class="n">new</span> <span class="n">TTest</span><span class="p">();</span>
</span><span class='line'>            <span class="c1">//将对象移到old，并置空aaa的aab属性</span>
</span><span class='line'>            <span class="n">test</span><span class="p">(</span><span class="n">tt</span><span class="p">);</span>
</span><span class='line'>            <span class="c1">//清理掉aab对象</span>
</span><span class='line'>            <span class="n">System</span><span class="p">.</span><span class="n">gc</span><span class="p">();</span>
</span><span class='line'>            <span class="n">System</span><span class="p">.</span><span class="n">out</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&quot;finished&quot;</span><span class="p">);</span>
</span><span class='line'>        <span class="p">}</span><span class="n">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">e</span><span class="p">){</span>
</span><span class='line'>            <span class="n">e</span><span class="p">.</span><span class="n">printStackTrace</span><span class="p">();</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="err">@</span><span class="n">SuppressWarnings</span><span class="p">(</span><span class="s">&quot;resource&quot;</span><span class="p">)</span>
</span><span class='line'>        <span class="n">public</span> <span class="k">static</span> <span class="kt">void</span> <span class="n">test</span><span class="p">(</span><span class="n">TTest</span> <span class="n">tt</span><span class="p">){</span>
</span><span class='line'>        <span class="n">try</span> <span class="p">{</span>
</span><span class='line'>          <span class="c1">//创建一个新的类加载器，从AAA.jar里加载AAA类</span>
</span><span class='line'>            <span class="n">URL</span><span class="p">[]</span> <span class="n">urls</span> <span class="o">=</span> <span class="n">new</span> <span class="n">URL</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span><span class='line'>            <span class="n">urls</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">new</span> <span class="n">URL</span><span class="p">(</span><span class="s">&quot;file:///home/nijiaben/tmp/AAA.jar&quot;</span><span class="p">);</span>
</span><span class='line'>            <span class="n">tt</span><span class="p">.</span><span class="n">aaa</span><span class="o">=</span><span class="n">new</span> <span class="n">TestLoader</span><span class="p">(</span><span class="n">urls</span><span class="p">).</span><span class="n">loadClass</span><span class="p">(</span><span class="s">&quot;AAA&quot;</span><span class="p">).</span><span class="n">newInstance</span><span class="p">();</span>
</span><span class='line'>            <span class="c1">//保证类加载器对象能进入到old里，因为ygc是不会对classLoader做清理的</span>
</span><span class='line'>            <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="n">i</span><span class="o">&lt;</span><span class="mi">10</span><span class="p">;</span><span class="n">i</span><span class="o">++</span><span class="p">){</span>
</span><span class='line'>                <span class="n">System</span><span class="p">.</span><span class="n">gc</span><span class="p">();</span>
</span><span class='line'>                <span class="n">Thread</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
</span><span class='line'>            <span class="p">}</span>
</span><span class='line'>            <span class="c1">//将aaa里的aab属性清空掉，以便在后面gc的时候能清理掉aab对象，这样AAB的类加载器其实就没有什么地方有强引用了，在full gc的时候能被回收</span>
</span><span class='line'>            <span class="n">Method</span><span class="p">[]</span> <span class="n">methods</span><span class="o">=</span><span class="n">tt</span><span class="p">.</span><span class="n">aaa</span><span class="p">.</span><span class="n">getClass</span><span class="p">().</span><span class="n">getDeclaredMethods</span><span class="p">();</span>
</span><span class='line'>            <span class="k">for</span><span class="p">(</span><span class="n">Method</span> <span class="nl">m</span><span class="p">:</span><span class="n">methods</span><span class="p">){</span>
</span><span class='line'>                <span class="k">if</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">getName</span><span class="p">().</span><span class="n">equals</span><span class="p">(</span><span class="s">&quot;clear&quot;</span><span class="p">)){</span>
</span><span class='line'>                        <span class="n">m</span><span class="p">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">tt</span><span class="p">.</span><span class="n">aaa</span><span class="p">);</span>
</span><span class='line'>                        <span class="k">break</span><span class="p">;</span>
</span><span class='line'>                <span class="p">}</span>
</span><span class='line'>            <span class="p">}</span>
</span><span class='line'>        <span class="p">}</span><span class="n">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">e</span><span class="p">){</span>
</span><span class='line'>            <span class="n">e</span><span class="p">.</span><span class="n">printStackTrace</span><span class="p">();</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>运行的时候请跑在JDK8下，打个断点在<code>System.out.println("finished")</code>的地方，然后做一次内存dump。</p>

<p>从上面的例子中我们得知，TTest是类加载器AppClassLoader加载的，其属性aaa的对象类型是通过TestLoader从AAA.jar里加载的，而aaa里的aab属性是从一个全新的类加载器TestLoader从AAB.jar里加载的，当我们做了多次System GC之后，这些对象会移到old，在做最后一次GC之后，aab对象会从内存里移除，其类加载器此时已经是没有任何地方的强引用了，只有一个WeakHashMap引用它，理论上做GC的时候也应该被回收，但是事实时这个AAB的这个类加载器并没有被回收，从分析结果来看，GC ROOT路径是WeakHashMap，如图所示：</p>

<p><img src="http://nijiaben.github.io/images/2016/04/mat.png"></p>

<h2>JDK8里的metaspace</h2>

<p>这里不得不提的一个概念是JDK8里的metaspace，它是为了取代perm的，至于好处是什么，我个人觉得不是那么明显，有点费力不讨好的感觉，代码改了很多，但是实际收益并不明显，据说是oracle内部斗争的一个结果。</p>

<p>在JDK8里虽然没了perm，但是klass的信息还是要有地方存，jvm里为此分配了两块内存，一块是紧挨着heap来的，就和perm一样，专门用来存klass的信息，可以通过<code>-XX:CompressedClassSpaceSize</code>来设置大小，另外一块和它们不一定连着，主要是存非klass之外的其他信息，比如常量池什么的，可以通过<code>-XX:InitialBootClassLoaderMetaspaceSize</code>来设置，同时我们还可以通过<code>-XX:MaxMetaspaceSize</code>来设置触发metaspace回收的阈值。</p>

<p>每个类加载器都会从全局的metaspace空间里取一些metaChunk管理起来，当有类定义的时候，其实就是从这些内存里分配的，当不够的时候再去全局的metaspace里分配一块并管理起来。</p>

<p>这块具体的情况后面可以专门写一篇文章来介绍，包括内存结构，内存分配，GC等。</p>

<h2>JDK8里的ClassLoaderDataGraph</h2>

<p>每个类加载器都会对应一个ClassLoaderData的数据结构，里面会存譬如具体的类加载器对象，加载的klass，管理内存的metaspace等，它是一个链式结构，会链到下一个ClassLoaderData上，gc的时候通过ClassLoaderDataGraph来遍历这些ClassLoaderData，ClassLoaderDataGraph的第一个ClassLoaderData是bootstrapClassLoader的</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="n">class</span> <span class="nl">ClassLoaderData</span> <span class="p">:</span> <span class="n">public</span> <span class="n">CHeapObj</span><span class="o">&lt;</span><span class="n">mtClass</span><span class="o">&gt;</span> <span class="p">{</span>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'>  <span class="k">static</span> <span class="n">ClassLoaderData</span> <span class="o">*</span> <span class="n">_the_null_class_loader_data</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">oop</span> <span class="n">_class_loader</span><span class="p">;</span>          <span class="c1">// oop used to uniquely identify a class loader</span>
</span><span class='line'>                              <span class="c1">// class loader or a canonical class path</span>
</span><span class='line'>  <span class="n">Dependencies</span> <span class="n">_dependencies</span><span class="p">;</span> <span class="c1">// holds dependencies from this class loader</span>
</span><span class='line'>                              <span class="c1">// data to others.</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">Metaspace</span> <span class="o">*</span> <span class="n">_metaspace</span><span class="p">;</span>  <span class="c1">// Meta-space where meta-data defined by the</span>
</span><span class='line'>                           <span class="c1">// classes in the class loader are allocated.</span>
</span><span class='line'>  <span class="n">Mutex</span><span class="o">*</span> <span class="n">_metaspace_lock</span><span class="p">;</span>  <span class="c1">// Locks the metaspace for allocations and setup.</span>
</span><span class='line'>  <span class="kt">bool</span> <span class="n">_unloading</span><span class="p">;</span>         <span class="c1">// true if this class loader goes away</span>
</span><span class='line'>  <span class="kt">bool</span> <span class="n">_keep_alive</span><span class="p">;</span>        <span class="c1">// if this CLD is kept alive without a keep_alive_object().</span>
</span><span class='line'>  <span class="kt">bool</span> <span class="n">_is_anonymous</span><span class="p">;</span>      <span class="c1">// if this CLD is for an anonymous class</span>
</span><span class='line'>  <span class="k">volatile</span> <span class="kt">int</span> <span class="n">_claimed</span><span class="p">;</span>   <span class="c1">// true if claimed, for example during GC traces.</span>
</span><span class='line'>                           <span class="c1">// To avoid applying oop closure more than once.</span>
</span><span class='line'>                           <span class="c1">// Has to be an int because we cas it.</span>
</span><span class='line'>  <span class="n">Klass</span><span class="o">*</span> <span class="n">_klasses</span><span class="p">;</span>         <span class="c1">// The classes defined by the class loader.</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">JNIHandleBlock</span><span class="o">*</span> <span class="n">_handles</span><span class="p">;</span> <span class="c1">// Handles to constant pool arrays</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// These method IDs are created for the class loader and set to NULL when the</span>
</span><span class='line'>  <span class="c1">// class loader is unloaded.  They are rarely freed, only for redefine classes</span>
</span><span class='line'>  <span class="c1">// and if they lose a data race in InstanceKlass.</span>
</span><span class='line'>  <span class="n">JNIMethodBlock</span><span class="o">*</span>                  <span class="n">_jmethod_ids</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// Metadata to be deallocated when it&#39;s safe at class unloading, when</span>
</span><span class='line'>  <span class="c1">// this class loader isn&#39;t unloaded itself.</span>
</span><span class='line'>  <span class="n">GrowableArray</span><span class="o">&lt;</span><span class="n">Metadata</span><span class="o">*&gt;*</span>      <span class="n">_deallocate_list</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// Support for walking class loader data objects</span>
</span><span class='line'>  <span class="n">ClassLoaderData</span><span class="o">*</span> <span class="n">_next</span><span class="p">;</span> <span class="c1">/// Next loader_datas created</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// ReadOnly and ReadWrite metaspaces (static because only on the null</span>
</span><span class='line'>  <span class="c1">// class loader for now).</span>
</span><span class='line'>  <span class="k">static</span> <span class="n">Metaspace</span><span class="o">*</span> <span class="n">_ro_metaspace</span><span class="p">;</span>
</span><span class='line'>  <span class="k">static</span> <span class="n">Metaspace</span><span class="o">*</span> <span class="n">_rw_metaspace</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>这里提几个属性：</p>

<ul>
<li><code>_class_loader</code> : 就是对应的类加载器对象</li>
<li><code>_keep_alive</code> : 如果这个值是true，那这个类加载器会认为是活的，会将其做为GC ROOT的一部分，gc的时候不会被回收</li>
<li><code>_unloading</code> : 表示这个类加载是否需要卸载的</li>
<li><code>_is_anonymous</code> : 是否匿名，这种ClassLoaderData主要是在lambda表达式里用的，这个我后面会详细说</li>
<li><code>_next</code> : 指向下一个ClassLoaderData，在gc的时候方便遍历</li>
<li><code>_dependencies</code> : 这个属性也是本文的重点，后面会细说</li>
</ul>


<p>再来看下构造函数：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="n">ClassLoaderData</span><span class="o">::</span><span class="n">ClassLoaderData</span><span class="p">(</span><span class="n">Handle</span> <span class="n">h_class_loader</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">is_anonymous</span><span class="p">,</span> <span class="n">Dependencies</span> <span class="n">dependencies</span><span class="p">)</span> <span class="o">:</span>
</span><span class='line'>  <span class="n">_class_loader</span><span class="p">(</span><span class="n">h_class_loader</span><span class="p">()),</span>
</span><span class='line'>  <span class="n">_is_anonymous</span><span class="p">(</span><span class="n">is_anonymous</span><span class="p">),</span>
</span><span class='line'>  <span class="c1">// An anonymous class loader data doesn&#39;t have anything to keep</span>
</span><span class='line'>  <span class="c1">// it from being unloaded during parsing of the anonymous class.</span>
</span><span class='line'>  <span class="c1">// The null-class-loader should always be kept alive.</span>
</span><span class='line'>  <span class="n">_keep_alive</span><span class="p">(</span><span class="n">is_anonymous</span> <span class="o">||</span> <span class="n">h_class_loader</span><span class="p">.</span><span class="n">is_null</span><span class="p">()),</span>
</span><span class='line'>  <span class="n">_metaspace</span><span class="p">(</span><span class="nb">NULL</span><span class="p">),</span> <span class="n">_unloading</span><span class="p">(</span><span class="nb">false</span><span class="p">),</span> <span class="n">_klasses</span><span class="p">(</span><span class="nb">NULL</span><span class="p">),</span>
</span><span class='line'>  <span class="n">_claimed</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="n">_jmethod_ids</span><span class="p">(</span><span class="nb">NULL</span><span class="p">),</span> <span class="n">_handles</span><span class="p">(</span><span class="nb">NULL</span><span class="p">),</span> <span class="n">_deallocate_list</span><span class="p">(</span><span class="nb">NULL</span><span class="p">),</span>
</span><span class='line'>  <span class="n">_next</span><span class="p">(</span><span class="nb">NULL</span><span class="p">),</span> <span class="n">_dependencies</span><span class="p">(</span><span class="n">dependencies</span><span class="p">),</span>
</span><span class='line'>  <span class="n">_metaspace_lock</span><span class="p">(</span><span class="n">new</span> <span class="n">Mutex</span><span class="p">(</span><span class="n">Monitor</span><span class="o">::</span><span class="n">leaf</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="s">&quot;Metaspace allocation lock&quot;</span><span class="p">,</span> <span class="nb">true</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">// empty</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>可见，<code>_keep_ailve</code>属性的值是根据<code>_is_anonymous</code>以及当前类加载器是不是bootstrapClassLoader来的。</p>

<p><code>_keep_alive</code>到底用在哪？其实是在GC的的时候，来决定要不要用Closure或者用什么Closure来扫描对应的ClassLoaderData。</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="kt">void</span> <span class="n">ClassLoaderDataGraph</span><span class="o">::</span><span class="n">roots_cld_do</span><span class="p">(</span><span class="n">CLDClosure</span><span class="o">*</span> <span class="n">strong</span><span class="p">,</span> <span class="n">CLDClosure</span><span class="o">*</span> <span class="n">weak</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">//从最后一个创建的classloader到bootstrapClassloader  </span>
</span><span class='line'>  <span class="k">for</span> <span class="p">(</span><span class="n">ClassLoaderData</span><span class="o">*</span> <span class="n">cld</span> <span class="o">=</span> <span class="n">_head</span><span class="p">;</span>  <span class="n">cld</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="n">cld</span> <span class="o">=</span> <span class="n">cld</span><span class="o">-&gt;</span><span class="n">_next</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">//如果是ygc，那weak和strong是一样的，对所有的类加载器都做扫描，保证它们都是活的 </span>
</span><span class='line'>    <span class="c1">//如果是cms initmark阶段，如果要unload_classes了(should_unload_classes()返回true)，则weak为null，那就只遍历bootstrapclassloader以及正在做匿名类加载的类加载  </span>
</span><span class='line'>    <span class="n">CLDClosure</span><span class="o">*</span> <span class="n">closure</span> <span class="o">=</span> <span class="n">cld</span><span class="o">-&gt;</span><span class="n">keep_alive</span><span class="p">()</span> <span class="o">?</span> <span class="nl">strong</span> <span class="p">:</span> <span class="n">weak</span><span class="p">;</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="n">closure</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="n">closure</span><span class="o">-&gt;</span><span class="n">do_cld</span><span class="p">(</span><span class="n">cld</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h2>类加载器什么时候被回收</h2>

<p>类加载器是否需要被回收，其实就是看这个类加载器对象是否是活的，所谓活的就是这个类加载器加载的任何一个类或者这些类的对象是强可达的，当然还包括这个类加载器本身就是GC ROOT一部分或者有GC ROOT可达的路径，那这个类加载器就肯定不会被回收。</p>

<p>从各种GC情况来看：</p>

<ul>
<li>如果是YGC，类加载器是作为GC ROOT的，也就是都不会被回收</li>
<li>如果是Full GC，只要是死的就会被回收</li>
<li>如果是CMS GC，CMS GC过程也是会做标记的（这是默认情况，不过可以通过一些参数来改变），但是不会做真正的清理，真正的清理动作是发生在下次进入安全点的时候。</li>
</ul>


<h2>僵尸类加载器如何产生</h2>

<p>如果类加载器是与GC ROOT的对象存在真正依赖的这种关系，这种类加载器对象是活的无可厚非，我们通过zprofiler或者mat都可以分析出来，可以将链路绘出来，但是有两种情况例外：</p>

<h3>lambda匿名类加载</h3>

<p>lambda匿名类加载走的是unsafe的defineAnonymousClass方法，这个方法在vm里对应的是下面的方法</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="n">UNSAFE_ENTRY</span><span class="p">(</span><span class="n">jclass</span><span class="p">,</span> <span class="n">Unsafe_DefineAnonymousClass</span><span class="p">(</span><span class="n">JNIEnv</span> <span class="o">*</span><span class="n">env</span><span class="p">,</span> <span class="n">jobject</span> <span class="n">unsafe</span><span class="p">,</span> <span class="n">jclass</span> <span class="n">host_class</span><span class="p">,</span> <span class="n">jbyteArray</span> <span class="n">data</span><span class="p">,</span> <span class="n">jobjectArray</span> <span class="n">cp_patches_jh</span><span class="p">))</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>  <span class="n">instanceKlassHandle</span> <span class="n">anon_klass</span><span class="p">;</span>
</span><span class='line'>  <span class="n">jobject</span> <span class="n">res_jh</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">UnsafeWrapper</span><span class="p">(</span><span class="s">&quot;Unsafe_DefineAnonymousClass&quot;</span><span class="p">);</span>
</span><span class='line'>  <span class="n">ResourceMark</span> <span class="nf">rm</span><span class="p">(</span><span class="n">THREAD</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">HeapWord</span><span class="o">*</span> <span class="n">temp_alloc</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">anon_klass</span> <span class="o">=</span> <span class="n">Unsafe_DefineAnonymousClass_impl</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">host_class</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span>
</span><span class='line'>                                                <span class="n">cp_patches_jh</span><span class="p">,</span>
</span><span class='line'>                                                   <span class="o">&amp;</span><span class="n">temp_alloc</span><span class="p">,</span> <span class="n">THREAD</span><span class="p">);</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">anon_klass</span><span class="p">()</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span>
</span><span class='line'>    <span class="n">res_jh</span> <span class="o">=</span> <span class="n">JNIHandles</span><span class="o">::</span><span class="n">make_local</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">anon_klass</span><span class="o">-&gt;</span><span class="n">java_mirror</span><span class="p">());</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// try/finally clause:</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">temp_alloc</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">FREE_C_HEAP_ARRAY</span><span class="p">(</span><span class="n">HeapWord</span><span class="p">,</span> <span class="n">temp_alloc</span><span class="p">,</span> <span class="n">mtInternal</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// The anonymous class loader data has been artificially been kept alive to</span>
</span><span class='line'>  <span class="c1">// this point.   The mirror and any instances of this class have to keep</span>
</span><span class='line'>  <span class="c1">// it alive afterwards.</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">anon_klass</span><span class="p">()</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">anon_klass</span><span class="o">-&gt;</span><span class="n">class_loader_data</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">set_keep_alive</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// let caller initialize it as needed...</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">return</span> <span class="p">(</span><span class="n">jclass</span><span class="p">)</span> <span class="n">res_jh</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="n">UNSAFE_END</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>可见，在创建成功匿名类之后，会将对应的ClassLoaderData的<code>_keep_alive</code>属性设置为false，那是不是意味着<code>_keep_alive</code>属性在这之前都是true呢？下面的<code>parse_stream</code>方法是从上面的方法最终会调下来的方法</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="n">Klass</span><span class="o">*</span> <span class="n">SystemDictionary</span><span class="o">::</span><span class="n">parse_stream</span><span class="p">(</span><span class="n">Symbol</span><span class="o">*</span> <span class="n">class_name</span><span class="p">,</span>
</span><span class='line'>                                      <span class="n">Handle</span> <span class="n">class_loader</span><span class="p">,</span>
</span><span class='line'>                                      <span class="n">Handle</span> <span class="n">protection_domain</span><span class="p">,</span>
</span><span class='line'>                                      <span class="n">ClassFileStream</span><span class="o">*</span> <span class="n">st</span><span class="p">,</span>
</span><span class='line'>                                      <span class="n">KlassHandle</span> <span class="n">host_klass</span><span class="p">,</span>
</span><span class='line'>                                      <span class="n">GrowableArray</span><span class="o">&lt;</span><span class="n">Handle</span><span class="o">&gt;*</span> <span class="n">cp_patches</span><span class="p">,</span>
</span><span class='line'>                                      <span class="n">TRAPS</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">TempNewSymbol</span> <span class="n">parsed_name</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">Ticks</span> <span class="n">class_load_start_time</span> <span class="o">=</span> <span class="n">Ticks</span><span class="o">::</span><span class="n">now</span><span class="p">();</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">ClassLoaderData</span><span class="o">*</span> <span class="n">loader_data</span><span class="p">;</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">host_klass</span><span class="p">.</span><span class="n">not_null</span><span class="p">())</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">// Create a new CLD for anonymous class, that uses the same class loader</span>
</span><span class='line'>    <span class="c1">// as the host_klass</span>
</span><span class='line'>    <span class="n">assert</span><span class="p">(</span><span class="n">EnableInvokeDynamic</span><span class="p">,</span> <span class="s">&quot;&quot;</span><span class="p">);</span>
</span><span class='line'>    <span class="n">guarantee</span><span class="p">(</span><span class="n">host_klass</span><span class="o">-&gt;</span><span class="n">class_loader</span><span class="p">()</span> <span class="o">==</span> <span class="n">class_loader</span><span class="p">(),</span> <span class="s">&quot;should be the same&quot;</span><span class="p">);</span>
</span><span class='line'>    <span class="n">guarantee</span><span class="p">(</span><span class="o">!</span><span class="n">DumpSharedSpaces</span><span class="p">,</span> <span class="s">&quot;must not create anonymous classes when dumping&quot;</span><span class="p">);</span>
</span><span class='line'>    <span class="n">loader_data</span> <span class="o">=</span> <span class="n">ClassLoaderData</span><span class="o">::</span><span class="n">anonymous_class_loader_data</span><span class="p">(</span><span class="n">class_loader</span><span class="p">(),</span> <span class="n">CHECK_NULL</span><span class="p">);</span>
</span><span class='line'>    <span class="n">loader_data</span><span class="o">-&gt;</span><span class="n">record_dependency</span><span class="p">(</span><span class="n">host_klass</span><span class="p">(),</span> <span class="n">CHECK_NULL</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">loader_data</span> <span class="o">=</span> <span class="n">ClassLoaderData</span><span class="o">::</span><span class="n">class_loader_data</span><span class="p">(</span><span class="n">class_loader</span><span class="p">());</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">instanceKlassHandle</span> <span class="n">k</span> <span class="o">=</span> <span class="n">ClassFileParser</span><span class="p">(</span><span class="n">st</span><span class="p">).</span><span class="n">parseClassFile</span><span class="p">(</span><span class="n">class_name</span><span class="p">,</span>
</span><span class='line'>                                                             <span class="n">loader_data</span><span class="p">,</span>
</span><span class='line'>                                                             <span class="n">protection_domain</span><span class="p">,</span>
</span><span class='line'>                                                             <span class="n">host_klass</span><span class="p">,</span>
</span><span class='line'>                                                             <span class="n">cp_patches</span><span class="p">,</span>
</span><span class='line'>                                                             <span class="n">parsed_name</span><span class="p">,</span>
</span><span class='line'>                                                             <span class="nb">true</span><span class="p">,</span>
</span><span class='line'>                                                             <span class="n">THREAD</span><span class="p">);</span>
</span><span class='line'><span class="p">...</span>
</span><span class='line'>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">ClassLoaderData</span><span class="o">*</span> <span class="n">ClassLoaderData</span><span class="o">::</span><span class="n">anonymous_class_loader_data</span><span class="p">(</span><span class="n">oop</span> <span class="n">loader</span><span class="p">,</span> <span class="n">TRAPS</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// Add a new class loader data to the graph.</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">ClassLoaderDataGraph</span><span class="o">::</span><span class="n">add</span><span class="p">(</span><span class="n">loader</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="n">CHECK_NULL</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">ClassLoaderData</span><span class="o">*</span> <span class="n">ClassLoaderDataGraph</span><span class="o">::</span><span class="n">add</span><span class="p">(</span><span class="n">Handle</span> <span class="n">loader</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">is_anonymous</span><span class="p">,</span> <span class="n">TRAPS</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// We need to allocate all the oops for the ClassLoaderData before allocating the</span>
</span><span class='line'>  <span class="c1">// actual ClassLoaderData object.</span>
</span><span class='line'>  <span class="n">ClassLoaderData</span><span class="o">::</span><span class="n">Dependencies</span> <span class="n">dependencies</span><span class="p">(</span><span class="n">CHECK_NULL</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">No_Safepoint_Verifier</span> <span class="n">no_safepoints</span><span class="p">;</span> <span class="c1">// we mustn&#39;t GC until we&#39;ve installed the</span>
</span><span class='line'>                                       <span class="c1">// ClassLoaderData in the graph since the CLD</span>
</span><span class='line'>                                       <span class="c1">// contains unhandled oops</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">ClassLoaderData</span><span class="o">*</span> <span class="n">cld</span> <span class="o">=</span> <span class="n">new</span> <span class="n">ClassLoaderData</span><span class="p">(</span><span class="n">loader</span><span class="p">,</span> <span class="n">is_anonymous</span><span class="p">,</span> <span class="n">dependencies</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="p">...</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>从上面的代码得知，只要走了unsafe的那个方法，都会为当前类加载器创建一个ClassLoaderData对象，并设置其<code>_is_anonymous</code>为true，也同时意味着<code>_keep_alive</code>的属性是true，并加入到ClassLoaderDataGraph中。</p>

<p>试想如果创建的这个匿名类没有成功，也就是<code>anon_klass()==null</code>，那这个<code>_keep_alive</code>属性就永远无法设置为false了，这意味着这个ClassLoaderData对应的ClassLoader对象将永远都是GC ROOT的一部分，无法被回收，这种情况就是真正的僵尸类加载器了，不过目前我还没模拟出这种情况来，有兴趣的同学可以试一试，如果真的能模拟出来，这绝对是JDK里的一个BUG，可以提交给社区。</p>

<h3>类加载器依赖导致的</h3>

<p>这里说的类加载器依赖，并不是说ClassLoader里的parent建立的那种依赖关系，如果是这种关系，那其实通过mat或者zprofiler这样的工具都是可以分析出来的，但是还存在一种情况，那些工具都是分析不出来的，这种关系就是通过ClassLoaderData里的<code>_dependencies</code>属性得出来的，比如说如果A类加载器的<code>_dependencies</code>属性里记录了B类加载器，那当GC遍历A类加载器的时候也会遍历B类加载器，并将其标活，哪怕B类加载器其实是可以被回收了的，可以看下下面的代码</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="kt">void</span> <span class="n">ClassLoaderData</span><span class="o">::</span><span class="n">oops_do</span><span class="p">(</span><span class="n">OopClosure</span><span class="o">*</span> <span class="n">f</span><span class="p">,</span> <span class="n">KlassClosure</span><span class="o">*</span> <span class="n">klass_closure</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">must_claim</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">must_claim</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">claim</span><span class="p">())</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">f</span><span class="o">-&gt;</span><span class="n">do_oop</span><span class="p">(</span><span class="o">&amp;</span><span class="n">_class_loader</span><span class="p">);</span>
</span><span class='line'>  <span class="n">_dependencies</span><span class="p">.</span><span class="n">oops_do</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
</span><span class='line'>  <span class="n">_handles</span><span class="o">-&gt;</span><span class="n">oops_do</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">klass_closure</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">classes_do</span><span class="p">(</span><span class="n">klass_closure</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>那问题来了，这种依赖关系是怎么记录的呢？其实我们上面的demo就模拟了这种情况，可以仔细去看看，我也针对这个demo描述下，比如加载AAA的类加载器TestLoader加载AAA后，并创建AAA对象，此时会看到有个类型是AAB的属性，此时会对常量池里的类型做一个解析，我们看到TestLoader的loadClass方法的时候做了一个判断，如果是AAB类型的类加载，那就创建一个新的类加载器对象从AAB.jar里去加载，当加载返回的时候，在jvm里其实就会记录这么一层依赖关系，认为AAA的类加载器依赖AAB的类加载器，并记录下来，但是纵观所有的hotspot代码，并没有一个地方来清理这种依赖关系的，也就是说只要这种依赖关系建立起来，会一直持续到AAA的类加载器被回收的时候，AAB的类加载器才会被回收，所以说这算一种伪僵尸类加载器，虽然从依赖关系上其实并不依赖了(比如demo里将AAA的aab属性做clear清空动作)，但是GC会一直认为他们是存在这种依赖关系的，会持续存在一段时间，具体持续多久就看AAA类加载器的情况了。</p>

<p>针对这种情况个人认为需要一个类似引用计数的GC策略，当某两个类加载器确实没有任何依赖的时候，将其清理掉这种依赖关系，估计要实现这种改动的地方也挺多，没那么简单，所以当时的设计者或许因为这样并没有这么做了，我觉得这算是偷懒妥协的结果吧，当然这只是我的一种猜测。</p>

<h1>欢迎各位关注个人微信公众号，主要围绕JVM写一系列的原理性，性能调优的文章</h1>

<p><img src="http://nijiaben.github.io/images/gzh.jpg" width="200" height="200"></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之栈溢出完全解读]]></title>
    <link href="http://nijiaben.github.io/blog/2016/04/19/stack-over-flow/"/>
    <updated>2016-04-19T01:24:25+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/04/19/stack-over-flow</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>之所以想写这篇文章，其实是因为最近有不少系统出现了栈溢出导致进程crash的问题，并且很隐蔽，根本原因还得借助coredump才能分析出来，于是想从JVM实现的角度来全面分析下栈溢出的这类问题，或许你碰到过如下的场景:</p>

<ul>
<li>日志里出现了StackOverflowError的异常</li>
<li>进程突然消失了，但是留下了crash日志</li>
<li>进程消失了，crash日志也没有留下</li>
</ul>


<p>这些都可能是栈溢出导致的。</p>

<!--more-->


<h2>如何定位是否是栈溢出</h2>

<p>上面提到的后面两种情况有可能不是我们今天要聊的栈溢出的问题导致的crash，也许是别的一些可能，那如何确定上面三种情况是栈溢出导致的呢？</p>

<ul>
<li>出现了StackOverflowError，这种毫无疑问，必然是栈溢出，具体什么方法导致的栈溢出从栈上是能知道的，不过要提醒一点，我们打印出来看到的栈可能是不全的，因为JVM里对栈的输出条数是可以控制的，默认是1024，这个参数是<code>-XX:MaxJavaStackTraceDepth=1024</code>，可以将这个参数设置为-1，那将会全部输出对应的堆栈</li>
<li>如果进程消失了，但是留下了crash日志，那请检查下crash日志里的Current thread的stack范围，以及RSP寄存器的值，如果RSP寄存器的值是超出这个stack范围的，那说明是栈溢出了。</li>
<li>如果crash日志也没有留下，那只能通过coredump来分析了，在进程运行前，先执行<code>ulimit -c unlimited</code>，然后再跑进程，在进程挂掉之后，会产生一个<code>core.&lt;pid&gt;</code>的文件，然后再通过<code>jstack $JAVA_HOME/bin/java core.&lt;pid&gt;</code>来看输出的栈，如果正常输出了，那就可以看是否存在很长的调用栈的线程，当然还有可能没有正常输出的，因为jstack的这条从core文件抓栈的命令其实是基于serviceability agent来实现的，而SA在某些版本里是存在bug的，当然现在的SA也不能说完全没有bug，还是存在不少bug的，祝你好运。</li>
</ul>


<h2>如何解决栈溢出的问题</h2>

<p>这个需要具体问题具体分析，因为导致栈溢出的原因很多，提三个主要的：
* java代码写得不当，比如出现递归死循环，这也是最常见的，只能靠写代码的人稍微小心了
* native代码有栈上分配的逻辑，并且要求的内存还不小
* 线程栈空间设置比较小</p>

<p>有时候我们的代码需要调用到native里去，最常见的一种情况譬如<code>java.net.SocketInputStream.read0</code>方法，这是一个native方法，在进入到这个方法里之后，它首先就要求到栈上去分配一个64KB的缓存(64位linux)，试想一下如果执行到read0这个方法的时候，剩余的栈空间已经不足以分配64KB的内存了会怎样？也许就是一开头我们提到的crash，这只是一个例子，还有其他的一些native实现，包括我们自己也可能写这种native代码，如果真有这种情况，我们就需要好好斟酌下我们的线程栈到底要设置多大了。</p>

<p>如果我们的代码确实存在正常的很深的递归调用的话，通常是我们的栈可能设置太小，我们可以通过<code>-Xss</code>或者<code>-XX:ThreadStackSize</code>来设置java线程栈的大小，如果两个参数都设置了，那具体有效的是写在后面的那个生效。顺便提下，线程栈内存是和java heap独立的内存，并不是在java heap内分配的，是直接malloc分配的内存。</p>

<h2>线程栈大小</h2>

<p>在jvm里，线程其实不仅仅只有一种，比如我们java里创建的叫做java线程，还有gc线程，编译线程等，默认情况下他们的栈大小如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="kt">size_t</span> <span class="n">os</span><span class="o">::</span><span class="n">Linux</span><span class="o">::</span><span class="n">default_stack_size</span><span class="p">(</span><span class="n">os</span><span class="o">::</span><span class="n">ThreadType</span> <span class="n">thr_type</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// default stack size (compiler thread needs larger stack)</span>
</span><span class='line'><span class="cp">#ifdef AMD64</span>
</span><span class='line'>  <span class="kt">size_t</span> <span class="n">s</span> <span class="o">=</span> <span class="p">(</span><span class="n">thr_type</span> <span class="o">==</span> <span class="n">os</span><span class="o">::</span><span class="n">compiler_thread</span> <span class="o">?</span> <span class="mi">4</span> <span class="o">*</span> <span class="nl">M</span> <span class="p">:</span> <span class="mi">1</span> <span class="o">*</span> <span class="n">M</span><span class="p">);</span>
</span><span class='line'><span class="cp">#else</span>
</span><span class='line'>  <span class="kt">size_t</span> <span class="n">s</span> <span class="o">=</span> <span class="p">(</span><span class="n">thr_type</span> <span class="o">==</span> <span class="n">os</span><span class="o">::</span><span class="n">compiler_thread</span> <span class="o">?</span> <span class="mi">2</span> <span class="o">*</span> <span class="nl">M</span> <span class="p">:</span> <span class="mi">512</span> <span class="o">*</span> <span class="n">K</span><span class="p">);</span>
</span><span class='line'><span class="cp">#endif </span><span class="c1">// AMD64</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">s</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>可见默认情况下编译线程需要的栈空间是其他种类线程的4倍。</p>

<p>各种类型的线程他们所需要的栈的大小其实是可以通过不同的参数来控制的：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="k">switch</span> <span class="p">(</span><span class="n">thr_type</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="k">case</span> <span class="n">os</span><span class="o">::</span><span class="nl">java_thread</span><span class="p">:</span>
</span><span class='line'>        <span class="c1">// Java threads use ThreadStackSize which default value can be</span>
</span><span class='line'>        <span class="c1">// changed with the flag -Xss</span>
</span><span class='line'>        <span class="n">assert</span> <span class="p">(</span><span class="n">JavaThread</span><span class="o">::</span><span class="n">stack_size_at_create</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">,</span> <span class="s">&quot;this should be set&quot;</span><span class="p">);</span>
</span><span class='line'>        <span class="n">stack_size</span> <span class="o">=</span> <span class="n">JavaThread</span><span class="o">::</span><span class="n">stack_size_at_create</span><span class="p">();</span>
</span><span class='line'>        <span class="k">break</span><span class="p">;</span>
</span><span class='line'>      <span class="k">case</span> <span class="n">os</span><span class="o">::</span><span class="nl">compiler_thread</span><span class="p">:</span>
</span><span class='line'>        <span class="k">if</span> <span class="p">(</span><span class="n">CompilerThreadStackSize</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>          <span class="n">stack_size</span> <span class="o">=</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)(</span><span class="n">CompilerThreadStackSize</span> <span class="o">*</span> <span class="n">K</span><span class="p">);</span>
</span><span class='line'>          <span class="k">break</span><span class="p">;</span>
</span><span class='line'>        <span class="p">}</span> <span class="c1">// else fall through:</span>
</span><span class='line'>          <span class="c1">// use VMThreadStackSize if CompilerThreadStackSize is not defined</span>
</span><span class='line'>      <span class="k">case</span> <span class="n">os</span><span class="o">::</span><span class="nl">vm_thread</span><span class="p">:</span>
</span><span class='line'>      <span class="k">case</span> <span class="n">os</span><span class="o">::</span><span class="nl">pgc_thread</span><span class="p">:</span>
</span><span class='line'>      <span class="k">case</span> <span class="n">os</span><span class="o">::</span><span class="nl">cgc_thread</span><span class="p">:</span>
</span><span class='line'>      <span class="k">case</span> <span class="n">os</span><span class="o">::</span><span class="nl">watcher_thread</span><span class="p">:</span>
</span><span class='line'>        <span class="k">if</span> <span class="p">(</span><span class="n">VMThreadStackSize</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="n">stack_size</span> <span class="o">=</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)(</span><span class="n">VMThreadStackSize</span> <span class="o">*</span> <span class="n">K</span><span class="p">);</span>
</span><span class='line'>        <span class="k">break</span><span class="p">;</span>
</span><span class='line'>      <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li><code>java_thread</code>的stack_size，其实就是-Xss或者-XX:ThreadStackSize的值</li>
<li><code>compiler_thread</code>的stack_size，是-XX:CompilerThreadStackSize指定的值</li>
<li>vm内部的线程比如gc线程等可以通过-XX:VMThreadStackSize来设置</li>
</ul>


<h2>JVM里栈溢出的实现</h2>

<p>JVM里的栈溢出到底是怎么实现的，得从栈的大致结构说起：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="c1">// Java thread:</span>
</span><span class='line'><span class="c1">//</span>
</span><span class='line'><span class="c1">//   Low memory addresses</span>
</span><span class='line'><span class="c1">//    +------------------------+</span>
</span><span class='line'><span class="c1">//    |                        |\  JavaThread created by VM does not have glibc</span>
</span><span class='line'><span class="c1">//    |    glibc guard page    | - guard, attached Java thread usually has</span>
</span><span class='line'><span class="c1">//    |                        |/  1 page glibc guard.</span>
</span><span class='line'><span class="c1">// P1 +------------------------+ Thread::stack_base() - Thread::stack_size()</span>
</span><span class='line'><span class="c1">//    |                        |\</span>
</span><span class='line'><span class="c1">//    |  HotSpot Guard Pages   | - red and yellow pages</span>
</span><span class='line'><span class="c1">//    |                        |/</span>
</span><span class='line'><span class="c1">//    +------------------------+ JavaThread::stack_yellow_zone_base()</span>
</span><span class='line'><span class="c1">//    |                        |\</span>
</span><span class='line'><span class="c1">//    |      Normal Stack      | -</span>
</span><span class='line'><span class="c1">//    |                        |/</span>
</span><span class='line'><span class="c1">// P2 +------------------------+ Thread::stack_base()</span>
</span><span class='line'><span class="c1">//</span>
</span><span class='line'><span class="c1">// Non-Java thread:</span>
</span><span class='line'><span class="c1">//</span>
</span><span class='line'><span class="c1">//   Low memory addresses</span>
</span><span class='line'><span class="c1">//    +------------------------+</span>
</span><span class='line'><span class="c1">//    |                        |\</span>
</span><span class='line'><span class="c1">//    |  glibc guard page      | - usually 1 page</span>
</span><span class='line'><span class="c1">//    |                        |/</span>
</span><span class='line'><span class="c1">// P1 +------------------------+ Thread::stack_base() - Thread::stack_size()</span>
</span><span class='line'><span class="c1">//    |                        |\</span>
</span><span class='line'><span class="c1">//    |      Normal Stack      | -</span>
</span><span class='line'><span class="c1">//    |                        |/</span>
</span><span class='line'><span class="c1">// P2 +------------------------+ Thread::stack_base()</span>
</span><span class='line'><span class="c1">//</span>
</span><span class='line'><span class="c1">// ** P1 (aka bottom) and size ( P2 = P1 - size) are the address and stack size returned from</span>
</span><span class='line'><span class="c1">//    pthread_attr_getstack()</span>
</span></code></pre></td></tr></table></div></figure>


<p>linux下java线程栈是从高地址往低地址方向走的，在栈尾（低地址）会预留两块受保护的内存区域，分别叫做yellow page和red page，其中yellow page在前，另外如果是java创建的线程，最后并没有图示的一个page的<code>glibc guard page</code>，非java线程是有的，但是没有yellow和red page，比如我们的gc线程，注意编译线程其实是java线程。</p>

<p>除了yellow page和red page，其实还有个shadow page，这三个page可以分别通过vm参数<code>-XX:StackYellowPages</code>,<code>-XX:StackRedPages</code>,<code>-XX:StackShadowPages</code>来控制。当我们要调用某个java方法的时候，它需要多大的栈其实是预先知道的，javac里就计算好了，但是如果调用的是native方法，那这就不好办了，在native方法里到底需要多大内存，这个无法得知，因此shadow page就是用来做一个大致的预测，看需要多大的栈空间，如果预测到新的RSP的值超过了yellowpage的位置，那就直接抛出栈溢出的异常，否则就去新的方法里处理，当我们的代码访问到yellow page或者red page里的地址的时候，因为这块内存是受保护的，所以会产生SIGSEGV的信号，此时会交给JVM里的信号处理函数来处理，针对yellow page以及red page会有不同的处理策略，其中yellow page的处理是会抛出StackOverflowError的异常，进程不会挂掉，也就是文章开头提到的第一个场景，但是如果是red page，那将直接导致进程退出，不过还是会产生Crash的日志，也就是文章开头提到的第二个场景，另外还有第三个场景，其实是没有栈空间了并且访问了超过了red page的地址，这个时候因为栈空间不够了，所以信号处理函数都进不去，因此就直接crash了，crash日志也不会产生。</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">sig</span> <span class="o">==</span> <span class="n">SIGSEGV</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="n">address</span> <span class="n">addr</span> <span class="o">=</span> <span class="p">(</span><span class="n">address</span><span class="p">)</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">si_addr</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>      <span class="c1">// check if fault address is within thread stack</span>
</span><span class='line'>      <span class="k">if</span> <span class="p">(</span><span class="n">addr</span> <span class="o">&lt;</span> <span class="kr">thread</span><span class="o">-&gt;</span><span class="n">stack_base</span><span class="p">()</span> <span class="o">&amp;&amp;</span>
</span><span class='line'>          <span class="n">addr</span> <span class="o">&gt;=</span> <span class="kr">thread</span><span class="o">-&gt;</span><span class="n">stack_base</span><span class="p">()</span> <span class="o">-</span> <span class="kr">thread</span><span class="o">-&gt;</span><span class="n">stack_size</span><span class="p">())</span> <span class="p">{</span>
</span><span class='line'>        <span class="c1">// stack overflow</span>
</span><span class='line'>        <span class="k">if</span> <span class="p">(</span><span class="kr">thread</span><span class="o">-&gt;</span><span class="n">in_stack_yellow_zone</span><span class="p">(</span><span class="n">addr</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'>          <span class="kr">thread</span><span class="o">-&gt;</span><span class="n">disable_stack_yellow_zone</span><span class="p">();</span>
</span><span class='line'>          <span class="k">if</span> <span class="p">(</span><span class="kr">thread</span><span class="o">-&gt;</span><span class="n">thread_state</span><span class="p">()</span> <span class="o">==</span> <span class="n">_thread_in_Java</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>            <span class="c1">// Throw a stack overflow exception.  Guard pages will be reenabled</span>
</span><span class='line'>            <span class="c1">// while unwinding the stack.</span>
</span><span class='line'>            <span class="n">stub</span> <span class="o">=</span> <span class="n">SharedRuntime</span><span class="o">::</span><span class="n">continuation_for_implicit_exception</span><span class="p">(</span><span class="kr">thread</span><span class="p">,</span> <span class="n">pc</span><span class="p">,</span> <span class="n">SharedRuntime</span><span class="o">::</span><span class="n">STACK_OVERFLOW</span><span class="p">);</span>
</span><span class='line'>          <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'>            <span class="c1">// Thread was in the vm or native code.  Return and try to finish.</span>
</span><span class='line'>            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span><span class='line'>          <span class="p">}</span>
</span><span class='line'>        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="kr">thread</span><span class="o">-&gt;</span><span class="n">in_stack_red_zone</span><span class="p">(</span><span class="n">addr</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'>          <span class="c1">// Fatal red zone violation.  Disable the guard pages and fall through</span>
</span><span class='line'>          <span class="c1">// to handle_unexpected_exception way down below.</span>
</span><span class='line'>          <span class="kr">thread</span><span class="o">-&gt;</span><span class="n">disable_stack_red_zone</span><span class="p">();</span>
</span><span class='line'>          <span class="n">tty</span><span class="o">-&gt;</span><span class="n">print_raw_cr</span><span class="p">(</span><span class="s">&quot;An irrecoverable stack overflow has occurred.&quot;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>          <span class="c1">// This is a likely cause, but hard to verify. Let&#39;s just print</span>
</span><span class='line'>          <span class="c1">// it as a hint.</span>
</span><span class='line'>          <span class="n">tty</span><span class="o">-&gt;</span><span class="n">print_raw_cr</span><span class="p">(</span><span class="s">&quot;Please check if any of your loaded .so files has &quot;</span>
</span><span class='line'>                            <span class="s">&quot;enabled executable stack (see man page execstack(8))&quot;</span><span class="p">);</span>
</span><span class='line'>        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'>          <span class="c1">// Accessing stack address below sp may cause SEGV if current</span>
</span><span class='line'>          <span class="c1">// thread has MAP_GROWSDOWN stack. This should only happen when</span>
</span><span class='line'>          <span class="c1">// current thread was created by user code with MAP_GROWSDOWN flag</span>
</span><span class='line'>          <span class="c1">// and then attached to VM. See notes in os_linux.cpp.</span>
</span><span class='line'>          <span class="k">if</span> <span class="p">(</span><span class="kr">thread</span><span class="o">-&gt;</span><span class="n">osthread</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">expanding_stack</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>             <span class="kr">thread</span><span class="o">-&gt;</span><span class="n">osthread</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">set_expanding_stack</span><span class="p">();</span>
</span><span class='line'>             <span class="k">if</span> <span class="p">(</span><span class="n">os</span><span class="o">::</span><span class="n">Linux</span><span class="o">::</span><span class="n">manually_expand_stack</span><span class="p">(</span><span class="kr">thread</span><span class="p">,</span> <span class="n">addr</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'>               <span class="kr">thread</span><span class="o">-&gt;</span><span class="n">osthread</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">clear_expanding_stack</span><span class="p">();</span>
</span><span class='line'>               <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span><span class='line'>             <span class="p">}</span>
</span><span class='line'>             <span class="kr">thread</span><span class="o">-&gt;</span><span class="n">osthread</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">clear_expanding_stack</span><span class="p">();</span>
</span><span class='line'>          <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'>             <span class="n">fatal</span><span class="p">(</span><span class="s">&quot;recursive segv. expanding stack.&quot;</span><span class="p">);</span>
</span><span class='line'>          <span class="p">}</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>     <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="p">......</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">stub</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">// save all thread context in case we need to restore it</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="kr">thread</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="kr">thread</span><span class="o">-&gt;</span><span class="n">set_saved_exception_pc</span><span class="p">(</span><span class="n">pc</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">uc</span><span class="o">-&gt;</span><span class="n">uc_mcontext</span><span class="p">.</span><span class="n">gregs</span><span class="p">[</span><span class="n">REG_PC</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">greg_t</span><span class="p">)</span><span class="n">stub</span><span class="p">;</span>
</span><span class='line'>    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// signal-chaining</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">os</span><span class="o">::</span><span class="n">Linux</span><span class="o">::</span><span class="n">chained_handler</span><span class="p">(</span><span class="n">sig</span><span class="p">,</span> <span class="n">info</span><span class="p">,</span> <span class="n">ucVoid</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'>     <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">abort_if_unrecognized</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">// caller wants another chance, so give it to him</span>
</span><span class='line'>    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">pc</span> <span class="o">==</span> <span class="nb">NULL</span> <span class="o">&amp;&amp;</span> <span class="n">uc</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">pc</span> <span class="o">=</span> <span class="n">os</span><span class="o">::</span><span class="n">Linux</span><span class="o">::</span><span class="n">ucontext_get_pc</span><span class="p">(</span><span class="n">uc</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// unmask current signal</span>
</span><span class='line'>  <span class="kt">sigset_t</span> <span class="n">newset</span><span class="p">;</span>
</span><span class='line'>  <span class="n">sigemptyset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">newset</span><span class="p">);</span>
</span><span class='line'>  <span class="n">sigaddset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">newset</span><span class="p">,</span> <span class="n">sig</span><span class="p">);</span>
</span><span class='line'>  <span class="n">sigprocmask</span><span class="p">(</span><span class="n">SIG_UNBLOCK</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">newset</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">VMError</span> <span class="nf">err</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">sig</span><span class="p">,</span> <span class="n">pc</span><span class="p">,</span> <span class="n">info</span><span class="p">,</span> <span class="n">ucVoid</span><span class="p">);</span>
</span><span class='line'>  <span class="n">err</span><span class="p">.</span><span class="n">report_and_die</span><span class="p">();</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">ShouldNotReachHere</span><span class="p">();</span>
</span></code></pre></td></tr></table></div></figure>


<p>了解上面的场景之后，再回过头来想想JVM为什么要设置这几个page，其实是为了安全，能预测到栈溢出的话就抛出StackOverfolwError，而避免导致进程挂掉。</p>

<h1>欢迎各位关注个人微信公众号，主要围绕JVM写一系列的原理性，性能调优的文章</h1>

<p><img src="http://nijiaben.github.io/images/gzh.jpg" width="200" height="200"></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JDK8在泛型类型推导上的变化]]></title>
    <link href="http://nijiaben.github.io/blog/2016/04/03/type-inference/"/>
    <updated>2016-04-03T12:50:39+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/04/03/type-inference</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>最近公司在大面积推广JDK8，整体来说升级上来算顺利的，大部分问题可能在编译期就碰到了，但是有些时候比较蛋疼，编译期没有出现问题，但是在运行期就出了问题，比如今天要说的这个话题，所以大家再升级的时候还是要多测测再上线，当然JDK8给我们带来了不少收益，花点时间升级上来还是值得的。</p>

<!--more-->


<h2>问题描述</h2>

<p>还是老规矩，先上demo，让大家直观地知道我们要说的问题。</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>public class Test {
</span><span class='line'>      static &lt;T extends Number&gt; T getObject() {
</span><span class='line'>              return (T)Long.valueOf(1L);
</span><span class='line'>      }
</span><span class='line'>
</span><span class='line'>      public static void main(String... args) throws Exception {
</span><span class='line'>              StringBuilder sb = new StringBuilder();
</span><span class='line'>              sb.append(getObject());
</span><span class='line'>      }
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>demo很简单，就是有个使用了泛型的函数getObject，其返回类型是Number的子类，然后我们将函数返回值传给StringBuilder的多态方法append，我们知道append方法有很多，参数类型也很多，但是唯独没有参数是Number的append方法，如果有的话，大家应该猜到会优先选择这个方法了，既然没有，那到底会选哪个呢，我们分别用jdk6(jdk7类似)和jdk8来编译下上面的类，然后用javap看看输出结果（只看main方法）：</p>

<p>jdk6编译的字节码：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>public static void main(java.lang.String...) throws java.lang.Exception;
</span><span class='line'>    flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
</span><span class='line'>    Code:
</span><span class='line'>      stack=2, locals=2, args_size=1
</span><span class='line'>         0: new           #3                  // class java/lang/StringBuilder
</span><span class='line'>         3: dup
</span><span class='line'>         4: invokespecial #4                  // Method java/lang/StringBuilder."&lt;init&gt;":()V
</span><span class='line'>         7: astore_1
</span><span class='line'>         8: aload_1
</span><span class='line'>         9: invokestatic  #5                  // Method getObject:()Ljava/lang/Number;
</span><span class='line'>        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
</span><span class='line'>        15: pop
</span><span class='line'>        16: return
</span><span class='line'>      LineNumberTable:
</span><span class='line'>        line 8: 0
</span><span class='line'>        line 9: 8
</span><span class='line'>        line 10: 16
</span><span class='line'>    Exceptions:
</span><span class='line'>      throws java.lang.Exception</span></code></pre></td></tr></table></div></figure>


<p>jdk8编译的字节码：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>public static void main(java.lang.String...) throws java.lang.Exception;
</span><span class='line'>    descriptor: ([Ljava/lang/String;)V
</span><span class='line'>    flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
</span><span class='line'>    Code:
</span><span class='line'>      stack=2, locals=2, args_size=1
</span><span class='line'>         0: new           #3                  // class java/lang/StringBuilder
</span><span class='line'>         3: dup
</span><span class='line'>         4: invokespecial #4                  // Method java/lang/StringBuilder."&lt;init&gt;":()V
</span><span class='line'>         7: astore_1
</span><span class='line'>         8: aload_1
</span><span class='line'>         9: invokestatic  #5                  // Method getObject:()Ljava/lang/Number;
</span><span class='line'>        12: checkcast     #6                  // class java/lang/CharSequence
</span><span class='line'>        15: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;
</span><span class='line'>        18: pop
</span><span class='line'>        19: return
</span><span class='line'>      LineNumberTable:
</span><span class='line'>        line 8: 0
</span><span class='line'>        line 9: 8
</span><span class='line'>        line 10: 19
</span><span class='line'>    Exceptions:
</span><span class='line'>      throws java.lang.Exception</span></code></pre></td></tr></table></div></figure>


<p>对比上面那个的差异，我们看到bci从12开始变了，jdk8里多了下面这行表示要对栈顶的数据做一次类型检查看是不是CharSequence类型：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> 12: checkcast     #6                  // class java/lang/CharSequence</span></code></pre></td></tr></table></div></figure>


<p>另外调用的StringBuilder的append方法也是不一样的，jdk7里是调用的参数为Object类型的append方法，而jdk8里调用的是CharSequence类型的append方法。</p>

<p>最主要的是在jdk6和jdk8下运行上面的代码，在jdk6下是正常跑过的，但是在jdk8下是直接抛出异常的：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Exception in thread "main" java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.CharSequence
</span><span class='line'>  at Test.main(Test.java:9)</span></code></pre></td></tr></table></div></figure>


<p>至此问题整个应该描述清楚了。</p>

<h2>问题分析</h2>

<p>先来说说如果要我们来做这个java编译器实现这个功能，我们要怎么来做，其他的都是很明确的，重点在于如下这段如何来确定append的方法使用哪个：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>sb.append(getObject());</span></code></pre></td></tr></table></div></figure>


<p>我们知道getObject()返回的是个泛型对象，这个对象是Number的子类，因此我们首先会去遍历StringBuilder的所有可见的方法，包括从父类继承过来的，找是不是存在一个方法叫做append，并且参数类型是Number的方法，如果有，那就直接使用这个方法，如果没有，那我们得想办法找到一个最合适的方法，关键就在于这个合适怎么定义，比如说我们看到有个append的方法，其参数是Object类型的，Number是Object的子类，所以我们选择这个方法肯定没问题，假如说另外有个append方法，其参数是Serializable类型(当然其实并没有这种参数的方法)，Number实现了这个接口，我们选择这个方法也是没问题的，那到底是Object参数的更合适还是Serializable的更合适呢，还有更甚者，我们知道StringBuilder有个方法，其参数是CharSequence，假如我们传进去的参数其实既是Number的子类，同时又实现了CharSequence这个接口，那我们究竟要不要选它呢？这些问题我们都需要去考虑，而且各有各的理由，说起来都感觉挺合理的。</p>

<h2>JDK6里泛型的类型推导</h2>

<p>这里分析的是jdk6的javac代码，不过大致看了下jdk7的这块针对这个问题的逻辑也差不多，所以就以这块为例了，jdk6里的泛型类型推导其实比较简单，从上面的输出结果我们也猜到了，其实就是选了参数为Object类型的append方法，它觉得它是最合适的：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>private Symbol findMethod(Env&lt;AttrContext&gt; env,
</span><span class='line'>                              Type site,
</span><span class='line'>                              Name name,
</span><span class='line'>                              List&lt;Type&gt; argtypes,
</span><span class='line'>                              List&lt;Type&gt; typeargtypes,
</span><span class='line'>                              Type intype,
</span><span class='line'>                              boolean abstractok,
</span><span class='line'>                              Symbol bestSoFar,
</span><span class='line'>                              boolean allowBoxing,
</span><span class='line'>                              boolean useVarargs,
</span><span class='line'>                              boolean operator) {
</span><span class='line'>        for (Type ct = intype; ct.tag == CLASS; ct = types.supertype(ct)) {
</span><span class='line'>            ClassSymbol c = (ClassSymbol)ct.tsym;
</span><span class='line'>            if ((c.flags() & (ABSTRACT | INTERFACE | ENUM)) == 0)
</span><span class='line'>                abstractok = false;
</span><span class='line'>            for (Scope.Entry e = c.members().lookup(name);
</span><span class='line'>                 e.scope != null;
</span><span class='line'>                 e = e.next()) {
</span><span class='line'>                //- System.out.println(" e " + e.sym);
</span><span class='line'>                if (e.sym.kind == MTH &&
</span><span class='line'>                    (e.sym.flags_field & SYNTHETIC) == 0) {
</span><span class='line'>                    bestSoFar = selectBest(env, site, argtypes, typeargtypes,
</span><span class='line'>                                           e.sym, bestSoFar,
</span><span class='line'>                                           allowBoxing,
</span><span class='line'>                                           useVarargs,
</span><span class='line'>                                           operator);
</span><span class='line'>                }
</span><span class='line'>            }
</span><span class='line'>            //- System.out.println(" - " + bestSoFar);
</span><span class='line'>            if (abstractok) {
</span><span class='line'>                Symbol concrete = methodNotFound;
</span><span class='line'>                if ((bestSoFar.flags() & ABSTRACT) == 0)
</span><span class='line'>                    concrete = bestSoFar;
</span><span class='line'>                for (List&lt;Type&gt; l = types.interfaces(c.type);
</span><span class='line'>                     l.nonEmpty();
</span><span class='line'>                     l = l.tail) {
</span><span class='line'>                    bestSoFar = findMethod(env, site, name, argtypes,
</span><span class='line'>                                           typeargtypes,
</span><span class='line'>                                           l.head, abstractok, bestSoFar,
</span><span class='line'>                                           allowBoxing, useVarargs, operator);
</span><span class='line'>                }
</span><span class='line'>          if (concrete != bestSoFar &&
</span><span class='line'>                    concrete.kind &lt; ERR  && bestSoFar.kind &lt; ERR &&
</span><span class='line'>                    types.isSubSignature(concrete.type, bestSoFar.type))
</span><span class='line'>                    bestSoFar = concrete;
</span><span class='line'>            }
</span><span class='line'>        }
</span><span class='line'>        return bestSoFar;
</span><span class='line'>    }</span></code></pre></td></tr></table></div></figure>


<p>上面的逻辑大概是遍历当前类(比如这个例子中的StringBuilder)及其父类，依次从他们的方法里找出一个最合适的方法返回，重点就落在了selectBest这个方法上:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Symbol selectBest(Env&lt;AttrContext&gt; env,
</span><span class='line'>                      Type site,
</span><span class='line'>                      List&lt;Type&gt; argtypes,
</span><span class='line'>                      List&lt;Type&gt; typeargtypes,
</span><span class='line'>                      Symbol sym,
</span><span class='line'>                      Symbol bestSoFar,
</span><span class='line'>                      boolean allowBoxing,
</span><span class='line'>                      boolean useVarargs,
</span><span class='line'>                      boolean operator) {
</span><span class='line'>        if (sym.kind == ERR) return bestSoFar;
</span><span class='line'>        if (!sym.isInheritedIn(site.tsym, types)) return bestSoFar;
</span><span class='line'>        assert sym.kind &lt; AMBIGUOUS;
</span><span class='line'>        try {
</span><span class='line'>            if (rawInstantiate(env, site, sym, argtypes, typeargtypes,
</span><span class='line'>                               allowBoxing, useVarargs, Warner.noWarnings) == null) {
</span><span class='line'>                // inapplicable
</span><span class='line'>                switch (bestSoFar.kind) {
</span><span class='line'>                case ABSENT_MTH: return wrongMethod.setWrongSym(sym);
</span><span class='line'>                case WRONG_MTH: return wrongMethods;
</span><span class='line'>                default: return bestSoFar;
</span><span class='line'>                }
</span><span class='line'>            }
</span><span class='line'>        } catch (Infer.NoInstanceException ex) {
</span><span class='line'>            switch (bestSoFar.kind) {
</span><span class='line'>            case ABSENT_MTH:
</span><span class='line'>                return wrongMethod.setWrongSym(sym, ex.getDiagnostic());
</span><span class='line'>            case WRONG_MTH:
</span><span class='line'>                return wrongMethods;
</span><span class='line'>            default:
</span><span class='line'>                return bestSoFar;
</span><span class='line'>            }
</span><span class='line'>        }
</span><span class='line'>        if (!isAccessible(env, site, sym)) {
</span><span class='line'>            return (bestSoFar.kind == ABSENT_MTH)
</span><span class='line'>                ? new AccessError(env, site, sym)
</span><span class='line'>                : bestSoFar;
</span><span class='line'>        }
</span><span class='line'>        return (bestSoFar.kind &gt; AMBIGUOUS)
</span><span class='line'>            ? sym
</span><span class='line'>            : mostSpecific(sym, bestSoFar, env, site,
</span><span class='line'>                           allowBoxing && operator, useVarargs);
</span><span class='line'>    }</span></code></pre></td></tr></table></div></figure>


<p>这个方法的主要逻辑落在rawInstantiate这个方法里(具体代码不贴了，有兴趣的去看下代码，我将最终最关键的调用方法argumentsAcceptable贴出来，主要做参数的匹配)，如果当前方法也合适，那就和之前挑出来的最好的方法做一个比较看谁最适合，这个选择过程在最后的mostSpecific方法里，其实就和冒泡排序差不多，不过是找最接近的那个类型(逐层找对应是父类的方法，和最小公倍数有点类似)。</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>boolean argumentsAcceptable(List&lt;Type&gt; argtypes,
</span><span class='line'>                            List&lt;Type&gt; formals,
</span><span class='line'>                            boolean allowBoxing,
</span><span class='line'>                            boolean useVarargs,
</span><span class='line'>                            Warner warn) {
</span><span class='line'>    Type varargsFormal = useVarargs ? formals.last() : null;
</span><span class='line'>    while (argtypes.nonEmpty() && formals.head != varargsFormal) {
</span><span class='line'>        boolean works = allowBoxing
</span><span class='line'>            ? types.isConvertible(argtypes.head, formals.head, warn)
</span><span class='line'>            : types.isSubtypeUnchecked(argtypes.head, formals.head, warn);
</span><span class='line'>        if (!works) return false;
</span><span class='line'>        argtypes = argtypes.tail;
</span><span class='line'>        formals = formals.tail;
</span><span class='line'>    }
</span><span class='line'>    if (formals.head != varargsFormal) return false; // not enough args
</span><span class='line'>    if (!useVarargs)
</span><span class='line'>        return argtypes.isEmpty();
</span><span class='line'>    Type elt = types.elemtype(varargsFormal);
</span><span class='line'>    while (argtypes.nonEmpty()) {
</span><span class='line'>        if (!types.isConvertible(argtypes.head, elt, warn))
</span><span class='line'>            return false;
</span><span class='line'>        argtypes = argtypes.tail;
</span><span class='line'>    }
</span><span class='line'>    return true;
</span><span class='line'>}    </span></code></pre></td></tr></table></div></figure>


<p>针对具体的例子其实就是看StringBuilder里的哪个方法的参数是Number的父类，如果不是就表示没有找到，如果参数都符合期望就表示找到，然后返回。</p>

<p>所以jdk6里的这块的逻辑相对来说比较简单。</p>

<h2>JDK8里泛型的类型推导</h2>

<p>jdk8里的推导相对来说比较复杂，不过大部分逻辑和上面的都差不多，但是argumentsAcceptable这块的变动比较大，增加了一些数据结构，规则变得更加复杂，考虑的场景也更多了，因为代码嵌套层数很深，具体的代码我就不贴了，有兴趣的自己去跟下代码（具体变化可以从AbstractMethodCheck.argumentsAcceptable这个方法开始）。</p>

<p>针对具体这个demo，如果getObject返回的对象既实现了CharSequence，又是Number的子类，那它认为这种情况其实选择参数为CharSequence类型的append方法比参数为Object类型的方法更合适，看起来是要求更严格一些了，适用范围收窄了一些，不是去匹配大而范的接口方法，因此其多加了一层checkcast的检查，不过我个人观点是觉得这块有点太激进了。</p>

<h1>欢迎各位关注个人微信公众号，主要围绕JVM写一系列的原理性，性能调优的文章</h1>

<p><img src="http://nijiaben.github.io/images/gzh.jpg" width="200" height="200"></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[不可逆的类初始化过程]]></title>
    <link href="http://nijiaben.github.io/blog/2016/03/31/class-init/"/>
    <updated>2016-03-31T22:11:56+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/03/31/class-init</id>
    <content type="html"><![CDATA[<p>类的加载过程说复杂很复杂，说简单也简单，说复杂是因为细节很多，比如说今天要说的这个，可能很多人都不了解；说简单，大致都知道类加载有这么几个阶段，loaded->linked->initialized，为了让大家能更轻松地知道我今天说的这个话题，我不详细说类加载的整个过程，改天有时间有精力了我将整个类加载的过程和大家好好说说（PS：我对类加载过程慢慢清晰起来得益于当初在支付宝做cloudengine容器开发的时候，当时引入了标准的osgi，解决类加载的问题几乎是每天的家常便饭，相信大家如果还在使用OSGI，那估计能体会我当时的那种痛，哈哈）。</p>

<!--more-->


<p>本文我想说的是最后一个阶段，类的初始化，但是也不细说其中的过程，只围绕我们今天要说的展开。</p>

<p>我们定义一个类的时候，可能有静态变量，可能有静态代码块，这些逻辑编译之后会封装到一个叫做clinit的方法里，比如下面的代码：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>class BadClass{
</span><span class='line'>    private static int a=100;
</span><span class='line'>    static{
</span><span class='line'>        System.out.println("before init");
</span><span class='line'>        int b=3/0;
</span><span class='line'>        System.out.println("after init");
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    public static void doSomething(){
</span><span class='line'>        System.out.println("do somthing");
</span><span class='line'>    }
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>编译之后我们通过<code>javap -verbose BadClass</code>可以看到如下字节码：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>{
</span><span class='line'>  BadClass();
</span><span class='line'>    flags:
</span><span class='line'>    Code:
</span><span class='line'>      stack=1, locals=1, args_size=1
</span><span class='line'>         0: aload_0
</span><span class='line'>         1: invokespecial #1                  // Method java/lang/Object."&lt;init&gt;":()V
</span><span class='line'>         4: return
</span><span class='line'>      LineNumberTable:
</span><span class='line'>        line 1: 0
</span><span class='line'>
</span><span class='line'>  public static void doSomething();
</span><span class='line'>    flags: ACC_PUBLIC, ACC_STATIC
</span><span class='line'>    Code:
</span><span class='line'>      stack=2, locals=0, args_size=0
</span><span class='line'>         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
</span><span class='line'>         3: ldc           #3                  // String do somthing
</span><span class='line'>         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
</span><span class='line'>         8: return
</span><span class='line'>      LineNumberTable:
</span><span class='line'>        line 10: 0
</span><span class='line'>        line 11: 8
</span><span class='line'>
</span><span class='line'>  static {};
</span><span class='line'>    flags: ACC_STATIC
</span><span class='line'>    Code:
</span><span class='line'>      stack=2, locals=1, args_size=0
</span><span class='line'>         0: bipush        100
</span><span class='line'>         2: putstatic     #5                  // Field a:I
</span><span class='line'>         5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
</span><span class='line'>         8: ldc           #6                  // String before init
</span><span class='line'>        10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
</span><span class='line'>        13: iconst_3
</span><span class='line'>        14: iconst_0
</span><span class='line'>        15: idiv
</span><span class='line'>        16: istore_0
</span><span class='line'>        17: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
</span><span class='line'>        20: ldc           #7                  // String after init
</span><span class='line'>        22: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
</span><span class='line'>        25: return
</span><span class='line'>      LineNumberTable:
</span><span class='line'>        line 2: 0
</span><span class='line'>        line 4: 5
</span><span class='line'>        line 5: 13
</span><span class='line'>        line 6: 17
</span><span class='line'>        line 7: 25
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>我们看到最后那个方法<code>static{}</code>，其实就是我上面说的clinit方法，我们看到静态字段的初始化和静态代码库都封装在这个方法里。</p>

<p>假如我们通过如下代码来测试上面的类：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> public static void main(String args[]){
</span><span class='line'>        try{
</span><span class='line'>            BadClass.doSomething();
</span><span class='line'>        }catch (Throwable e){
</span><span class='line'>            e.printStackTrace();
</span><span class='line'>        }
</span><span class='line'>
</span><span class='line'>        BadClass.doSomething();
</span><span class='line'>    }</span></code></pre></td></tr></table></div></figure>


<p>大家觉得输出会是什么？是会打印多次<code>before init</code>吗？其实不然，输出结果如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>before init
</span><span class='line'>java.lang.ExceptionInInitializerError
</span><span class='line'>  at ObjectTest.main(ObjectTest.java:7)
</span><span class='line'>  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
</span><span class='line'>  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
</span><span class='line'>  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
</span><span class='line'>  at java.lang.reflect.Method.invoke(Method.java:606)
</span><span class='line'>  at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
</span><span class='line'>Caused by: java.lang.ArithmeticException: / by zero
</span><span class='line'>  at BadClass.&lt;clinit&gt;(ObjectTest.java:25)
</span><span class='line'>  ... 6 more
</span><span class='line'>Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class BadClass
</span><span class='line'>  at ObjectTest.main(ObjectTest.java:12)
</span><span class='line'>  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
</span><span class='line'>  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
</span><span class='line'>  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
</span><span class='line'>  at java.lang.reflect.Method.invoke(Method.java:606)
</span><span class='line'>  at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)</span></code></pre></td></tr></table></div></figure>


<p>也就是说其实是只输出了一次<code>before init</code>，这是为什么呢？</p>

<p>clinit方法在我们第一次主动使用这个类的时候会触发执行，比如我们访问这个类的静态方法或者静态字段就会触发执行clinit，但是这个过程是不可逆的，也就是说当我们执行一遍之后再也不会执行了，如果在执行这个方法过程中出现了异常没有被捕获，那这个类将永远不可用，虽然我们上面执行<code>BadClass.doSomething()</code>的时候catch住了异常，但是当代码跑到这里的时候，在jvm里已经将这个类打上标记了，说这个类初始化失败了，下次再初始化的时候就会直接返回并抛出类似的异常<code>java.lang.NoClassDefFoundError: Could not initialize class BadClass</code>，而不去再次执行初始化的逻辑，具体可以看下jvm里对类的状态定义：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> enum ClassState {
</span><span class='line'>    unparsable_by_gc = 0,               // object is not yet parsable by gc. Value of _init_state at object allocation.
</span><span class='line'>    allocated,                          // allocated (but not yet linked)
</span><span class='line'>    loaded,                             // loaded and inserted in class hierarchy (but not linked yet)
</span><span class='line'>    linked,                             // successfully linked/verified (but not initialized yet)
</span><span class='line'>    being_initialized,                  // currently running class initializer
</span><span class='line'>    fully_initialized,                  // initialized (successfull final state)
</span><span class='line'>    initialization_error                // error happened during initialization
</span><span class='line'>  };</span></code></pre></td></tr></table></div></figure>


<p>如果clinit执行失败了，抛了一个未被捕获的异常，那将这个类的状态设置为<code>initialization_error</code>,并且无法再恢复，因为jvm会认为你这次初始化失败了，下次肯定也是失败的，为了防止不断抛这种异常，所以做了一个缓存处理，不是每次都再去执行clinit，因此大家要特别注意，类的初始化过程可千万不能出错，出错就可能只能重启了哦。</p>

<h1>欢迎各位关注个人微信公众号，主要围绕JVM写一系列的原理性，性能调优的文章</h1>

<p><img src="http://nijiaben.github.io/images/gzh.jpg" width="200" height="200"></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[如何定位消耗CPU最多的线程]]></title>
    <link href="http://nijiaben.github.io/blog/2016/03/31/cpu-thread/"/>
    <updated>2016-03-31T18:27:35+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/03/31/cpu-thread</id>
    <content type="html"><![CDATA[<p>之前有朋友反馈说发的内容希望有个梯度，逐步加深，前面发了几篇关于jvm源码分析的文章，可能我觉得我已经把内容写得浅显易懂了，但是对于某些没怎么接触的同学来说还是比较难理解，这个我以后慢慢改进吧，今天发篇轻松点的文章，可能大家在工作过程中也会可能碰到类似的问题，或许有经验的同学看到这个题目就知道我要说什么了，也有自己的定位方法。</p>

<!--more-->


<p>话不多说了，先来看代码吧</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>public class Test{
</span><span class='line'>        public static void main(String args[]){
</span><span class='line'>                for(int i=0;i&lt;10;i++){
</span><span class='line'>                        new Thread(){
</span><span class='line'>                                public void run(){
</span><span class='line'>                                        try{
</span><span class='line'>                                                Thread.sleep(100000);
</span><span class='line'>                                        }catch(Exception e){}
</span><span class='line'>                                }
</span><span class='line'>                        }.start();
</span><span class='line'>                }
</span><span class='line'>                Thread t=new Thread(){
</span><span class='line'>                        public void run(){
</span><span class='line'>                                int i=0;
</span><span class='line'>                                while(true){
</span><span class='line'>                                        i=(i++)/100;
</span><span class='line'>                                }
</span><span class='line'>                        }
</span><span class='line'>                };
</span><span class='line'>                t.setName("Busiest Thread");
</span><span class='line'>                t.start();
</span><span class='line'>        }
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>这个例子里新创建了11个线程，其中10个线程没干什么事，主要是sleep，另外有一个线程在循环里一直跑着，可以想象这个线程是这个进程里最耗cpu的线程了，那怎么把这个线程给抓出来呢？</p>

<p>首先我们可以通过<code>top -Hp &lt;pid&gt;</code>来看这个进程里所有线程的cpu消耗情况，得到类似下面的数据</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ top -Hp 18207
</span><span class='line'>top - 19:11:43 up 573 days,  2:43,  2 users,  load average: 3.03, 3.03, 3.02
</span><span class='line'>Tasks:  44 total,   1 running,  43 sleeping,   0 stopped,   0 zombie
</span><span class='line'>Cpu(s): 18.8%us,  0.0%sy,  0.0%ni, 81.1%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
</span><span class='line'>Mem:  99191752k total, 98683576k used,   508176k free,   128248k buffers
</span><span class='line'>Swap:  1999864k total,   191064k used,  1808800k free, 17413760k cached
</span><span class='line'>
</span><span class='line'>  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
</span><span class='line'>18250 admin     20   0 26.1g  28m  10m R 99.9  0.0   0:19.50 java Test
</span><span class='line'>18207 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18208 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.09 java Test
</span><span class='line'>18209 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18210 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18211 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18212 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18213 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18214 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18215 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18216 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18217 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18218 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18219 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18220 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18221 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18222 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18223 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18224 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18225 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18226 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test
</span><span class='line'>18227 admin     20   0 26.1g  28m  10m S  0.0  0.0   0:00.00 java Test</span></code></pre></td></tr></table></div></figure>


<p>拿到这个结果之后，我们可以看到cpu最高的线程是pid为18250的线程，占了99.9%：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
</span><span class='line'>18250 admin     20   0 26.1g  28m  10m R 99.9  0.0   0:19.50 java Test</span></code></pre></td></tr></table></div></figure>


<p>接着我们可以通过<code>jstack &lt;pid&gt;</code>的输出来看各个线程栈:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ jstack 18207
</span><span class='line'>2016-03-30 19:12:23
</span><span class='line'>Full thread dump OpenJDK 64-Bit Server VM (25.66-b60 mixed mode):
</span><span class='line'>
</span><span class='line'>"Attach Listener" #30 daemon prio=9 os_prio=0 tid=0x00007fb90be13000 nid=0x47d7 waiting on condition [0x0000000000000000]
</span><span class='line'>   java.lang.Thread.State: RUNNABLE
</span><span class='line'>
</span><span class='line'>"DestroyJavaVM" #29 prio=5 os_prio=0 tid=0x00007fb96245b800 nid=0x4720 waiting on condition [0x0000000000000000]
</span><span class='line'>   java.lang.Thread.State: RUNNABLE
</span><span class='line'>
</span><span class='line'>"Busiest Thread" #28 prio=5 os_prio=0 tid=0x00007fb91498d000 nid=0x474a runnable [0x00007fb9065fe000]
</span><span class='line'>   java.lang.Thread.State: RUNNABLE
</span><span class='line'>  at Test$2.run(Test.java:18)
</span><span class='line'>
</span><span class='line'>"Thread-9" #27 prio=5 os_prio=0 tid=0x00007fb91498c800 nid=0x4749 waiting on condition [0x00007fb906bfe000]
</span><span class='line'>   java.lang.Thread.State: TIMED_WAITING (sleeping)
</span><span class='line'>  at java.lang.Thread.sleep(Native Method)
</span><span class='line'>  at Test$1.run(Test.java:9)
</span><span class='line'>
</span><span class='line'>"Thread-8" #26 prio=5 os_prio=0 tid=0x00007fb91498b800 nid=0x4748 waiting on condition [0x00007fb906ffe000]
</span><span class='line'>   java.lang.Thread.State: TIMED_WAITING (sleeping)
</span><span class='line'>  at java.lang.Thread.sleep(Native Method)
</span><span class='line'>  at Test$1.run(Test.java:9)
</span><span class='line'>
</span><span class='line'>"Thread-7" #25 prio=5 os_prio=0 tid=0x00007fb91498b000 nid=0x4747 waiting on condition [0x00007fb9073fe000]
</span><span class='line'>   java.lang.Thread.State: TIMED_WAITING (sleeping)
</span><span class='line'>  at java.lang.Thread.sleep(Native Method)
</span><span class='line'>  at Test$1.run(Test.java:9)
</span><span class='line'>
</span><span class='line'>"Thread-6" #24 prio=5 os_prio=0 tid=0x00007fb91498a000 nid=0x4746 waiting on condition [0x00007fb9077fe000]
</span><span class='line'>   java.lang.Thread.State: TIMED_WAITING (sleeping)
</span><span class='line'>  at java.lang.Thread.sleep(Native Method)
</span><span class='line'>  at Test$1.run(Test.java:9)
</span><span class='line'>...   </span></code></pre></td></tr></table></div></figure>


<p>上面的线程栈我们注意到nid的值其实就是线程ID，它是十六进制的，我们将消耗cpu最高的线程<code>18250</code>，转成十六进制<code>0x474a</code>，然后从上面的线程栈里找到<code>nid=0x474a</code>的线程，其栈为：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>
</span><span class='line'>"Busiest Thread" #28 prio=5 os_prio=0 tid=0x00007fb91498d000 nid=0x474a runnable [0x00007fb9065fe000]
</span><span class='line'>   java.lang.Thread.State: RUNNABLE
</span><span class='line'>  at Test$2.run(Test.java:18)</span></code></pre></td></tr></table></div></figure>


<p>即将最耗cpu的线程找出来了，是<code>Businest Thread</code></p>

<h1>欢迎各位关注个人微信公众号，主要围绕JVM写一系列的原理性，性能调优的文章</h1>

<p><img src="http://nijiaben.github.io/images/gzh.jpg" width="200" height="200"></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之Object.wait/notify(All)完全解读]]></title>
    <link href="http://nijiaben.github.io/blog/2016/03/27/object-wait-notify/"/>
    <updated>2016-03-27T11:31:25+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/03/27/object-wait-notify</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>本文其实一直都想写，因为各种原因一直拖着没写，直到开公众号的第一天，有朋友再次问到这个问题，这次让我静心下来准备写下这篇文章，本文有些东西是我自己的理解，比如为什么JDK一开始要这么设计，初衷是什么，没怎么去找相关资料，所以只能谈谈自己的理解，所以大家看到文章之后可以谈谈自己的看法，对于实现部分我倒觉得说清楚问题不大，code is here，看明白了就知道怎么回事了。</p>

<!--more-->


<p>Object.wait/notify(All)大家都知道主要是协同线程处理的，大家用得也很多，大概逻辑和下面的用法差不多</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>    new Thread(){
</span><span class='line'>        public void run(){
</span><span class='line'>            synchronized (lock){
</span><span class='line'>                try{
</span><span class='line'>                    lock.wait();
</span><span class='line'>                }catch (InterruptedException e){
</span><span class='line'>                    e.printStackTrace();
</span><span class='line'>                }
</span><span class='line'>            }
</span><span class='line'>        }
</span><span class='line'>    }.start();
</span><span class='line'>
</span><span class='line'>    new Thread(){
</span><span class='line'>        public void run(){
</span><span class='line'>            synchronized (lock){
</span><span class='line'>                lock.notify();
</span><span class='line'>            }
</span><span class='line'>        }
</span><span class='line'>    }.start();</span></code></pre></td></tr></table></div></figure>


<p>看到上面代码，你会有什么疑惑吗？至少我会有几个问题会问自己：</p>

<ul>
<li>为什么进入wait和notify的时候要加synchronized锁</li>
<li>既然加了synchronized锁，那当某个线程调用了wait的时候明明还在synchronized块里，其他线程怎么进入到锁里去执行notify的</li>
<li>为什么wait方法可能会抛出InterruptedException异常</li>
<li>如果有多个线程都进入wait状态，那某个线程调用notify唤醒线程时是否按照顺序唤起那些wait线程</li>
<li>wait的线程是在某个线程执行完notify之后立马就被唤起吗</li>
<li>notifyAll又是怎么实现全唤起的</li>
<li>wait的线程是否会影响load</li>
</ul>


<p>如果上面这些问题也都是你想了解的，那这篇文章或许能给你一个答案。</p>

<h2>为何要加synchronized锁</h2>

<p>从实现上来说，这个锁至关重要，正因为这把锁，才能让整个wait/notify玩转起来，当然我觉得其实通过其他的方式也可以实现类似的机制，不过hotspot至少是完全依赖这把锁来实现wait/notify的。</p>

<p>如果要我们来实现这种机制我们会怎么去做，我们知道wait/notify是为了线程间协作而设计的，当我们执行wait的时候让线程挂起，当执行notify的时候唤醒其中一个挂起的线程，那需要有个地方来保存对象和线程之间的映射关系(可以想象一个map，key是对象，value是一个线程列表)，当调用这个对象的wait方法时，将当前线程放到这个线程列表里，当调用这个对象的notify方法时从这个线程列表里取出一个来让其继续执行，这样看来是可行的，也比较简单，那现在的问题这种映射关系放到哪里。而synchronized正好也是为线程间协作而设计的，上面碰到的问题它也要解决，或许正因为这样wait和notify的实现就直接依赖synchronzied(monitorenter/monitorexit是jvm规范里要求要去实现的)来实现了，这只是我的理解，可能初衷不是这个原因，这其实也是这篇文章迟迟未写的一个原因吧，因为我无法取证自己的理解是对的，欢迎各位在这块谈谈自己的见解。</p>

<h2>wait方法执行后未退出同步块，其他线程如何进入同步块</h2>

<p>这个问题其实要回答很简单，因为在wait处理过程中会临时释放同步锁，不过需要注意的是当某个线程调用notify唤起了这个线程的时候，在wait方法退出之前会重新获取这把锁，只有获取了这把锁才会继续执行，想象一下，我们知道wait的方法是被monitorenter和monitorexit包围起来，当我们在执行wait方法过程中如果释放了锁，出来的时候又不拿锁，那在执行到monitorexit指令的时候会发生什么？当然这可以做兼容，不过这实现起来还是很奇怪的。</p>

<h2>为什么wait方法可能抛出InterruptedException异常</h2>

<p>这个异常大家应该都知道，当我们调用了某个线程的interrupt方法时，对应的线程会抛出这个异常，wait方法也不希望破坏这种规则，因此就算当前线程因为wait一直在阻塞，当某个线程希望它起来继续执行的时候，它还是得从阻塞态恢复过来，因此wait方法被唤醒起来的时候会去检测这个状态，当有线程interrupt了它的时候，它就会抛出这个异常从阻塞状态恢复过来。</p>

<p>这里有两点要注意：</p>

<ul>
<li>如果被interrupt的线程只是创建了，并没有start，那等他start之后进入wait态之后也是不能会恢复的</li>
<li>如果被interrupt的线程已经start了，在进入wait之前，如果有线程调用了其interrupt方法，那这个wait等于什么都没做，会直接跳出来，不会阻塞</li>
</ul>


<h2>被notify(All)的线程有规律吗</h2>

<p>这里要分情况：</p>

<ul>
<li>如果是通过notify来唤起的线程，那先进入wait的线程会先被唤起来</li>
<li>如果是通过nootifyAll唤起的线程，默认情况是最后进入的会先被唤起来，即LIFO的策略</li>
</ul>


<h2>notify执行之后立马唤醒线程吗</h2>

<p>其实这个大家可以验证一下，在notify之后写一些逻辑，看这些逻辑是在其他线程被唤起之前还是之后执行，这个是个细节问题，可能大家并没有关注到这个，其实hotspot里真正的实现是退出同步块的时候才会去真正唤醒对应的线程，不过这个也是个默认策略，也可以改的，在notify之后立马唤醒相关线程。</p>

<h2>notifyAll是怎么实现全唤起的</h2>

<p>或许大家立马想到这个简单，一个for循环就搞定了，不过在jvm里没实现这么简单，而是借助了monitorexit，上面我提到了当某个线程从wait状态恢复出来的时候，要先获取锁，然后再退出同步块，所以notifyAll的实现是调用notify的线程在退出其同步块的时候唤醒起最后一个进入wait状态的线程，然后这个线程退出同步块的时候继续唤醒其倒数第二个进入wait状态的线程，依次类推，同样这这是一个策略的问题，jvm里提供了挨个直接唤醒线程的参数，不过都很罕见就不提了。</p>

<h2>wait的线程是否会影响load</h2>

<p>这个或许是大家比较关心的话题，因为关乎系统性能问题，wait/nofity是通过jvm里的park/unpark机制来实现的，在linux下这种机制又是通过pthread_cond_wait/pthread_cond_signal来玩的，因此当线程进入到wait状态的时候其实是会放弃cpu的，也就是说这类线程是不会占用cpu资源。</p>

<h1>欢迎各位关注个人微信公众号，主要围绕JVM写一系列的原理性，性能调优的文章</h1>

<p><img src="http://nijiaben.github.io/images/gzh.jpg" width="200" height="200"></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之自定义类加载器如何拉长YGC]]></title>
    <link href="http://nijiaben.github.io/blog/2016/03/15/ygc-classloader/"/>
    <updated>2016-03-15T13:51:58+08:00</updated>
    <id>http://nijiaben.github.io/blog/2016/03/15/ygc-classloader</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>本文重点讲述毕玄大师在其公众号上发的一个GC问题<a href="http://hellojava.info/?p=438">一个jstack/jmap等不能用的case</a>（PS：话说毕大师超级喜欢在题目里用case这个词，我觉得题目还是能尽量做到顾名思义好，不然要找起相关文章来真的好难找），对于毕大师那篇文章，题目上没有提到GC的那个问题，不过进入到文章里可以看到，既然文章提到了jstack/jmap的问题，这里也简单回答下jstack/jmap无法使用的问题，其实最常见的场景是使用jstack/jmap的用户和目标进程不是同一个用户，哪怕你执行jstack/jmap的动作是root用户也无济于事，详情可以参考我的这篇文章，<a href="http://lovestblog.cn/blog/2014/06/18/jvm-attach/">JVM Attach机制实现</a>,主要是讲JVM Attach机制的，不过毕大师这里主要提到的是jmap -heap/histo这两个参数带来的问题，如果使用-heap/histo的参数，其实和大家使用-F参数是一样的，底层都是通过serviceability agent来实现的，并不是jvm attach的方式，通过sa连上去之后会挂起进程，在serviceability agent里存在bug可能导致detach的动作不会被执行，从而会让进程一直挂着，可以通过top命令验证进程是否处于T状态，如果是说明进程被挂起了，如果进程被挂起了，可以通过kill -CONT [pid]来恢复。</p>

<!--more-->


<p>再回到那个GC的问题，用的参数如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xms512m -Xmx512m -Xmn100m -XX:+UseConcMarkSweepGC</span></code></pre></td></tr></table></div></figure>


<p>demo程序如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>import com.thoughtworks.xstream.XStream;
</span><span class='line'> 
</span><span class='line'>public class XStreamTest {
</span><span class='line'>     
</span><span class='line'>    public static void main(String[] args) throws Exception {
</span><span class='line'>        while(true){
</span><span class='line'>            XStream xs = new XStream();
</span><span class='line'>            xs.toString();
</span><span class='line'>            xs = null;
</span><span class='line'>        }
</span><span class='line'>    }
</span><span class='line'> 
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>执行效果如下</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>2016-03-14T22:48:01.502+0800: [GC [ParNew: 327680K-&gt;4258K(368640K), 0.0179071 secs] 327680K-&gt;4258K(1007616K), 0.0179448 secs] [Times: user=0.06 sys=0.01, real=0.01 secs] 
</span><span class='line'>2016-03-14T22:48:05.975+0800: [GC [ParNew: 331938K-&gt;10239K(368640K), 0.0336279 secs] 331938K-&gt;10239K(1007616K), 0.0336593 secs] [Times: user=0.13 sys=0.02, real=0.03 secs] 
</span><span class='line'>2016-03-14T22:48:12.215+0800: [GC [ParNew: 337919K-&gt;14444K(368640K), 0.0471005 secs] 337919K-&gt;14444K(1007616K), 0.0471257 secs] [Times: user=0.19 sys=0.02, real=0.05 secs] 
</span><span class='line'>2016-03-14T22:48:21.768+0800: [GC [ParNew: 342124K-&gt;19088K(368640K), 0.0605017 secs] 342124K-&gt;19088K(1007616K), 0.0605295 secs] [Times: user=0.26 sys=0.03, real=0.06 secs] 
</span><span class='line'>2016-03-14T22:48:35.180+0800: [GC [ParNew: 346768K-&gt;20633K(368640K), 0.0993470 secs] 346768K-&gt;25248K(1007616K), 0.0993777 secs] [Times: user=0.34 sys=0.04, real=0.09 secs]</span></code></pre></td></tr></table></div></figure>


<p>发现gc的时间越来越长，但是gc触发的时机以及回收的效果都差不多，那问题究竟在哪里呢？</p>

<h2>Demo分析</h2>

<p>虽然这个demo代码逻辑很简单，但是其实这是一个特殊的demo，并不简单，如果我们将XStream对象换成Object对象，会发现不存在这个问题，既然如此那有必要进去看看这个XStream的构造函数：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> public XStream() {
</span><span class='line'>        this((ReflectionProvider)null, (Mapper)((Mapper)null), (HierarchicalStreamDriver)(new XppDriver()));
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    /** @deprecated */
</span><span class='line'>    public XStream(ReflectionProvider reflectionProvider, Mapper mapper, HierarchicalStreamDriver driver) {
</span><span class='line'>        this(reflectionProvider, driver, (ClassLoader)(new CompositeClassLoader()), mapper);
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    /** @deprecated */
</span><span class='line'>    public XStream(ReflectionProvider reflectionProvider, HierarchicalStreamDriver driver, ClassLoader classLoader, Mapper mapper) {
</span><span class='line'>        this(reflectionProvider, driver, new ClassLoaderReference(classLoader), mapper, new DefaultConverterLookup());
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    public XStream(ReflectionProvider reflectionProvider, HierarchicalStreamDriver driver, ClassLoaderReference classLoader, Mapper mapper, final DefaultConverterLookup defaultConverterLookup) {
</span><span class='line'>        this(reflectionProvider, driver, (ClassLoaderReference)classLoader, mapper, new ConverterLookup() {
</span><span class='line'>            public Converter lookupConverterForType(Class type) {
</span><span class='line'>                return defaultConverterLookup.lookupConverterForType(type);
</span><span class='line'>            }
</span><span class='line'>        }, new ConverterRegistry() {
</span><span class='line'>            public void registerConverter(Converter converter, int priority) {
</span><span class='line'>                defaultConverterLookup.registerConverter(converter, priority);
</span><span class='line'>            }
</span><span class='line'>        });
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    /** @deprecated */
</span><span class='line'>    public XStream(ReflectionProvider reflectionProvider, HierarchicalStreamDriver driver, ClassLoader classLoader, Mapper mapper, ConverterLookup converterLookup, ConverterRegistry converterRegistry) {
</span><span class='line'>        this(reflectionProvider, driver, (ClassLoaderReference)(new ClassLoaderReference(classLoader)), mapper, converterLookup, converterRegistry);
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    public XStream(ReflectionProvider reflectionProvider, HierarchicalStreamDriver driver, ClassLoaderReference classLoaderReference, Mapper mapper, ConverterLookup converterLookup, ConverterRegistry converterRegistry) {
</span><span class='line'>        if(reflectionProvider == null) {
</span><span class='line'>            reflectionProvider = JVM.newReflectionProvider();
</span><span class='line'>        }
</span><span class='line'>
</span><span class='line'>        this.reflectionProvider = reflectionProvider;
</span><span class='line'>        this.hierarchicalStreamDriver = driver;
</span><span class='line'>        this.classLoaderReference = classLoaderReference;
</span><span class='line'>        this.converterLookup = converterLookup;
</span><span class='line'>        this.converterRegistry = converterRegistry;
</span><span class='line'>        this.mapper = mapper == null?this.buildMapper():mapper;
</span><span class='line'>        this.setupMappers();
</span><span class='line'>        this.setupSecurity();
</span><span class='line'>        this.setupAliases();
</span><span class='line'>        this.setupDefaultImplementations();
</span><span class='line'>        this.setupConverters();
</span><span class='line'>        this.setupImmutableTypes();
</span><span class='line'>        this.setMode(1003);
</span><span class='line'>    }</span></code></pre></td></tr></table></div></figure>


<p>这个构造函数还是很复杂的，里面会创建很多的对象，上面还有一些方法实现我就不贴了，总之都是在不断构建各种大大小小的对象，一个XStream对象构建出来的时候大概好像有12M的样子。</p>

<p>那到底是哪些对象会导致ygc不断增长呢，于是可能想到逐步替换上面这些逻辑，比如将最后一个构造函数里的那些逻辑都禁掉，然后我们再跑测试看看还会不会让ygc不断恶化，最终我们会发现，如果我们直接使用如下构造函数构造对象时，如果传入的classloader是AppClassLoader，那会发现这个问题不再出现了。</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> public XStream(ReflectionProvider reflectionProvider, HierarchicalStreamDriver driver, ClassLoader classLoader, Mapper mapper) {
</span><span class='line'>        this(reflectionProvider, driver, new ClassLoaderReference(classLoader), mapper, new DefaultConverterLookup());
</span><span class='line'> }</span></code></pre></td></tr></table></div></figure>


<p>测试代码如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> public static void main(String[] args) throws Exception {
</span><span class='line'>        int i=0;
</span><span class='line'>        while (true) {
</span><span class='line'>            XStream xs = new XStream(null,null, new ClassLoaderReference(XStreamTest.class.getClassLoader()),null, new DefaultConverterLookup());
</span><span class='line'>            xs.toString();
</span><span class='line'>            xs=null;
</span><span class='line'>        }
</span><span class='line'>  }</span></code></pre></td></tr></table></div></figure>


<p>gc日志如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>2016-03-14T23:10:33.537+0800: [GC [ParNew: 327680K-&gt;758K(368640K), 0.0019803 secs] 327680K-&gt;758K(1007616K), 0.0020182 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
</span><span class='line'>2016-03-14T23:10:35.189+0800: [GC [ParNew: 328438K-&gt;1066K(368640K), 0.0018641 secs] 328438K-&gt;1066K(1007616K), 0.0019055 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
</span><span class='line'>2016-03-14T23:10:36.465+0800: [GC [ParNew: 328746K-&gt;1156K(368640K), 0.0010304 secs] 328746K-&gt;1156K(1007616K), 0.0010519 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
</span><span class='line'>2016-03-14T23:10:37.767+0800: [GC [ParNew: 328836K-&gt;1065K(368640K), 0.0011329 secs] 328836K-&gt;1065K(1007616K), 0.0011543 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
</span><span class='line'>2016-03-14T23:10:39.035+0800: [GC [ParNew: 328745K-&gt;351K(368640K), 0.0043387 secs] 328745K-&gt;1127K(1007616K), 0.0043700 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
</span><span class='line'>2016-03-14T23:10:40.324+0800: [GC [ParNew: 328031K-&gt;160K(368640K), 0.0011579 secs] 328807K-&gt;936K(1007616K), 0.0011793 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
</span><span class='line'>2016-03-14T23:10:41.610+0800: [GC [ParNew: 327840K-&gt;31K(368640K), 0.0007010 secs] 328616K-&gt;826K(1007616K), 0.0007219 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
</span><span class='line'>2016-03-14T23:10:42.919+0800: [GC [ParNew: 327711K-&gt;24K(368640K), 0.0011246 secs] 328506K-&gt;819K(1007616K), 0.0011450 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
</span><span class='line'>2016-03-14T23:10:44.196+0800: [GC [ParNew: 327704K-&gt;24K(368640K), 0.0006797 secs] 328499K-&gt;819K(1007616K), 0.0007586 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] </span></code></pre></td></tr></table></div></figure>


<p>是不是觉得很神奇，由此可见，这个classloader至关重要。</p>

<h2>不得不说的类加载器</h2>

<p>这里着重要说的两个概念是<code>初始类加载器</code>和<code>定义类加载器</code>。举个栗子说吧，AClassLoader->BClassLoader->CClassLoader，表示AClassLoader在加载类的时候会委托BClassLoader类加载器来加载，BClassLoader加载类的时候会委托CClassLoader来加载，假如我们使用AClassLoader来加载X这个类，而X这个类最终是被CClassLoader来加载的，那么我们称CClassLoader为X类的定义类加载器，而AClassLoader为X类的初始类加载器，JVM在加载某个类的时候对AClassLoader和CClassLoader进行记录，记录的数据结构是一个叫做SystemDictionary的hashtable，其key是根据ClassLoader对象和类名算出来的hash值（其实是一个entry，可以根据这个hash值找到具体的index位置，然后构建一个包含kalssName和classloader对象的entry放到map里），而value是真正的由定义类加载器加载的Klass对象，因为初始类加载器和定义类加载器是不同的classloader，因此算出来的hash值也是不同的，因此在SystemDictionary里会有多项值的value都是指向同一个Klass对象。</p>

<p>那么JVM为什么要分这两种类加载器呢，其实主要是为了快速找到已经加载的类，比如我们已经通过AClassLoader来触发了对X类的加载，当我们再次使用AClassLoader这个类加载器来加载X这个类的时候就不需要再委托给BClassLoader去找了，因为加载过的类在JVM里有这个类加载器的直接加载的记录，只需要直接返回对应的Klass对象即可。</p>

<h2>Demo中的类加载器是否会加载类</h2>

<p>我们的demo里发现构建了一个CompositeClassLoader的类加载器，那到底有没有用这个类加载器加载类呢，我们可以设置一个断点在CompositeClassLoader的loadClass方法上，于是看到下面的堆栈：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>main@1, prio=5, in group 'main', status: 'RUNNING'
</span><span class='line'>    at com.thoughtworks.xstream.core.util.CompositeClassLoader.loadClass(CompositeClassLoader.java:53)
</span><span class='line'>    at java.lang.Class.forName0(Class.java:-1)
</span><span class='line'>    at java.lang.Class.forName(Class.java:249)
</span><span class='line'>    at com.thoughtworks.xstream.XStream.buildMapperDynamically(XStream.java:191)
</span><span class='line'>    at com.thoughtworks.xstream.XStream.buildMapper(XStream.java:170)
</span><span class='line'>    at com.thoughtworks.xstream.XStream.&lt;init&gt;(XStream.java:142)
</span><span class='line'>    at com.thoughtworks.xstream.XStream.&lt;init&gt;(XStream.java:116)
</span><span class='line'>    at com.BBBB.main(BBBB.java:15)</span></code></pre></td></tr></table></div></figure>


<p>可见确实有类加载的动作，根据类加载委托机制，在这个demo中我们能肯定类是交给AppClassLoader来加载的，这样一来CompositeClassLoader就变成了初始类加载器，而AppClassLoader会是定义类加载器，都会在SystemDictionary里存在，因此当我们不断new XStream的时候会不断new CompositeClassLoader对象，加载类的时候会不断往SystemDictionary里插入记录，从而使SystemDictionary越来越膨胀，那自然而然会想到如果GC过程不断去扫描这个SystemDictionary的话，那随着SystemDictionary不断膨胀，那么GC的效率也就越低，抱着验证下猜想的方式我们可以使用perf工具来看看，如果发现cpu占比排前的函数如果都是操作SystemDictionary的，那就基本验证了我们的说法，下面是perf工具的截图，基本证实了这一点。</p>

<p><img src="http://nijiaben.github.io/images/2016/03/ygc_classloader_perf.png"></p>

<h2>SystemDictionary为什么会影响GC过程</h2>

<p>想象一下这么个情况，我们加载了一个类，然后构建了一个对象(这个对象在eden里构建)当一个属性设置到这个类里，如果gc发生的时候，这个对象是不是要被找出来标活才行，那么自然而然我们加载的类肯定是我们一项重要的gc root，这样SystemDictionary就成为了gc过程中的被扫描对象了，事实也是如此，可以看vm的具体代码：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void SharedHeap::process_strong_roots(bool activate_scope,
</span><span class='line'>                                      bool collecting_perm_gen,
</span><span class='line'>                                      ScanningOption so,
</span><span class='line'>                                      OopClosure* roots,
</span><span class='line'>                                      CodeBlobClosure* code_roots,
</span><span class='line'>                                      OopsInGenClosure* perm_blk) {
</span><span class='line'>  StrongRootsScope srs(this, activate_scope);
</span><span class='line'>  // General strong roots.
</span><span class='line'>  assert(_strong_roots_parity != 0, "must have called prologue code");
</span><span class='line'>  // _n_termination for _process_strong_tasks should be set up stream
</span><span class='line'>  // in a method not running in a GC worker.  Otherwise the GC worker
</span><span class='line'>  // could be trying to change the termination condition while the task
</span><span class='line'>  // is executing in another GC worker.
</span><span class='line'>  if (!_process_strong_tasks-&gt;is_task_claimed(SH_PS_Universe_oops_do)) {
</span><span class='line'>    Universe::oops_do(roots);
</span><span class='line'>    // Consider perm-gen discovered lists to be strong.
</span><span class='line'>    //将perm gen的非强引用标记为root的一部分
</span><span class='line'>    perm_gen()-&gt;ref_processor()-&gt;weak_oops_do(roots);
</span><span class='line'>  }
</span><span class='line'>  // Global (strong) JNI handles
</span><span class='line'>  if (!_process_strong_tasks-&gt;is_task_claimed(SH_PS_JNIHandles_oops_do))
</span><span class='line'>    JNIHandles::oops_do(roots);
</span><span class='line'>  // All threads execute this; the individual threads are task groups.
</span><span class='line'>  if (ParallelGCThreads &gt; 0) {
</span><span class='line'>    Threads::possibly_parallel_oops_do(roots, code_roots);
</span><span class='line'>  } else {
</span><span class='line'>    Threads::oops_do(roots, code_roots);
</span><span class='line'>  }
</span><span class='line'>  if (!_process_strong_tasks-&gt; is_task_claimed(SH_PS_ObjectSynchronizer_oops_do))
</span><span class='line'>    ObjectSynchronizer::oops_do(roots);
</span><span class='line'>  if (!_process_strong_tasks-&gt;is_task_claimed(SH_PS_FlatProfiler_oops_do))
</span><span class='line'>    FlatProfiler::oops_do(roots);
</span><span class='line'>  if (!_process_strong_tasks-&gt;is_task_claimed(SH_PS_Management_oops_do))
</span><span class='line'>    Management::oops_do(roots);
</span><span class='line'>  if (!_process_strong_tasks-&gt;is_task_claimed(SH_PS_jvmti_oops_do))
</span><span class='line'>    JvmtiExport::oops_do(roots);
</span><span class='line'>
</span><span class='line'>  if (!_process_strong_tasks-&gt;is_task_claimed(SH_PS_SystemDictionary_oops_do)) {
</span><span class='line'>    if (so & SO_AllClasses) {
</span><span class='line'>      SystemDictionary::oops_do(roots);
</span><span class='line'>    } else if (so & SO_SystemClasses) {
</span><span class='line'>      SystemDictionary::always_strong_oops_do(roots);
</span><span class='line'>    }
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>  if (!_process_strong_tasks-&gt;is_task_claimed(SH_PS_StringTable_oops_do)) {
</span><span class='line'>    //JavaObjectsInPerm为false，那么String intern的对象已经class对象都是存在heap里的，否则都存在perm里  
</span><span class='line'>    if (so & SO_Strings || (!collecting_perm_gen && !JavaObjectsInPerm)) {
</span><span class='line'>      //虽然不回收perm，但是interned的String对象不在perm里，那么还是需要遍历下StringTable里的String对象，因为这些对象在heap里
</span><span class='line'>      StringTable::oops_do(roots);
</span><span class='line'>    }
</span><span class='line'>    if (JavaObjectsInPerm) {
</span><span class='line'>      // Verify the string table contents are in the perm gen
</span><span class='line'>      NOT_PRODUCT(StringTable::oops_do(&assert_is_perm_closure));
</span><span class='line'>    }
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>  if (!_process_strong_tasks-&gt;is_task_claimed(SH_PS_CodeCache_oops_do)) {
</span><span class='line'>    if (so & SO_CodeCache) {
</span><span class='line'>      // (Currently, CMSCollector uses this to do intermediate-strength collections.)
</span><span class='line'>      assert(collecting_perm_gen, "scanning all of code cache");
</span><span class='line'>      assert(code_roots != NULL, "must supply closure for code cache");
</span><span class='line'>      if (code_roots != NULL) {
</span><span class='line'>        CodeCache::blobs_do(code_roots);
</span><span class='line'>      }
</span><span class='line'>    } else if (so & (SO_SystemClasses|SO_AllClasses)) {
</span><span class='line'>      if (!collecting_perm_gen) {
</span><span class='line'>        // If we are collecting from class statics, but we are not going to
</span><span class='line'>        // visit all of the CodeCache, collect from the non-perm roots if any.
</span><span class='line'>        // This makes the code cache function temporarily as a source of strong
</span><span class='line'>        // roots for oops, until the next major collection.
</span><span class='line'>        //
</span><span class='line'>        // If collecting_perm_gen is true, we require that this phase will call
</span><span class='line'>        // CodeCache::do_unloading.  This will kill off nmethods with expired
</span><span class='line'>        // weak references, such as stale invokedynamic targets.
</span><span class='line'>        CodeCache::scavenge_root_nmethods_do(code_roots);
</span><span class='line'>      }
</span><span class='line'>    }
</span><span class='line'>    // Verify that the code cache contents are not subject to
</span><span class='line'>    // movement by a scavenging collection.
</span><span class='line'>    DEBUG_ONLY(CodeBlobToOopClosure assert_code_is_non_scavengable(&assert_is_non_scavengable_closure, /*do_marking=*/ false));
</span><span class='line'>    DEBUG_ONLY(CodeCache::asserted_non_scavengable_nmethods_do(&assert_code_is_non_scavengable));
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>  if (!collecting_perm_gen) {
</span><span class='line'>    //如果是不回收perm，那找出所有perm指向new的对象  
</span><span class='line'>    // All threads perform this; coordination is handled internally.
</span><span class='line'>    rem_set()-&gt;younger_refs_iterate(perm_gen(), perm_blk);//perm的level是-1
</span><span class='line'>  }
</span><span class='line'>  _process_strong_tasks-&gt;all_tasks_completed();
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>看上面的<code>SH_PS_SystemDictionary_oops_do</code> task就知道了，这个就是对SystemDictionary进行扫描。</p>

<p>但是这里要说的是虽然有对SystemDictionary进行扫描，但是ygc的过程并不会对SystemDictionary进行处理，如果要对它进行处理需要开启类卸载的vm参数，CMS算法下，CMS GC和Full GC在开启CMSClassUnloadingEnabled的情况下是可能对类做卸载动作的，此时会对SystemDictionary进行清理，所以当我们在跑上面demo的时候，通过<code>jmap -dump:live,format=b,file=heap.bin &lt;pid&gt;</code>命令执行完之后，ygc的时间瞬间降下来了，不过又会慢慢回去，这是因为jmap的这个命令会做一次gc，这个gc过程会对SystemDictionary进行清理。</p>

<h2>修改VM代码验证</h2>

<p>很遗憾hotspot目前没有对ygc的每个task做一个时间的统计，因此无法直接知道是不是<code>SH_PS_SystemDictionary_oops_do</code>这个task导致了ygc的时间变长，为了证明这个结论，我特地修改了一下代码，在上面的代码上加了一行：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>if (!_process_strong_tasks-&gt;is_task_claimed(SH_PS_SystemDictionary_oops_do)) {
</span><span class='line'>  GCTraceTime t("SystemDictionary_OOPS_DO",PrintGCDetails,true,NULL);
</span><span class='line'>    if (so & SO_AllClasses) {
</span><span class='line'>      SystemDictionary::oops_do(roots);
</span><span class='line'>    } else if (so & SO_SystemClasses) {
</span><span class='line'>      SystemDictionary::always_strong_oops_do(roots);
</span><span class='line'>    }
</span><span class='line'>  }</span></code></pre></td></tr></table></div></figure>


<p>然后重新编译，跑我们的demo，测试结果如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>2016-03-14T23:57:24.293+0800: [GC2016-03-14T23:57:24.294+0800: [ParNew2016-03-14T23:57:24.296+0800: [SystemDictionary_OOPS_DO, 0.0578430 secs]
</span><span class='line'>: 81920K-&gt;3184K(92160K), 0.0889740 secs] 81920K-&gt;3184K(514048K), 0.0900970 secs] [Times: user=0.27 sys=0.00, real=0.09 secs]
</span><span class='line'>2016-03-14T23:57:28.467+0800: [GC2016-03-14T23:57:28.468+0800: [ParNew2016-03-14T23:57:28.468+0800: [SystemDictionary_OOPS_DO, 0.0779210 secs]
</span><span class='line'>: 85104K-&gt;5175K(92160K), 0.1071520 secs] 85104K-&gt;5175K(514048K), 0.1080490 secs] [Times: user=0.65 sys=0.00, real=0.11 secs]
</span><span class='line'>2016-03-14T23:57:32.984+0800: [GC2016-03-14T23:57:32.984+0800: [ParNew2016-03-14T23:57:32.984+0800: [SystemDictionary_OOPS_DO, 0.1075680 secs]
</span><span class='line'>: 87095K-&gt;8188K(92160K), 0.1434270 secs] 87095K-&gt;8188K(514048K), 0.1439870 secs] [Times: user=0.90 sys=0.01, real=0.14 secs]
</span><span class='line'>2016-03-14T23:57:37.900+0800: [GC2016-03-14T23:57:37.900+0800: [ParNew2016-03-14T23:57:37.901+0800: [SystemDictionary_OOPS_DO, 0.1745390 secs]
</span><span class='line'>: 90108K-&gt;7093K(92160K), 0.2876260 secs] 90108K-&gt;9992K(514048K), 0.2884150 secs] [Times: user=1.44 sys=0.02, real=0.29 secs]</span></code></pre></td></tr></table></div></figure>


<p>我们会发现YGC的时间变长的时候，SystemDictionary_OOPS_DO的时间也会相应变长多少，因此验证了我们的说法。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[消失的死锁]]></title>
    <link href="http://nijiaben.github.io/blog/2015/10/21/deadlock/"/>
    <updated>2015-10-21T18:54:01+08:00</updated>
    <id>http://nijiaben.github.io/blog/2015/10/21/deadlock</id>
    <content type="html"><![CDATA[<h2>问题描述</h2>

<p>如果java层面发生了死锁，当我们使用<code>jstack</code>命令的时候其实是可以将死锁的信息给dump出来的，在dump结果的最后会有类似<code>Found one Java-level deadlock:</code>的关键字，接着会把发生死锁的线程的堆栈及对应的同步锁给打印出来，这次碰到一个系统就发生类似的问题，不过这个dump文档里虽然提到了如下的死锁信息：</p>

<!--more-->




<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Found one Java-level deadlock:
</span><span class='line'>=============================
</span><span class='line'>"worker-1-thread-121":
</span><span class='line'>  waiting to lock monitor 0x00007f3758209dc8 (object 0x0000000764cd2b20, a java.util.concurrent.ConcurrentHashMap),
</span><span class='line'>  which is held by "HSFBizProcessor-4-thread-4"
</span><span class='line'>"HSFBizProcessor-4-thread-4":
</span><span class='line'>  waiting to lock monitor 0x00007f3758289260 (object 0x000000076073ddc8, a com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader),
</span><span class='line'>  which is held by "HSFBizProcessor-4-thread-5"
</span><span class='line'>"HSFBizProcessor-4-thread-5":
</span><span class='line'>  waiting to lock monitor 0x00007f3758253420 (object 0x00000007608e6fc8, a com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader),
</span><span class='line'>  which is held by "HSFBizProcessor-4-thread-4"</span></code></pre></td></tr></table></div></figure>


<p>但是我们在堆栈里搜索对应的锁的时候并没发现，也就是上面提到的</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>object 0x00000007608e6fc8 which is held by "HSFBizProcessor-4-thread-4"</span></code></pre></td></tr></table></div></figure>


<p>我们在<code>HSFBizProcessor-4-thread-4</code>这个线程的堆栈里并没有看到对应的持锁信息。</p>

<p>附上线程dump详情</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Found one Java-level deadlock:
</span><span class='line'>=============================
</span><span class='line'>"worker-1-thread-121":
</span><span class='line'>  waiting to lock monitor 0x00007f3758209dc8 (object 0x0000000764cd2b20, a java.util.concurrent.ConcurrentHashMap),
</span><span class='line'>  which is held by "HSFBizProcessor-4-thread-4"
</span><span class='line'>"HSFBizProcessor-4-thread-4":
</span><span class='line'>  waiting to lock monitor 0x00007f3758289260 (object 0x000000076073ddc8, a com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader),
</span><span class='line'>  which is held by "HSFBizProcessor-4-thread-5"
</span><span class='line'>"HSFBizProcessor-4-thread-5":
</span><span class='line'>  waiting to lock monitor 0x00007f3758253420 (object 0x00000007608e6fc8, a com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader),
</span><span class='line'>  which is held by "HSFBizProcessor-4-thread-4"
</span><span class='line'>
</span><span class='line'>Java stack information for the threads listed above:
</span><span class='line'>===================================================
</span><span class='line'>"worker-1-thread-121":
</span><span class='line'>  at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:180)
</span><span class='line'>  - waiting to lock &lt;0x0000000764cd2b20&gt; (a java.util.concurrent.ConcurrentHashMap)
</span><span class='line'>  at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:455)
</span><span class='line'>  at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:317)
</span><span class='line'>  ......
</span><span class='line'>  at java.util.concurrent.FutureTask.run(FutureTask.java:138)
</span><span class='line'>  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
</span><span class='line'>  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
</span><span class='line'>  at java.lang.Thread.run(Thread.java:662)
</span><span class='line'>"HSFBizProcessor-4-thread-4":
</span><span class='line'>  at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLoadedClass(Unknown Source)
</span><span class='line'>  - waiting to lock &lt;0x000000076073ddc8&gt; (a com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader)
</span><span class='line'>  at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.SingleSourcePackage.loadClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(Unknown Source)
</span><span class='line'>  at com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader.loadClass(KernelBundleClassLoader.java:121)
</span><span class='line'>  at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
</span><span class='line'>  at org.springframework.scripting.groovy.GroovyScriptFactory.executeScript(GroovyScriptFactory.java:238)
</span><span class='line'>  ......
</span><span class='line'>  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
</span><span class='line'>  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
</span><span class='line'>  at java.lang.Thread.run(Thread.java:662)
</span><span class='line'>"HSFBizProcessor-4-thread-5":
</span><span class='line'>  at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLoadedClass(Unknown Source)
</span><span class='line'>  - waiting to lock &lt;0x00000007608e6fc8&gt; (a com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader)
</span><span class='line'>  at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.buddy.DependentPolicy.loadClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.buddy.PolicyHandler.doBuddyClassLoading(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(Unknown Source)
</span><span class='line'>  at com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader.loadClass(KernelBundleClassLoader.java:121)
</span><span class='line'>  at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
</span><span class='line'>  at java.lang.Class.forName0(Native Method)
</span><span class='line'>  at java.lang.Class.forName(Class.java:169)
</span><span class='line'>  at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:127)
</span><span class='line'>  at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122)
</span><span class='line'>    ......
</span><span class='line'>  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
</span><span class='line'>  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
</span><span class='line'>  at java.lang.Thread.run(Thread.java:662)
</span><span class='line'>
</span><span class='line'>Found 1 deadlock.</span></code></pre></td></tr></table></div></figure>


<h2>类加载的问题？</h2>

<p>首先应该怀疑类加载的问题，因为我们看到导致死锁的对象是一个classloader对象：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>waiting to lock monitor 0x00007f3758289260 (object 0x000000076073ddc8, a com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader)</span></code></pre></td></tr></table></div></figure>


<p>然后我们再来分析下堆栈</p>

<h3>HSFBizProcessor-4-thread-4</h3>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>"HSFBizProcessor-4-thread-4":
</span><span class='line'>  at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLoadedClass(Unknown Source)
</span><span class='line'>  - waiting to lock &lt;0x000000076073ddc8&gt; (a com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader)
</span><span class='line'>  at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.SingleSourcePackage.loadClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(Unknown Source)
</span><span class='line'>  at com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader.loadClass(KernelBundleClassLoader.java:121)
</span><span class='line'>  at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
</span><span class='line'>  at org.springframework.scripting.groovy.GroovyScriptFactory.executeScript(GroovyScriptFactory.java:238)
</span><span class='line'>  at org.springframework.scripting.groovy.GroovyScriptFactory.getScriptedObject(GroovyScriptFactory.java:185)</span></code></pre></td></tr></table></div></figure>


<p>我这里只把关键的线程栈贴出来，从栈顶知道正在等一把锁：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>- waiting to lock &lt;0x000000076073ddc8&gt; (a com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader)
</span></code></pre></td></tr></table></div></figure>


<p>这把锁的对象是一个ClassLoader对象，我们找到对应的代码，确实存在synchronized的操作：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>private Class&lt;?&gt; findLoadedClass(String classname) {
</span><span class='line'>    if ((LOCK_CLASSNAME) || (this.isParallelClassLoader)) {
</span><span class='line'>      boolean initialLock = lockClassName(classname);
</span><span class='line'>      try {
</span><span class='line'>        return this.classloader.publicFindLoaded(classname);
</span><span class='line'>      } finally {
</span><span class='line'>        if (initialLock)
</span><span class='line'>          unlockClassName(classname);
</span><span class='line'>      }
</span><span class='line'>    }
</span><span class='line'>    synchronized (this.classloader) {
</span><span class='line'>      return this.classloader.publicFindLoaded(classname);
</span><span class='line'>    }
</span><span class='line'>  }
</span></code></pre></td></tr></table></div></figure>


<p>另外我们还知道它正在执行loadClass的动作，并且是从groovy调用来的，同样找到对应的代码：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>protected Object executeScript(ScriptSource scriptSource, Class scriptClass)
</span><span class='line'>    throws ScriptCompilationException
</span><span class='line'>  {
</span><span class='line'>    try
</span><span class='line'>    {
</span><span class='line'>      GroovyObject goo = (GroovyObject)scriptClass.newInstance();//line 238
</span><span class='line'>
</span><span class='line'>      if (this.groovyObjectCustomizer != null)
</span><span class='line'>      {
</span><span class='line'>        this.groovyObjectCustomizer.customize(goo);
</span><span class='line'>      }
</span><span class='line'>
</span><span class='line'>      if ((goo instanceof Script))
</span><span class='line'>      {
</span><span class='line'>        return ((Script)goo).run();
</span><span class='line'>      }
</span><span class='line'>
</span><span class='line'>      return goo;
</span><span class='line'>    }
</span><span class='line'>    catch (InstantiationException ex)
</span><span class='line'>    {
</span><span class='line'>      throw new ScriptCompilationException(
</span><span class='line'>        scriptSource, "Could not instantiate Groovy script class: " + scriptClass.getName(), ex);
</span><span class='line'>    }
</span><span class='line'>    catch (IllegalAccessException ex) {
</span><span class='line'>      throw new ScriptCompilationException(
</span><span class='line'>        scriptSource, "Could not access Groovy script constructor: " + scriptClass.getName(), ex);
</span><span class='line'>    }
</span><span class='line'>  }</span></code></pre></td></tr></table></div></figure>


<p>执行到第238行的时候</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>GroovyObject goo = (GroovyObject)scriptClass.newInstance();//line 238</span></code></pre></td></tr></table></div></figure>


<p>突然发现调用了</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>java.lang.ClassLoader.loadClass(ClassLoader.java:247)</span></code></pre></td></tr></table></div></figure>


<p>而我们看到上面第238行的逻辑其实就是实例化一个对象，然后进行强转，我们看看对应的字节码：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> 0: aload_2
</span><span class='line'> 1: invokevirtual #164                // Method java/lang/Class.newInstance:()Ljava/lang/Object;
</span><span class='line'> 4: checkcast     #168                // class groovy/lang/GroovyObject
</span><span class='line'> 7: astore_3</span></code></pre></td></tr></table></div></figure>


<p>其实就对应这么几条字节码指令，其实在jvm里当我们执行checkcast指令的时候会触发类加载的动作：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void TemplateTable::checkcast() {
</span><span class='line'>  ...
</span><span class='line'>  call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc));
</span><span class='line'>  ...
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>IRT_ENTRY(void, InterpreterRuntime::quicken_io_cc(JavaThread* thread))
</span><span class='line'>  // Force resolving; quicken the bytecode
</span><span class='line'>  int which = get_index_u2(thread, Bytecodes::_checkcast);
</span><span class='line'>  constantPoolOop cpool = method(thread)-&gt;constants();
</span><span class='line'>  // We'd expect to assert that we're only here to quicken bytecodes, but in a multithreaded
</span><span class='line'>  // program we might have seen an unquick'd bytecode in the interpreter but have another
</span><span class='line'>  // thread quicken the bytecode before we get here.
</span><span class='line'>  // assert( cpool-&gt;tag_at(which).is_unresolved_klass(), "should only come here to quicken bytecodes" );
</span><span class='line'>  klassOop klass = cpool-&gt;klass_at(which, CHECK);
</span><span class='line'>  thread-&gt;set_vm_result(klass);
</span><span class='line'>IRT_END
</span><span class='line'>
</span><span class='line'>klassOop klass_at(int which, TRAPS) {
</span><span class='line'>    constantPoolHandle h_this(THREAD, this);
</span><span class='line'>    return klass_at_impl(h_this, which, CHECK_NULL);
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>klassOop constantPoolOopDesc::klass_at_impl(constantPoolHandle this_oop, int which, TRAPS) {
</span><span class='line'>  ...
</span><span class='line'>    klassOop k_oop = SystemDictionary::resolve_or_fail(name, loader, h_prot, true, THREAD);
</span><span class='line'>  ...
</span><span class='line'>} 
</span><span class='line'>
</span><span class='line'>//SystemDictionary::resolve_or_fail最终会调用到下面这个方法
</span><span class='line'>klassOop SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle class_loader, Handle protection_domain, TRAPS) {
</span><span class='line'>  ...
</span><span class='line'>  // Class is not in SystemDictionary so we have to do loading.
</span><span class='line'>  // Make sure we are synchronized on the class loader before we proceed
</span><span class='line'>  Handle lockObject = compute_loader_lock_object(class_loader, THREAD);
</span><span class='line'>  check_loader_lock_contention(lockObject, THREAD);
</span><span class='line'>  ObjectLocker ol(lockObject, THREAD, DoObjectLock);
</span><span class='line'>  ...
</span><span class='line'>  //此时会调用ClassLoader.loadClass来加载类了
</span><span class='line'>  ...
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>Handle SystemDictionary::compute_loader_lock_object(Handle class_loader, TRAPS) {
</span><span class='line'>  // If class_loader is NULL we synchronize on _system_loader_lock_obj
</span><span class='line'>  if (class_loader.is_null()) {
</span><span class='line'>    return Handle(THREAD, _system_loader_lock_obj);
</span><span class='line'>  } else {
</span><span class='line'>    return class_loader;
</span><span class='line'>  }
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p><code>SystemDictionary::resolve_instance_class_or_null</code>这个方法非常关键了，在里面我们看到会获取一把锁ObjectLocker，其相当于我们java代码里的<code>synchronized</code>关键字，而对象对应的是lockObject，这个对象是上面的<code>SystemDictionary::compute_loader_lock_object</code>方法返回的，从代码可知只要不是bootstrapClassloader加载的类就会返回当前classloader对象，也就是说当我们在加载一个类的时候其实是会持有当前类加载对象的锁的，在获取了这把锁之后就会调用ClassLoader.loadClass来加载类了。这其实就解释了<code>HSFBizProcessor-4-thread-4</code>这个线程为什么持有了</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>object 0x00000007608e6fc8, a com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader</span></code></pre></td></tr></table></div></figure>


<p>这个类加载的锁，不过遗憾的是因为这把锁不是java层面来显示加载的，因此我们在<code>jstack</code>线程dump的输出里居然看不到这把锁的存在.</p>

<h3>HSFBizProcessor-4-thread-5</h3>

<p>先上堆栈：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>"HSFBizProcessor-4-thread-5":
</span><span class='line'>  at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLoadedClass(Unknown Source)
</span><span class='line'>  - waiting to lock &lt;0x00000007608e6fc8&gt; (a com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader)
</span><span class='line'>  at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.buddy.DependentPolicy.loadClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.buddy.PolicyHandler.doBuddyClassLoading(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.loader.BundleLoader.findClass(Unknown Source)
</span><span class='line'>  at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(Unknown Source)
</span><span class='line'>  at com.alipay.cloudengine.extensions.equinox.KernelBundleClassLoader.loadClass(KernelBundleClassLoader.java:121)
</span><span class='line'>  at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
</span><span class='line'>  at java.lang.Class.forName0(Native Method)
</span><span class='line'>  at java.lang.Class.forName(Class.java:169)</span></code></pre></td></tr></table></div></figure>


<p>这个线程栈其实和之前那个线程差不多，只是等的锁不一样，另外触发类加载的动作是<code>Class.forName</code>，获取大家也猜到了，其实是在下面两行堆栈之间同样获取了一把类加载器的锁</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
</span><span class='line'>at java.lang.Class.forName0(Native Method)</span></code></pre></td></tr></table></div></figure>


<p>这里的代码我也不细贴了，最终调用的jvm里的方法都是一样的，获取锁的逻辑也是一样的</p>

<h2>总结</h2>

<p>想象下这种场景，两个线程分别使用不同的classloader对两个类进行类加载，然而由于osgi类加载机制的缘故，在loadClass过程中可能会委托给别的classloader去加载，而正巧，这两个线程在获取当前classloader的锁之后，然后分别委托对方的classloader去加载，可以看到文章开头列的那个findLoadedClass方法，而synchronized的那个classloader正好是对方的classloader，从而导致了死锁</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之javaagent原理完全解读]]></title>
    <link href="http://nijiaben.github.io/blog/2015/09/14/javaagent/"/>
    <updated>2015-09-14T13:17:50+08:00</updated>
    <id>http://nijiaben.github.io/blog/2015/09/14/javaagent</id>
    <content type="html"><![CDATA[<p><code>注:文章首发于InfoQ：</code><a href="http://www.infoq.com/cn/articles/javaagent-illustrated">JVM源码分析之javaagent原理完全解读</a></p>

<h2>概述</h2>

<p>本文重点讲述javaagent的具体实现，因为它面向的是我们java程序员，而且agent都是用java编写的，不需要太多的c/c++编程基础，不过这篇文章里也会讲到JVMTIAgent(c实现的)，因为javaagent的运行还是依赖于一个特殊的JVMTIAgent。</p>

<!-- more -->


<p>对于javaagent或许大家都听过，甚至使用过，常见的用法大致如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>java -javaagent:myagent.jar=mode=test Test</span></code></pre></td></tr></table></div></figure>


<p>我们通过-javaagent来指定我们编写的agent的jar路径（./myagent.jar）及要传给agent的参数（mode=test），这样在启动的时候这个agent就可以做一些我们想要它做的事了。</p>

<p>javaagent的主要的功能如下：</p>

<ul>
<li>可以在加载class文件之前做拦截把字节码做修改</li>
<li>可以在运行期将已经加载的类的字节码做变更，但是这种情况下会有很多的限制，后面会详细说</li>
<li>还有其他的一些小众的功能

<ul>
<li>获取所有已经被加载过的类</li>
<li>获取所有已经被初始化过了的类（执行过了clinit方法，是上面的一个子集）</li>
<li>获取某个对象的大小</li>
<li>将某个jar加入到bootstrapclasspath里作为高优先级被bootstrapClassloader加载</li>
<li>将某个jar加入到classpath里供AppClassloard去加载</li>
<li>设置某些native方法的前缀，主要在查找native方法的时候做规则匹配</li>
</ul>
</li>
</ul>


<p>想象一下可以让程序按照我们预期的逻辑去执行，听起来是不是挺酷的。</p>

<h2>JVMTI</h2>

<p><a href="http://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html">JVMTI</a>全称JVM Tool Interface，是jvm暴露出来的一些供用户扩展的接口集合，JVMTI是基于事件驱动的，JVM每执行到一定的逻辑就会调用一些事件的回调接口（如果有的话），这些接口可以供开发者去扩展自己的逻辑。</p>

<p>比如说我们最常见的想在某个类的字节码文件读取之后类定义之前能修改相关的字节码，从而使创建的class对象是我们修改之后的字节码内容，那我们就可以实现一个回调函数赋给JvmtiEnv（JVMTI的运行时，通常一个JVMTIAgent对应一个jvmtiEnv，但是也可以对应多个）的回调方法集合里的ClassFileLoadHook，这样在接下来的类文件加载过程中都会调用到这个函数里来了，大致实现如下:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>jvmtiEventCallbacks callbacks;
</span><span class='line'>jvmtiEnv *          jvmtienv = jvmti(agent);
</span><span class='line'>jvmtiError          jvmtierror;
</span><span class='line'>memset(&callbacks, 0, sizeof(callbacks));
</span><span class='line'>callbacks.ClassFileLoadHook = &eventHandlerClassFileLoadHook;
</span><span class='line'>jvmtierror = (*jvmtienv)-&gt;SetEventCallbacks( jvmtienv,
</span><span class='line'>                                             &callbacks,
</span><span class='line'>                                             sizeof(callbacks));</span></code></pre></td></tr></table></div></figure>


<h2>JVMTIAgent</h2>

<p>JVMTIAgent其实就是一个动态库，利用JVMTI暴露出来的一些接口来干一些我们想做但是正常情况下又做不到的事情，不过为了和普通的动态库进行区分，它一般会实现如下的一个或者多个函数：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>JNIEXPORT jint JNICALL
</span><span class='line'>Agent_OnLoad(JavaVM *vm, char *options, void *reserved);
</span><span class='line'>
</span><span class='line'>JNIEXPORT jint JNICALL
</span><span class='line'>Agent_OnAttach(JavaVM* vm, char* options, void* reserved);
</span><span class='line'>
</span><span class='line'>JNIEXPORT void JNICALL
</span><span class='line'>Agent_OnUnload(JavaVM *vm); 
</span></code></pre></td></tr></table></div></figure>


<ul>
<li><code>Agent_OnLoad</code>函数，如果agent是在启动的时候加载的，也就是在vm参数里通过-agentlib来指定，那在启动过程中就会去执行这个agent里的<code>Agent_OnLoad</code>函数。</li>
<li><code>Agent_OnAttach</code>函数，如果agent不是在启动的时候加载的，是我们先attach到目标进程上，然后给对应的目标进程发送load命令来加载agent，在加载过程中就会调用<code>Agent_OnAttach</code>函数。</li>
<li><code>Agent_OnUnload</code>函数，在agent做卸载的时候调用，不过貌似基本上很少实现它。</li>
</ul>


<p>其实我们每天都在和JVMTIAgent打交道，只是你可能没有意识到而已，比如我们经常使用eclipse等工具对java代码做调试，其实就利用了jre自带的jdwp agent来实现的，只是由于eclipse等工具在没让你察觉的情况下将相关参数(类似<code>-agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:61349</code>)给自动加到程序启动参数列表里了，其中agentlib参数就是用来跟要加载的agent的名字，比如这里的jdwp(不过这不是动态库的名字，而JVM是会做一些名称上的扩展，比如在linux下会去找<code>libjdwp.so</code>的动态库进行加载，也就是在名字的基础上加前缀<code>lib</code>,再加后缀<code>.so</code>)，接下来会跟一堆相关的参数，会将这些参数传给<code>Agent_OnLoad</code>或者<code>Agent_OnAttach</code>函数里对应的<code>options</code>参数。</p>

<h2>javaagent</h2>

<p>说到javaagent必须要讲的是一个叫做instrument的JVMTIAgent（linux下对应的动态库是libinstrument.so），因为就是它来实现javaagent的功能的，另外instrument agent还有个别名叫JPLISAgent(Java Programming Language Instrumentation Services Agent)，从这名字里也完全体现了其最本质的功能：就是专门为java语言编写的插桩服务提供支持的。</p>

<h3>instrument agent</h3>

<p>instrument agent实现了<code>Agent_OnLoad</code>和<code>Agent_OnAttach</code>两方法，也就是说我们在用它的时候既支持启动的时候来加载agent，也支持在运行期来动态来加载这个agent，其中启动时加载agent还可以通过类似<code>-javaagent:myagent.jar</code>的方式来间接加载instrument agent，运行期动态加载agent依赖的是jvm的attach机制<a href="http://lovestblog.cn/blog/2014/06/18/jvm-attach/">JVM Attach机制实现</a>，通过发送load命令来加载agent。</p>

<p>instrument agent的核心数据结构如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>struct _JPLISAgent {
</span><span class='line'>    JavaVM *                mJVM;                   /* handle to the JVM */
</span><span class='line'>    JPLISEnvironment        mNormalEnvironment;     /* for every thing but retransform stuff */
</span><span class='line'>    JPLISEnvironment        mRetransformEnvironment;/* for retransform stuff only */
</span><span class='line'>    jobject                 mInstrumentationImpl;   /* handle to the Instrumentation instance */
</span><span class='line'>    jmethodID               mPremainCaller;         /* method on the InstrumentationImpl that does the premain stuff (cached to save lots of lookups) */
</span><span class='line'>    jmethodID               mAgentmainCaller;       /* method on the InstrumentationImpl for agents loaded via attach mechanism */
</span><span class='line'>    jmethodID               mTransform;             /* method on the InstrumentationImpl that does the class file transform */
</span><span class='line'>    jboolean                mRedefineAvailable;     /* cached answer to "does this agent support redefine" */
</span><span class='line'>    jboolean                mRedefineAdded;         /* indicates if can_redefine_classes capability has been added */
</span><span class='line'>    jboolean                mNativeMethodPrefixAvailable; /* cached answer to "does this agent support prefixing" */
</span><span class='line'>    jboolean                mNativeMethodPrefixAdded;     /* indicates if can_set_native_method_prefix capability has been added */
</span><span class='line'>    char const *            mAgentClassName;        /* agent class name */
</span><span class='line'>    char const *            mOptionsString;         /* -javaagent options string */
</span><span class='line'>};
</span><span class='line'>
</span><span class='line'>struct _JPLISEnvironment {
</span><span class='line'>    jvmtiEnv *              mJVMTIEnv;              /* the JVM TI environment */
</span><span class='line'>    JPLISAgent *            mAgent;                 /* corresponding agent */
</span><span class='line'>    jboolean                mIsRetransformer;       /* indicates if special environment */
</span><span class='line'>};
</span></code></pre></td></tr></table></div></figure>


<p>这里解释下几个重要项：
* mNormalEnvironment：主要提供正常的类transform及redefine功能的。
* mRetransformEnvironment：主要提供类retransform功能的。
* mInstrumentationImpl：这个对象非常重要，也是我们java agent和JVM进行交互的入口，或许写过javaagent的人在写<code>premain</code>以及<code>agentmain</code>方法的时候注意到了有个Instrumentation的参数，这个参数其实就是这里的对象。
* mPremainCaller：指向<code>sun.instrument.InstrumentationImpl.loadClassAndCallPremain</code>方法，如果agent是在启动的时候加载的，那该方法会被调用。
* mAgentmainCaller：指向<code>sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain</code>方法，该方法在通过attach的方式动态加载agent的时候调用。
* mTransform：指向<code>sun.instrument.InstrumentationImpl.transform</code>方法。
* mAgentClassName：在我们javaagent的MANIFEST.MF里指定的<code>Agent-Class</code>。
* mOptionsString：传给agent的一些参数。
* mRedefineAvailable：是否开启了redefine功能，在javaagent的MANIFEST.MF里设置<code>Can-Redefine-Classes:true</code>。
* mNativeMethodPrefixAvailable：是否支持native方法前缀设置，通样在javaagent的MANIFEST.MF里设置<code>Can-Set-Native-Method-Prefix:true</code>。
* mIsRetransformer：如果在javaagent的MANIFEST.MF文件里定义了<code>Can-Retransform-Classes:true</code>，那将会设置mRetransformEnvironment的mIsRetransformer为true。</p>

<h3>启动时加载instrument agent</h3>

<p>正如『概述』里提到的方式，就是启动的时候加载instrument agent，具体过程都在<code>InvocationAdapter.c</code>的<code>Agent_OnLoad</code>方法里，简单描述下过程：</p>

<ul>
<li>创建并初始化JPLISAgent</li>
<li>监听VMInit事件，在vm初始化完成之后做下面的事情：

<ul>
<li>创建InstrumentationImpl对象</li>
<li>监听ClassFileLoadHook事件</li>
<li>调用InstrumentationImpl的<code>loadClassAndCallPremain</code>方法，在这个方法里会去调用javaagent里MANIFEST.MF里指定的<code>Premain-Class</code>类的premain方法</li>
</ul>
</li>
<li>解析javaagent里MANIFEST.MF里的参数，并根据这些参数来设置JPLISAgent里的一些内容</li>
</ul>


<h3>运行时加载instrument agent</h3>

<p>运行时加载的方式，大致按照下面的方式来操作：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>VirtualMachine vm = VirtualMachine.attach(pid);
</span><span class='line'>vm.loadAgent(agentPath, agentArgs);</span></code></pre></td></tr></table></div></figure>


<p>上面会通过jvm的attach机制来请求目标jvm加载对应的agent，过程大致如下：</p>

<ul>
<li>创建并初始化JPLISAgent</li>
<li>解析javaagent里MANIFEST.MF里的参数</li>
<li>创建InstrumentationImpl对象</li>
<li>监听ClassFileLoadHook事件</li>
<li>调用InstrumentationImpl的<code>loadClassAndCallAgentmain</code>方法，在这个方法里会去调用javaagent里MANIFEST.MF里指定的<code>Agent-Class</code>类的<code>agentmain</code>方法</li>
</ul>


<h3>instrument agent的ClassFileLoadHook回调实现</h3>

<p>不管是启动时还是运行时加载的instrument agent都关注着同一个jvmti事件&mdash;<code>ClassFileLoadHook</code>，这个事件是在读取字节码文件之后回调时用的，这样可以对原来的字节码做修改，那这里面究竟是怎样实现的呢？</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void JNICALL
</span><span class='line'>eventHandlerClassFileLoadHook(  jvmtiEnv *              jvmtienv,
</span><span class='line'>                                JNIEnv *                jnienv,
</span><span class='line'>                                jclass                  class_being_redefined,
</span><span class='line'>                                jobject                 loader,
</span><span class='line'>                                const char*             name,
</span><span class='line'>                                jobject                 protectionDomain,
</span><span class='line'>                                jint                    class_data_len,
</span><span class='line'>                                const unsigned char*    class_data,
</span><span class='line'>                                jint*                   new_class_data_len,
</span><span class='line'>                                unsigned char**         new_class_data) {
</span><span class='line'>    JPLISEnvironment * environment  = NULL;
</span><span class='line'>
</span><span class='line'>    environment = getJPLISEnvironment(jvmtienv);
</span><span class='line'>
</span><span class='line'>    /* if something is internally inconsistent (no agent), just silently return without touching the buffer */
</span><span class='line'>    if ( environment != NULL ) {
</span><span class='line'>        jthrowable outstandingException = preserveThrowable(jnienv);
</span><span class='line'>        transformClassFile( environment-&gt;mAgent,
</span><span class='line'>                            jnienv,
</span><span class='line'>                            loader,
</span><span class='line'>                            name,
</span><span class='line'>                            class_being_redefined,
</span><span class='line'>                            protectionDomain,
</span><span class='line'>                            class_data_len,
</span><span class='line'>                            class_data,
</span><span class='line'>                            new_class_data_len,
</span><span class='line'>                            new_class_data,
</span><span class='line'>                            environment-&gt;mIsRetransformer);
</span><span class='line'>        restoreThrowable(jnienv, outstandingException);
</span><span class='line'>    }
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>先根据jvmtiEnv取得对应的JPLISEnvironment，因为上面我已经说到其实有两个JPLISEnvironment（并且有两个jvmtiEnv），其中一个专门做retransform的，而另外一个用来做其他的事情，根据不同的用途我们在注册具体的ClassFileTransformer的时候也是分开的，对于作为retransform用的ClassFileTransformer我们会注册到一个单独的TransformerManager里。</p>

<p>接着调用transformClassFile方法，由于函数实现比较长，我这里就不贴代码了，大致意思就是调用InstrumentationImpl对象的transform方法，根据最后那个参数来决定选哪个TransformerManager里的ClassFileTransformer对象们做transform操作。</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> private byte[]
</span><span class='line'>    transform(  ClassLoader         loader,
</span><span class='line'>                String              classname,
</span><span class='line'>                Class               classBeingRedefined,
</span><span class='line'>                ProtectionDomain    protectionDomain,
</span><span class='line'>                byte[]              classfileBuffer,
</span><span class='line'>                boolean             isRetransformer) {
</span><span class='line'>        TransformerManager mgr = isRetransformer?
</span><span class='line'>                                        mRetransfomableTransformerManager :
</span><span class='line'>                                        mTransformerManager;
</span><span class='line'>        if (mgr == null) {
</span><span class='line'>            return null; // no manager, no transform
</span><span class='line'>        } else {
</span><span class='line'>            return mgr.transform(   loader,
</span><span class='line'>                                    classname,
</span><span class='line'>                                    classBeingRedefined,
</span><span class='line'>                                    protectionDomain,
</span><span class='line'>                                    classfileBuffer);
</span><span class='line'>        }
</span><span class='line'>    }
</span><span class='line'>    
</span><span class='line'>  public byte[]
</span><span class='line'>    transform(  ClassLoader         loader,
</span><span class='line'>                String              classname,
</span><span class='line'>                Class               classBeingRedefined,
</span><span class='line'>                ProtectionDomain    protectionDomain,
</span><span class='line'>                byte[]              classfileBuffer) {
</span><span class='line'>        boolean someoneTouchedTheBytecode = false;
</span><span class='line'>
</span><span class='line'>        TransformerInfo[]  transformerList = getSnapshotTransformerList();
</span><span class='line'>
</span><span class='line'>        byte[]  bufferToUse = classfileBuffer;
</span><span class='line'>
</span><span class='line'>        // order matters, gotta run 'em in the order they were added
</span><span class='line'>        for ( int x = 0; x &lt; transformerList.length; x++ ) {
</span><span class='line'>            TransformerInfo         transformerInfo = transformerList[x];
</span><span class='line'>            ClassFileTransformer    transformer = transformerInfo.transformer();
</span><span class='line'>            byte[]                  transformedBytes = null;
</span><span class='line'>
</span><span class='line'>            try {
</span><span class='line'>                transformedBytes = transformer.transform(   loader,
</span><span class='line'>                                                            classname,
</span><span class='line'>                                                            classBeingRedefined,
</span><span class='line'>                                                            protectionDomain,
</span><span class='line'>                                                            bufferToUse);
</span><span class='line'>            }
</span><span class='line'>            catch (Throwable t) {
</span><span class='line'>                // don't let any one transformer mess it up for the others.
</span><span class='line'>                // This is where we need to put some logging. What should go here? FIXME
</span><span class='line'>            }
</span><span class='line'>
</span><span class='line'>            if ( transformedBytes != null ) {
</span><span class='line'>                someoneTouchedTheBytecode = true;
</span><span class='line'>                bufferToUse = transformedBytes;
</span><span class='line'>            }
</span><span class='line'>        }
</span><span class='line'>
</span><span class='line'>        // if someone modified it, return the modified buffer.
</span><span class='line'>        // otherwise return null to mean "no transforms occurred"
</span><span class='line'>        byte [] result;
</span><span class='line'>        if ( someoneTouchedTheBytecode ) {
</span><span class='line'>            result = bufferToUse;
</span><span class='line'>        }
</span><span class='line'>        else {
</span><span class='line'>            result = null;
</span><span class='line'>        }
</span><span class='line'>
</span><span class='line'>        return result;
</span><span class='line'>    }   </span></code></pre></td></tr></table></div></figure>


<p>以上是最终调到的java代码，可以看到已经调用到我们自己编写的javaagent代码里了，我们一般是实现一个ClassFileTransformer类，然后创建一个对象注册了对应的TransformerManager里。</p>

<h2>Class Transform的实现</h2>

<p>这里说的class transform其实是狭义的，主要是针对第一次类文件加载的时候就要求被transform的场景，在加载类文件的时候发出ClassFileLoad的事件，然后交给instrumenat agent来调用javaagent里注册的ClassFileTransformer实现字节码的修改。</p>

<h2>Class Redefine的实现</h2>

<p>类重新定义，这是Instrumentation提供的基础功能之一，主要用在已经被加载过的类上，想对其进行修改，要做这件事，我们必须要知道两个东西，一个是要修改哪个类，另外一个是那个类你想修改成怎样的结构，有了这两信息之后于是你就可以通过InstrumentationImpl的下面的redefineClasses方法去操作了：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>public void
</span><span class='line'>    redefineClasses(ClassDefinition[]   definitions)
</span><span class='line'>            throws  ClassNotFoundException {
</span><span class='line'>        if (!isRedefineClassesSupported()) {
</span><span class='line'>            throw new UnsupportedOperationException("redefineClasses is not supported in this environment");
</span><span class='line'>        }
</span><span class='line'>        if (definitions == null) {
</span><span class='line'>            throw new NullPointerException("null passed as 'definitions' in redefineClasses");
</span><span class='line'>        }
</span><span class='line'>        for (int i = 0; i &lt; definitions.length; ++i) {
</span><span class='line'>            if (definitions[i] == null) {
</span><span class='line'>                throw new NullPointerException("element of 'definitions' is null in redefineClasses");
</span><span class='line'>            }
</span><span class='line'>        }
</span><span class='line'>        if (definitions.length == 0) {
</span><span class='line'>            return; // short-circuit if there are no changes requested
</span><span class='line'>        }
</span><span class='line'>
</span><span class='line'>        redefineClasses0(mNativeAgent, definitions);
</span><span class='line'>    }</span></code></pre></td></tr></table></div></figure>


<p>在JVM里对应的实现是创建一个<code>VM_RedefineClasses</code>的<code>VM_Operation</code>，注意执行它的时候会stop the world的：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>jvmtiError
</span><span class='line'>JvmtiEnv::RedefineClasses(jint class_count, const jvmtiClassDefinition* class_definitions) {
</span><span class='line'>//TODO: add locking
</span><span class='line'>  VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_redefine);
</span><span class='line'>  VMThread::execute(&op);
</span><span class='line'>  return (op.check_error());
</span><span class='line'>} /* end RedefineClasses */</span></code></pre></td></tr></table></div></figure>


<p>这个过程我尽量用语言来描述清楚，不详细贴代码了，因为代码量实在有点大：</p>

<ul>
<li>挨个遍历要批量重定义的jvmtiClassDefinition</li>
<li>然后读取新的字节码，如果有关注ClassFileLoadHook事件的，还会走对应的transform来对新的字节码再做修改</li>
<li>字节码解析好，创建一个klassOop对象</li>
<li>对比新老类，并要求如下：

<ul>
<li>父类是同一个</li>
<li>实现的接口数也要相同，并且是相同的接口</li>
<li>类访问符必须一致</li>
<li>字段数和字段名要一致</li>
<li>新增或删除的方法必须是private static/final的</li>
<li>可以修改方法</li>
</ul>
</li>
<li>对新类做字节码校验</li>
<li>合并新老类的常量池</li>
<li>如果老类上有断点，那都清除掉</li>
<li>对老类做jit去优化</li>
<li>对新老方法匹配的方法的jmethodid做更新，将老的jmethodId更新到新的method上</li>
<li>新类的常量池的holer指向老的类</li>
<li>将新类和老类的一些属性做交换，比如常量池，methods，内部类</li>
<li>初始化新的vtable和itable</li>
<li>交换annotation的method,field,paramenter</li>
<li>遍历所有当前类的子类，修改他们的vtable及itable</li>
</ul>


<p>上面是基本的过程，总的来说就是只更新了类里内容，相当于只更新了指针指向的内容，并没有更新指针，避免了遍历大量已有类对象对它们进行更新带来的开销。</p>

<h2>Class Retransform的实现</h2>

<p>retransform class可以简单理解为回滚操作，具体回滚到哪个版本，这个需要看情况而定，下面不管那种情况都有一个前提，那就是javaagent已经要求要有retransform的能力了：</p>

<ul>
<li>如果类是在第一次加载的的时候就做了transform，那么做retransform的时候会将代码回滚到transform之后的代码</li>
<li>如果类是在第一次加载的的时候没有任何变化，那么做retransform的时候会将代码回滚到最原始的类文件里的字节码</li>
<li>如果类已经被加载了，期间类可能做过多次redefine(比如被另外一个agent做过)，但是接下来加载一个新的agent要求有retransform的能力了，然后对类做redefine的动作，那么retransform的时候会将代码回滚到上一个agent最后一次做redefine后的字节码</li>
</ul>


<p>我们从InstrumentationImpl的<code>retransformClasses</code>方法参数看猜到应该是做回滚操作，因为我们只指定了class</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>public void
</span><span class='line'>retransformClasses(Class&lt;?&gt;[] classes) {
</span><span class='line'>    if (!isRetransformClassesSupported()) {
</span><span class='line'>        throw new UnsupportedOperationException(
</span><span class='line'>          "retransformClasses is not supported in this environment");
</span><span class='line'>    }
</span><span class='line'>    retransformClasses0(mNativeAgent, classes);
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>不过retransform的实现其实也是通过redefine的功能来实现，在类加载的时候有比较小的差别，主要体现在究竟会走哪些transform上，如果当前是做retransform的话，那将忽略那些注册到正常的TransformerManager里的ClassFileTransformer，而只会走专门为retransform而准备的TransformerManager的ClassFileTransformer，不然想象一下字节码又被无声无息改成某个中间态了。</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>private:
</span><span class='line'>  void post_all_envs() {
</span><span class='line'>    if (_load_kind != jvmti_class_load_kind_retransform) {
</span><span class='line'>      // for class load and redefine,
</span><span class='line'>      // call the non-retransformable agents
</span><span class='line'>      JvmtiEnvIterator it;
</span><span class='line'>      for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
</span><span class='line'>        if (!env-&gt;is_retransformable() && env-&gt;is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {
</span><span class='line'>          // non-retransformable agents cannot retransform back,
</span><span class='line'>          // so no need to cache the original class file bytes
</span><span class='line'>          post_to_env(env, false);
</span><span class='line'>        }
</span><span class='line'>      }
</span><span class='line'>    }
</span><span class='line'>    JvmtiEnvIterator it;
</span><span class='line'>    for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
</span><span class='line'>      // retransformable agents get all events
</span><span class='line'>      if (env-&gt;is_retransformable() && env-&gt;is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {
</span><span class='line'>        // retransformable agents need to cache the original class file
</span><span class='line'>        // bytes if changes are made via the ClassFileLoadHook
</span><span class='line'>        post_to_env(env, true);
</span><span class='line'>      }
</span><span class='line'>    }
</span><span class='line'>  }</span></code></pre></td></tr></table></div></figure>


<h2>javaagent的其他小众功能</h2>

<p>javaagent除了做字节码上面的修改之外，其实还有一些小功能，有时候还是挺有用的</p>

<ul>
<li>获取所有已经被加载的类</li>
</ul>


<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>     Class[] getAllLoadedClasses();</span></code></pre></td></tr></table></div></figure>


<ul>
<li>获取所有已经被初始化过了的类</li>
</ul>


<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>  Class[] getInitiatedClasses(ClassLoader loader);</span></code></pre></td></tr></table></div></figure>


<ul>
<li>获取某个对象的大小</li>
</ul>


<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>  long getObjectSize(Object objectToSize);</span></code></pre></td></tr></table></div></figure>


<ul>
<li>将某个jar加入到bootstrapclasspath里优先其他jar被加载</li>
</ul>


<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void appendToBootstrapClassLoaderSearch(JarFile jarfile);</span></code></pre></td></tr></table></div></figure>


<ul>
<li>将某个jar加入到classpath里供appclassloard去加载</li>
</ul>


<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void appendToSystemClassLoaderSearch(JarFile jarfile);</span></code></pre></td></tr></table></div></figure>


<ul>
<li>设置某些native方法的前缀，主要在找native方法的时候做规则匹配</li>
</ul>


<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix);</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[进程物理内存远大于Xmx的问题分析]]></title>
    <link href="http://nijiaben.github.io/blog/2015/08/21/rssxmx/"/>
    <updated>2015-08-21T18:58:53+08:00</updated>
    <id>http://nijiaben.github.io/blog/2015/08/21/rssxmx</id>
    <content type="html"><![CDATA[<h2>问题描述</h2>

<p>最近经常被问到一个问题，"为什么我们系统进程占用的物理内存(Res/Rss)会远远大于设置的Xmx值"，比如Xmx设置1.7G，但是top看到的Res的值却达到了3.0G，随着进程的运行，Res的值还在递增，直到达到某个值，被OS当做bad process直接被kill掉了。</p>

<!-- more -->




<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>top - 16:57:47 up 73 days,  4:12,  8 users,  load average: 6.78, 9.68, 13.31
</span><span class='line'>Tasks: 130 total,   1 running, 123 sleeping,   6 stopped,   0 zombie
</span><span class='line'>Cpu(s): 89.9%us,  5.6%sy,  0.0%ni,  2.0%id,  0.7%wa,  0.7%hi,  1.2%si,  0.0%st
</span><span class='line'>  ...
</span><span class='line'>  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
</span><span class='line'>22753 admin     20   0 4252m 3.0g  17m S 192.8 52.7 151:47.59 /opt/taobao/java/bin/java -server -Xms1700m -Xmx1700m -Xmn680m -Xss256k -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseStringCache -XX:+
</span><span class='line'>   40 root      20   0     0    0    0 D  0.3  0.0   5:53.07 [kswapd0]</span></code></pre></td></tr></table></div></figure>


<h2>物理内存大于Xmx可能吗</h2>

<p>先说下Xmx，这个vm配置只包括我们熟悉的新生代和老生代的最大值，不包括持久代，也不包括CodeCache，还有我们常听说的堆外内存从名字上一看也知道没有包括在内，当然还有其他内存也不会算在内等，因此理论上我们看到物理内存大于Xmx也是可能的，不过超过太多估计就可能有问题了。</p>

<h2>物理内存和虚拟内存间的映射关系</h2>

<p>我们知道os在内存上面的设计是花了心思的，为了让资源得到最大合理利用，在物理内存之上搞一层虚拟地址，同一台机器上每个进程可访问的虚拟地址空间大小都是一样的，为了屏蔽掉复杂的到物理内存的映射，该工作os直接做了，当需要物理内存的时候，当前虚拟地址又没有映射到物理内存上的时候，就会发生缺页中断，由内核去为之准备一块物理内存，所以即使我们分配了一块1G的虚拟内存，物理内存上不一定有一块1G的空间与之对应，那到底这块虚拟内存块到底映射了多少物理内存呢，这个我们在linux下可以通过<code>/proc/&lt;pid&gt;/smaps</code>这个文件看到，其中的Size表示虚拟内存大小，而Rss表示的是物理内存，所以从这层意义上来说和虚拟内存块对应的物理内存块不应该超过此虚拟内存块的空间范围</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>8dc00000-100000000 rwxp 00000000 00:00 0
</span><span class='line'>Size:            1871872 kB
</span><span class='line'>Rss:             1798444 kB
</span><span class='line'>Pss:             1798444 kB
</span><span class='line'>Shared_Clean:          0 kB
</span><span class='line'>Shared_Dirty:          0 kB
</span><span class='line'>Private_Clean:         0 kB
</span><span class='line'>Private_Dirty:   1798444 kB
</span><span class='line'>Referenced:      1798392 kB
</span><span class='line'>Anonymous:       1798444 kB
</span><span class='line'>AnonHugePages:         0 kB
</span><span class='line'>Swap:                  0 kB
</span><span class='line'>KernelPageSize:        4 kB
</span><span class='line'>MMUPageSize:           4 kB</span></code></pre></td></tr></table></div></figure>


<p>此次为了排查这个问题，我特地写了个简单的分析工具来分析这个问题，将连续的虚拟内存块合并做统计，一般来说连续分配的内存块还是有一定关系的，当然也不能完全肯定这种关系，得到的效果大致如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>from-&gt;to  vs rss rss_percentage(rss/total_rss) merge_block_count
</span><span class='line'>
</span><span class='line'>0x8dc00000-&gt;0x30c9a20000      1871872      1487480      53.77%     1
</span><span class='line'>0x7faf7a4c5000-&gt;0x7fffa7dd9000      1069464      735996      26.60%     440
</span><span class='line'>0x7faf50c75000-&gt;0x7faf6c02a000      445996      226860      8.20%     418
</span><span class='line'>0x7faf6c027000-&gt;0x7faf78010000      196452      140640      5.08%     492
</span><span class='line'>0x418e8000-&gt;0x100000000      90968      90904      3.29%     1
</span><span class='line'>0x7faf48000000-&gt;0x7faf50c78000      131072      35120      1.27%     4
</span><span class='line'>0x7faf28000000-&gt;0x7faf3905e000      196608      20708      0.75%     6
</span><span class='line'>0x7faf38000000-&gt;0x7faf4ad83000      196608      17036      0.62%     6
</span><span class='line'>0x7faf78009000-&gt;0x7faf7a4c6000      37612      10440      0.38%     465
</span><span class='line'>0x30c9e00000-&gt;0x30ca202000      3656      716      0.03%     5
</span><span class='line'>0x7faf20000000-&gt;0x7faf289c7000      65536      132      0.00%     2
</span><span class='line'>0x30c9a00000-&gt;0x30c9c20000      128      108      0.00%     1
</span><span class='line'>0x30ca600000-&gt;0x30cae83000      2164      76      0.00%     5
</span><span class='line'>0x30cbe00000-&gt;0x30cca16000      2152      68      0.00%     5
</span><span class='line'>0x7fffa7dc3000-&gt;0x7fffa7e00000      92      48      0.00%     1
</span><span class='line'>0x30cca00000-&gt;0x7faf21dba000      2148      32      0.00%     5
</span><span class='line'>0x30cb200000-&gt;0x30cbe16000      2080      28      0.00%     4
</span><span class='line'>0x30cae00000-&gt;0x30cb207000      2576      20      0.00%     4
</span><span class='line'>0x30ca200000-&gt;0x30ca617000      2064      16      0.00%     4
</span><span class='line'>0x40000000-&gt;0x4010a000      36      12      0.00%     2
</span><span class='line'>0x30c9c1f000-&gt;0x30c9f89000      12      12      0.00%     3
</span><span class='line'>0x40108000-&gt;0x471be000      8      8      0.00%     1
</span><span class='line'>0x7fffa7dff000-&gt;0x0      4      4      0.00%     0</span></code></pre></td></tr></table></div></figure>


<p>当然这只是一个简单的分析，如果更有价值需要我们挖掘更多的点出来，比如每个内存块是属于哪块memory pool，到底是什么地方分配的等，不过需要jvm支持(<code>注：上面的第一条，其实就是new+old+perm对应的虚拟内存及其物理内存映射情况</code>)。</p>

<h2>进程满足什么条件会被os因为oom而被kill</h2>

<p>当一个进程无故消失的时候，我们一般看<code>/var/log/message</code>里是否有<code>Out of memory: Kill process</code>关键字(如果是java进程我们先看是否有crash日志)，如果有就说明是被os因为oom而被kill了：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238016] java invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238022] java cpuset=/ mems_allowed=0
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238024] Pid: 25371, comm: java Not tainted 2.6.32-220.23.2.ali878.el6.x86_64 #1
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238026] Call Trace:
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238039]  [&lt;ffffffff810c35e1&gt;] ? cpuset_print_task_mems_allowed+0x91/0xb0
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238068]  [&lt;ffffffff81114d70&gt;] ? dump_header+0x90/0x1b0
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238074]  [&lt;ffffffff810e1b2e&gt;] ? __delayacct_freepages_end+0x2e/0x30
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238079]  [&lt;ffffffff81213ffc&gt;] ? security_real_capable_noaudit+0x3c/0x70
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238082]  [&lt;ffffffff811151fa&gt;] ? oom_kill_process+0x8a/0x2c0
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238084]  [&lt;ffffffff81115131&gt;] ? select_bad_process+0xe1/0x120
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238087]  [&lt;ffffffff81115650&gt;] ? out_of_memory+0x220/0x3c0
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238093]  [&lt;ffffffff81125929&gt;] ? __alloc_pages_nodemask+0x899/0x930
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238099]  [&lt;ffffffff81159b6a&gt;] ? alloc_pages_current+0xaa/0x110
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238102]  [&lt;ffffffff81111ea7&gt;] ? __page_cache_alloc+0x87/0x90
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238105]  [&lt;ffffffff81127f4b&gt;] ? __do_page_cache_readahead+0xdb/0x270
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238108]  [&lt;ffffffff81128101&gt;] ? ra_submit+0x21/0x30
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238110]  [&lt;ffffffff81113e17&gt;] ? filemap_fault+0x5b7/0x600
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238113]  [&lt;ffffffff8113ca64&gt;] ? __do_fault+0x54/0x510
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238116]  [&lt;ffffffff811140a0&gt;] ? __generic_file_aio_write+0x240/0x470
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238118]  [&lt;ffffffff8113d017&gt;] ? handle_pte_fault+0xf7/0xb50
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238121]  [&lt;ffffffff8111438e&gt;] ? generic_file_aio_write+0xbe/0xe0
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238133]  [&lt;ffffffffa008a171&gt;] ? ext4_file_write+0x61/0x1e0 [ext4]
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238135]  [&lt;ffffffff8113dc54&gt;] ? handle_mm_fault+0x1e4/0x2b0
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238138]  [&lt;ffffffff81177c7a&gt;] ? do_sync_write+0xfa/0x140
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238143]  [&lt;ffffffff81042c69&gt;] ? __do_page_fault+0x139/0x480
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238147]  [&lt;ffffffff8118ad22&gt;] ? vfs_ioctl+0x22/0xa0
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238151]  [&lt;ffffffff814e4f8e&gt;] ? do_page_fault+0x3e/0xa0
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.238154]  [&lt;ffffffff814e2345&gt;] ? page_fault+0x25/0x30
</span><span class='line'>
</span><span class='line'>...
</span><span class='line'>
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.247969] [24673]  1801 24673  1280126   926068   1       0             0 java
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.247971] [25084]  1801 25084     3756      101   0       0             0 top
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.247973] [25094]  1801 25094    25233       30   1       0             0 tail
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.247975] [25098]  1801 25098    25233       31   0       0             0 tail
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.247977] [25100]  1801 25100    25233       30   1       0             0 tail
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.247979] [25485]  1801 25485    25233       30   1       0             0 tail
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.247981] [26055]  1801 26055    25233       30   0       0             0 tail
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.247984] [26069]  1801 26069    25233       30   0       0             0 tail
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.247986] [26081]  1801 26081    25233       30   0       0             0 tail
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.247988] [26147]  1801 26147    25233       32   0       0             0 tail
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.247990] Out of memory: Kill process 24673 (java) score 946 or sacrifice child
</span><span class='line'>Aug 19 08:32:38 mybank-ant kernel: : [6176841.249016] Killed process 24673, UID 1801, (java) total-vm:5120504kB, anon-rss:3703788kB, file-rss:484kB</span></code></pre></td></tr></table></div></figure>


<p>从上面我们看到了一个堆栈，也就是内核里选择被kill进程的过程，这个过程会对进程进行一系列的计算，每个进程都会给它们计算一个score，这个分数会记录在<code>/proc/&lt;pid&gt;/oom_score</code>里，通常这个分数越高，就越危险，被kill的可能性就越大，下面将内核相关的代码贴出来，有兴趣的可以看看，其中代码注释上也写了挺多相关的东西了：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
<span class='line-number'>101</span>
<span class='line-number'>102</span>
<span class='line-number'>103</span>
<span class='line-number'>104</span>
<span class='line-number'>105</span>
<span class='line-number'>106</span>
<span class='line-number'>107</span>
<span class='line-number'>108</span>
<span class='line-number'>109</span>
<span class='line-number'>110</span>
<span class='line-number'>111</span>
<span class='line-number'>112</span>
<span class='line-number'>113</span>
<span class='line-number'>114</span>
<span class='line-number'>115</span>
<span class='line-number'>116</span>
<span class='line-number'>117</span>
<span class='line-number'>118</span>
<span class='line-number'>119</span>
<span class='line-number'>120</span>
<span class='line-number'>121</span>
<span class='line-number'>122</span>
<span class='line-number'>123</span>
<span class='line-number'>124</span>
<span class='line-number'>125</span>
<span class='line-number'>126</span>
<span class='line-number'>127</span>
<span class='line-number'>128</span>
<span class='line-number'>129</span>
<span class='line-number'>130</span>
<span class='line-number'>131</span>
<span class='line-number'>132</span>
<span class='line-number'>133</span>
<span class='line-number'>134</span>
<span class='line-number'>135</span>
<span class='line-number'>136</span>
<span class='line-number'>137</span>
<span class='line-number'>138</span>
<span class='line-number'>139</span>
<span class='line-number'>140</span>
<span class='line-number'>141</span>
<span class='line-number'>142</span>
<span class='line-number'>143</span>
<span class='line-number'>144</span>
<span class='line-number'>145</span>
<span class='line-number'>146</span>
<span class='line-number'>147</span>
<span class='line-number'>148</span>
<span class='line-number'>149</span>
<span class='line-number'>150</span>
<span class='line-number'>151</span>
<span class='line-number'>152</span>
<span class='line-number'>153</span>
<span class='line-number'>154</span>
<span class='line-number'>155</span>
<span class='line-number'>156</span>
<span class='line-number'>157</span>
<span class='line-number'>158</span>
<span class='line-number'>159</span>
<span class='line-number'>160</span>
<span class='line-number'>161</span>
<span class='line-number'>162</span>
<span class='line-number'>163</span>
<span class='line-number'>164</span>
<span class='line-number'>165</span>
<span class='line-number'>166</span>
<span class='line-number'>167</span>
<span class='line-number'>168</span>
<span class='line-number'>169</span>
<span class='line-number'>170</span>
<span class='line-number'>171</span>
<span class='line-number'>172</span>
<span class='line-number'>173</span>
<span class='line-number'>174</span>
<span class='line-number'>175</span>
<span class='line-number'>176</span>
<span class='line-number'>177</span>
<span class='line-number'>178</span>
<span class='line-number'>179</span>
<span class='line-number'>180</span>
<span class='line-number'>181</span>
<span class='line-number'>182</span>
<span class='line-number'>183</span>
<span class='line-number'>184</span>
<span class='line-number'>185</span>
<span class='line-number'>186</span>
<span class='line-number'>187</span>
<span class='line-number'>188</span>
<span class='line-number'>189</span>
<span class='line-number'>190</span>
<span class='line-number'>191</span>
<span class='line-number'>192</span>
<span class='line-number'>193</span>
<span class='line-number'>194</span>
<span class='line-number'>195</span>
<span class='line-number'>196</span>
<span class='line-number'>197</span>
<span class='line-number'>198</span>
<span class='line-number'>199</span>
<span class='line-number'>200</span>
<span class='line-number'>201</span>
<span class='line-number'>202</span>
<span class='line-number'>203</span>
<span class='line-number'>204</span>
<span class='line-number'>205</span>
<span class='line-number'>206</span>
<span class='line-number'>207</span>
<span class='line-number'>208</span>
<span class='line-number'>209</span>
<span class='line-number'>210</span>
<span class='line-number'>211</span>
<span class='line-number'>212</span>
<span class='line-number'>213</span>
<span class='line-number'>214</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>/*
</span><span class='line'> * Simple selection loop. We chose the process with the highest
</span><span class='line'> * number of 'points'. We expect the caller will lock the tasklist.
</span><span class='line'> *
</span><span class='line'> * (not docbooked, we don't want this one cluttering up the manual)
</span><span class='line'> */
</span><span class='line'>static struct task_struct *select_bad_process(unsigned long *ppoints,
</span><span class='line'>                      struct mem_cgroup *mem)
</span><span class='line'>{
</span><span class='line'>  struct task_struct *p;
</span><span class='line'>  struct task_struct *chosen = NULL;
</span><span class='line'>  struct timespec uptime;
</span><span class='line'>  *ppoints = 0;
</span><span class='line'>
</span><span class='line'>  do_posix_clock_monotonic_gettime(&uptime);
</span><span class='line'>  for_each_process(p) {
</span><span class='line'>      unsigned long points;
</span><span class='line'>
</span><span class='line'>      /*
</span><span class='line'>       * skip kernel threads and tasks which have already released
</span><span class='line'>       * their mm.
</span><span class='line'>       */
</span><span class='line'>      if (!p-&gt;mm)
</span><span class='line'>          continue;
</span><span class='line'>      /* skip the init task */
</span><span class='line'>      if (is_global_init(p))
</span><span class='line'>          continue;
</span><span class='line'>      if (mem && !task_in_mem_cgroup(p, mem))
</span><span class='line'>          continue;
</span><span class='line'>
</span><span class='line'>      /*
</span><span class='line'>       * This task already has access to memory reserves and is
</span><span class='line'>       * being killed. Don't allow any other task access to the
</span><span class='line'>       * memory reserve.
</span><span class='line'>       *
</span><span class='line'>       * Note: this may have a chance of deadlock if it gets
</span><span class='line'>       * blocked waiting for another task which itself is waiting
</span><span class='line'>       * for memory. Is there a better alternative?
</span><span class='line'>       */
</span><span class='line'>      if (test_tsk_thread_flag(p, TIF_MEMDIE))
</span><span class='line'>          return ERR_PTR(-1UL);
</span><span class='line'>
</span><span class='line'>      /*
</span><span class='line'>       * This is in the process of releasing memory so wait for it
</span><span class='line'>       * to finish before killing some other task by mistake.
</span><span class='line'>       *
</span><span class='line'>       * However, if p is the current task, we allow the 'kill' to
</span><span class='line'>       * go ahead if it is exiting: this will simply set TIF_MEMDIE,
</span><span class='line'>       * which will allow it to gain access to memory reserves in
</span><span class='line'>       * the process of exiting and releasing its resources.
</span><span class='line'>       * Otherwise we could get an easy OOM deadlock.
</span><span class='line'>       */
</span><span class='line'>      if (p-&gt;flags & PF_EXITING) {
</span><span class='line'>          if (p != current)
</span><span class='line'>              return ERR_PTR(-1UL);
</span><span class='line'>
</span><span class='line'>          chosen = p;
</span><span class='line'>          *ppoints = ULONG_MAX;
</span><span class='line'>      }
</span><span class='line'>
</span><span class='line'>      if (p-&gt;signal-&gt;oom_adj == OOM_DISABLE)
</span><span class='line'>          continue;
</span><span class='line'>
</span><span class='line'>      points = badness(p, uptime.tv_sec);
</span><span class='line'>      if (points &gt; *ppoints || !chosen) {
</span><span class='line'>          chosen = p;
</span><span class='line'>          *ppoints = points;
</span><span class='line'>      }
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>  return chosen;
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>/**
</span><span class='line'> * badness - calculate a numeric value for how bad this task has been
</span><span class='line'> * @p: task struct of which task we should calculate
</span><span class='line'> * @uptime: current uptime in seconds
</span><span class='line'> *
</span><span class='line'> * The formula used is relatively simple and documented inline in the
</span><span class='line'> * function. The main rationale is that we want to select a good task
</span><span class='line'> * to kill when we run out of memory.
</span><span class='line'> *
</span><span class='line'> * Good in this context means that:
</span><span class='line'> * 1) we lose the minimum amount of work done
</span><span class='line'> * 2) we recover a large amount of memory
</span><span class='line'> * 3) we don't kill anything innocent of eating tons of memory
</span><span class='line'> * 4) we want to kill the minimum amount of processes (one)
</span><span class='line'> * 5) we try to kill the process the user expects us to kill, this
</span><span class='line'> *    algorithm has been meticulously tuned to meet the principle
</span><span class='line'> *    of least surprise ... (be careful when you change it)
</span><span class='line'> */
</span><span class='line'>
</span><span class='line'>unsigned long badness(struct task_struct *p, unsigned long uptime)
</span><span class='line'>{
</span><span class='line'>  unsigned long points, cpu_time, run_time;
</span><span class='line'>  struct mm_struct *mm;
</span><span class='line'>  struct task_struct *child;
</span><span class='line'>  int oom_adj = p-&gt;signal-&gt;oom_adj;
</span><span class='line'>  struct task_cputime task_time;
</span><span class='line'>  unsigned long utime;
</span><span class='line'>  unsigned long stime;
</span><span class='line'>
</span><span class='line'>  if (oom_adj == OOM_DISABLE)
</span><span class='line'>      return 0;
</span><span class='line'>
</span><span class='line'>  task_lock(p);
</span><span class='line'>  mm = p-&gt;mm;
</span><span class='line'>  if (!mm) {
</span><span class='line'>      task_unlock(p);
</span><span class='line'>      return 0;
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>  /*
</span><span class='line'>   * The memory size of the process is the basis for the badness.
</span><span class='line'>   */
</span><span class='line'>  points = mm-&gt;total_vm;
</span><span class='line'>
</span><span class='line'>  /*
</span><span class='line'>   * After this unlock we can no longer dereference local variable `mm'
</span><span class='line'>   */
</span><span class='line'>  task_unlock(p);
</span><span class='line'>
</span><span class='line'>  /*
</span><span class='line'>   * swapoff can easily use up all memory, so kill those first.
</span><span class='line'>   */
</span><span class='line'>  if (p-&gt;flags & PF_OOM_ORIGIN)
</span><span class='line'>      return ULONG_MAX;
</span><span class='line'>
</span><span class='line'>  /*
</span><span class='line'>   * Processes which fork a lot of child processes are likely
</span><span class='line'>   * a good choice. We add half the vmsize of the children if they
</span><span class='line'>   * have an own mm. This prevents forking servers to flood the
</span><span class='line'>   * machine with an endless amount of children. In case a single
</span><span class='line'>   * child is eating the vast majority of memory, adding only half
</span><span class='line'>   * to the parents will make the child our kill candidate of choice.
</span><span class='line'>   */
</span><span class='line'>  list_for_each_entry(child, &p-&gt;children, sibling) {
</span><span class='line'>      task_lock(child);
</span><span class='line'>      if (child-&gt;mm != mm && child-&gt;mm)
</span><span class='line'>          points += child-&gt;mm-&gt;total_vm/2 + 1;
</span><span class='line'>      task_unlock(child);
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>  /*
</span><span class='line'>   * CPU time is in tens of seconds and run time is in thousands
</span><span class='line'>         * of seconds. There is no particular reason for this other than
</span><span class='line'>         * that it turned out to work very well in practice.
</span><span class='line'>   */
</span><span class='line'>  thread_group_cputime(p, &task_time);
</span><span class='line'>  utime = cputime_to_jiffies(task_time.utime);
</span><span class='line'>  stime = cputime_to_jiffies(task_time.stime);
</span><span class='line'>  cpu_time = (utime + stime) &gt;&gt; (SHIFT_HZ + 3);
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>  if (uptime &gt;= p-&gt;start_time.tv_sec)
</span><span class='line'>      run_time = (uptime - p-&gt;start_time.tv_sec) &gt;&gt; 10;
</span><span class='line'>  else
</span><span class='line'>      run_time = 0;
</span><span class='line'>
</span><span class='line'>  if (cpu_time)
</span><span class='line'>      points /= int_sqrt(cpu_time);
</span><span class='line'>  if (run_time)
</span><span class='line'>      points /= int_sqrt(int_sqrt(run_time));
</span><span class='line'>
</span><span class='line'>  /*
</span><span class='line'>   * Niced processes are most likely less important, so double
</span><span class='line'>   * their badness points.
</span><span class='line'>   */
</span><span class='line'>  if (task_nice(p) &gt; 0)
</span><span class='line'>      points *= 2;
</span><span class='line'>
</span><span class='line'>  /*
</span><span class='line'>   * Superuser processes are usually more important, so we make it
</span><span class='line'>   * less likely that we kill those.
</span><span class='line'>   */
</span><span class='line'>  if (has_capability_noaudit(p, CAP_SYS_ADMIN) ||
</span><span class='line'>      has_capability_noaudit(p, CAP_SYS_RESOURCE))
</span><span class='line'>      points /= 4;
</span><span class='line'>
</span><span class='line'>  /*
</span><span class='line'>   * We don't want to kill a process with direct hardware access.
</span><span class='line'>   * Not only could that mess up the hardware, but usually users
</span><span class='line'>   * tend to only have this flag set on applications they think
</span><span class='line'>   * of as important.
</span><span class='line'>   */
</span><span class='line'>  if (has_capability_noaudit(p, CAP_SYS_RAWIO))
</span><span class='line'>      points /= 4;
</span><span class='line'>
</span><span class='line'>  /*
</span><span class='line'>   * If p's nodes don't overlap ours, it may still help to kill p
</span><span class='line'>   * because p may have allocated or otherwise mapped memory on
</span><span class='line'>   * this node before. However it will be less likely.
</span><span class='line'>   */
</span><span class='line'>  if (!has_intersects_mems_allowed(p))
</span><span class='line'>      points /= 8;
</span><span class='line'>
</span><span class='line'>  /*
</span><span class='line'>   * Adjust the score by oom_adj.
</span><span class='line'>   */
</span><span class='line'>  if (oom_adj) {
</span><span class='line'>      if (oom_adj &gt; 0) {
</span><span class='line'>          if (!points)
</span><span class='line'>              points = 1;
</span><span class='line'>          points &lt;&lt;= oom_adj;
</span><span class='line'>      } else
</span><span class='line'>          points &gt;&gt;= -(oom_adj);
</span><span class='line'>  }
</span><span class='line'>
</span><span class='line'>#ifdef DEBUG
</span><span class='line'>  printk(KERN_DEBUG "OOMkill: task %d (%s) got %lu points\n",
</span><span class='line'>  p-&gt;pid, p-&gt;comm, points);
</span><span class='line'>#endif
</span><span class='line'>  return points;
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<h2>物理内存到底去哪了？</h2>

<h3>DirectByteBuffer冰山对象？</h3>

<p>这是我们查这个问题首先要想到的一个地方，是否是因为什么地方不断创建DirectByteBuffer对象，但是由于没有被回收导致了内存泄露呢，之前有篇文章已经详细介绍了这种特殊对象<a href="http://lovestblog.cn/blog/2015/05/12/direct-buffer/">JVM源码分析之堆外内存完全解读</a>，对阿里内部的童鞋，可以直接使用zprofiler的heap视图里的堆外内存分析功能拿到统计结果，知道后台到底绑定了多少堆外内存还没有被回收：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>object   position    limit   capacity
</span><span class='line'> java.nio.DirectByteBuffer @ 0x760afaed0  133 133 6380562
</span><span class='line'> java.nio.DirectByteBuffer @ 0x790d51ae0  0   262144  262144
</span><span class='line'> java.nio.DirectByteBuffer @ 0x790d20b80  133934  133934  262144
</span><span class='line'> java.nio.DirectByteBuffer @ 0x790d20b40  0   262144  262144
</span><span class='line'> java.nio.DirectByteBuffer @ 0x790d20b00  133934  133934  262144
</span><span class='line'> java.nio.DirectByteBuffer @ 0x771ba3608  0   262144  262144
</span><span class='line'> java.nio.DirectByteBuffer @ 0x771ba35c8  133934  133934  262144
</span><span class='line'> java.nio.DirectByteBuffer @ 0x7c5c9e250  0   131072  131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x7c5c9e210  74670   74670   131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x7c185cd10  0   131072  131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x7c185ccd0  98965   98965   131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x7b181c980  65627   65627   131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x7a40d6e40  0   131072  131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x794ac3320  0   131072  131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x794a7a418  80490   80490   131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x77279e1d8  0   131072  131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x77279dde8  65627   65627   131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x76ea84000  0   131072  131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x76ea83fc0  82549   82549   131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x764d8d678  0   0   131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x764d8d638  0   0   131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x764d8d5f8  0   0   131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x761a76340  0   131072  131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x761a76300  74369   74369   131072
</span><span class='line'> java.nio.DirectByteBuffer @ 0x7607423d0  0   131072  131072
</span><span class='line'> 总共: 25 / 875 条目; 还有850条,双击展开   1267762 3826551 12083282</span></code></pre></td></tr></table></div></figure>


<h3>某个动态库里频繁分配？</h3>

<p>对于动态库里频繁分配的问题，主要得使用google的perftools工具了，该工具网上介绍挺多的，就不对其用法做详细介绍了，通过该工具我们能得到native方法分配内存的情况，该工具主要利用了unix的一个环境变量LD_PRELOAD，它允许你要加载的动态库优先加载起来，相当于一个Hook了，于是可以针对同一个函数可以选择不同的动态库里的实现了，比如googleperftools就是将malloc方法替换成了tcmalloc的实现，这样就可以跟踪内存分配路径了，得到的效果类似如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Total: 1670.0 MB
</span><span class='line'>  1616.3  96.8%  96.8%   1616.3  96.8% zcalloc
</span><span class='line'>    40.3   2.4%  99.2%     40.3   2.4% os::malloc
</span><span class='line'>     9.4   0.6%  99.8%      9.4   0.6% init
</span><span class='line'>     1.6   0.1%  99.9%      1.7   0.1% readCEN
</span><span class='line'>     1.3   0.1%  99.9%      1.3   0.1% ObjectSynchronizer::omAlloc
</span><span class='line'>     0.5   0.0% 100.0%   1591.0  95.3% Java_java_util_zip_Deflater_init
</span><span class='line'>     0.1   0.0% 100.0%      0.1   0.0% _dl_allocate_tls
</span><span class='line'>     0.1   0.0% 100.0%      0.2   0.0% addMetaName
</span><span class='line'>     0.1   0.0% 100.0%      0.2   0.0% allocZip
</span><span class='line'>     0.1   0.0% 100.0%      0.1   0.0% instanceKlass::add_dependent_nmethod
</span><span class='line'>     0.1   0.0% 100.0%      0.1   0.0% newEntry
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% strdup
</span><span class='line'>     0.0   0.0% 100.0%     25.8   1.5% Java_java_util_zip_Inflater_init
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% growMetaNames
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% _dl_new_object
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% pthread_cond_wait@GLIBC_2.2.5
</span><span class='line'>     0.0   0.0% 100.0%      1.4   0.1% Thread::Thread
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% pthread_cond_timedwait@GLIBC_2.2.5
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% JLI_MemAlloc
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% read_alias_file
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% _nl_intern_locale_data
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% nss_parse_service_list
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% getprotobyname
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% getpwuid
</span><span class='line'>     0.0   0.0% 100.0%      0.0   0.0% _dl_check_map_versions
</span><span class='line'>     0.0   0.0% 100.0%   1590.5  95.2% deflateInit2_</span></code></pre></td></tr></table></div></figure>


<p>从上面的输出中我们看到了<code>zcalloc</code>函数总共分配了1616.3M的内存，还有<code>Java_java_util_zip_Deflater_init</code>分配了1591.0M内存，<code>deflateInit2_</code>分配了1590.5M，然而总共才分配了1670.0M内存，所以这几个函数肯定是调用者和被调用者的关系：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
<span class='line-number'>101</span>
<span class='line-number'>102</span>
<span class='line-number'>103</span>
<span class='line-number'>104</span>
<span class='line-number'>105</span>
<span class='line-number'>106</span>
<span class='line-number'>107</span>
<span class='line-number'>108</span>
<span class='line-number'>109</span>
<span class='line-number'>110</span>
<span class='line-number'>111</span>
<span class='line-number'>112</span>
<span class='line-number'>113</span>
<span class='line-number'>114</span>
<span class='line-number'>115</span>
<span class='line-number'>116</span>
<span class='line-number'>117</span>
<span class='line-number'>118</span>
<span class='line-number'>119</span>
<span class='line-number'>120</span>
<span class='line-number'>121</span>
<span class='line-number'>122</span>
<span class='line-number'>123</span>
<span class='line-number'>124</span>
<span class='line-number'>125</span>
<span class='line-number'>126</span>
<span class='line-number'>127</span>
<span class='line-number'>128</span>
<span class='line-number'>129</span>
<span class='line-number'>130</span>
<span class='line-number'>131</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>JNIEXPORT jlong JNICALL
</span><span class='line'>Java_java_util_zip_Deflater_init(JNIEnv *env, jclass cls, jint level,
</span><span class='line'>                                 jint strategy, jboolean nowrap)
</span><span class='line'>{
</span><span class='line'>    z_stream *strm = calloc(1, sizeof(z_stream));
</span><span class='line'>
</span><span class='line'>    if (strm == 0) {
</span><span class='line'>        JNU_ThrowOutOfMemoryError(env, 0);
</span><span class='line'>        return jlong_zero;
</span><span class='line'>    } else {
</span><span class='line'>        char *msg;
</span><span class='line'>        switch (deflateInit2(strm, level, Z_DEFLATED,
</span><span class='line'>                             nowrap ? -MAX_WBITS : MAX_WBITS,
</span><span class='line'>                             DEF_MEM_LEVEL, strategy)) {
</span><span class='line'>          case Z_OK:
</span><span class='line'>            return ptr_to_jlong(strm);
</span><span class='line'>          case Z_MEM_ERROR:
</span><span class='line'>            free(strm);
</span><span class='line'>            JNU_ThrowOutOfMemoryError(env, 0);
</span><span class='line'>            return jlong_zero;
</span><span class='line'>          case Z_STREAM_ERROR:
</span><span class='line'>            free(strm);
</span><span class='line'>            JNU_ThrowIllegalArgumentException(env, 0);
</span><span class='line'>            return jlong_zero;
</span><span class='line'>          default:
</span><span class='line'>            msg = strm-&gt;msg;
</span><span class='line'>            free(strm);
</span><span class='line'>            JNU_ThrowInternalError(env, msg);
</span><span class='line'>            return jlong_zero;
</span><span class='line'>        }
</span><span class='line'>    }
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
</span><span class='line'>                  version, stream_size)
</span><span class='line'>    z_streamp strm;
</span><span class='line'>    int  level;
</span><span class='line'>    int  method;
</span><span class='line'>    int  windowBits;
</span><span class='line'>    int  memLevel;
</span><span class='line'>    int  strategy;
</span><span class='line'>    const char *version;
</span><span class='line'>    int stream_size;
</span><span class='line'>{
</span><span class='line'>    deflate_state *s;
</span><span class='line'>    int wrap = 1;
</span><span class='line'>    static const char my_version[] = ZLIB_VERSION;
</span><span class='line'>
</span><span class='line'>    ushf *overlay;
</span><span class='line'>    /* We overlay pending_buf and d_buf+l_buf. This works since the average
</span><span class='line'>     * output size for (length,distance) codes is &lt;= 24 bits.
</span><span class='line'>     */
</span><span class='line'>
</span><span class='line'>    if (version == Z_NULL || version[0] != my_version[0] ||
</span><span class='line'>        stream_size != sizeof(z_stream)) {
</span><span class='line'>        return Z_VERSION_ERROR;
</span><span class='line'>    }
</span><span class='line'>    if (strm == Z_NULL) return Z_STREAM_ERROR;
</span><span class='line'>
</span><span class='line'>    strm-&gt;msg = Z_NULL;
</span><span class='line'>    if (strm-&gt;zalloc == (alloc_func)0) {
</span><span class='line'>        strm-&gt;zalloc = zcalloc;
</span><span class='line'>        strm-&gt;opaque = (voidpf)0;
</span><span class='line'>    }
</span><span class='line'>    if (strm-&gt;zfree == (free_func)0) strm-&gt;zfree = zcfree;
</span><span class='line'>
</span><span class='line'>#ifdef FASTEST
</span><span class='line'>    if (level != 0) level = 1;
</span><span class='line'>#else
</span><span class='line'>    if (level == Z_DEFAULT_COMPRESSION) level = 6;
</span><span class='line'>#endif
</span><span class='line'>
</span><span class='line'>    if (windowBits &lt; 0) { /* suppress zlib wrapper */
</span><span class='line'>        wrap = 0;
</span><span class='line'>        windowBits = -windowBits;
</span><span class='line'>    }
</span><span class='line'>#ifdef GZIP
</span><span class='line'>    else if (windowBits &gt; 15) {
</span><span class='line'>        wrap = 2;       /* write gzip wrapper instead */
</span><span class='line'>        windowBits -= 16;
</span><span class='line'>    }
</span><span class='line'>#endif
</span><span class='line'>    if (memLevel &lt; 1 || memLevel &gt; MAX_MEM_LEVEL || method != Z_DEFLATED ||
</span><span class='line'>        windowBits &lt; 8 || windowBits &gt; 15 || level &lt; 0 || level &gt; 9 ||
</span><span class='line'>        strategy &lt; 0 || strategy &gt; Z_FIXED) {
</span><span class='line'>        return Z_STREAM_ERROR;
</span><span class='line'>    }
</span><span class='line'>    if (windowBits == 8) windowBits = 9;  /* until 256-byte window bug fixed */
</span><span class='line'>    s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
</span><span class='line'>    if (s == Z_NULL) return Z_MEM_ERROR;
</span><span class='line'>    strm-&gt;state = (struct internal_state FAR *)s;
</span><span class='line'>    s-&gt;strm = strm;
</span><span class='line'>
</span><span class='line'>    s-&gt;wrap = wrap;
</span><span class='line'>    s-&gt;gzhead = Z_NULL;
</span><span class='line'>    s-&gt;w_bits = windowBits;
</span><span class='line'>    s-&gt;w_size = 1 &lt;&lt; s-&gt;w_bits;
</span><span class='line'>    s-&gt;w_mask = s-&gt;w_size - 1;
</span><span class='line'>
</span><span class='line'>    s-&gt;hash_bits = memLevel + 7;
</span><span class='line'>    s-&gt;hash_size = 1 &lt;&lt; s-&gt;hash_bits;
</span><span class='line'>    s-&gt;hash_mask = s-&gt;hash_size - 1;
</span><span class='line'>    s-&gt;hash_shift =  ((s-&gt;hash_bits+MIN_MATCH-1)/MIN_MATCH);
</span><span class='line'>
</span><span class='line'>    s-&gt;window = (Bytef *) ZALLOC(strm, s-&gt;w_size, 2*sizeof(Byte));
</span><span class='line'>    s-&gt;prev   = (Posf *)  ZALLOC(strm, s-&gt;w_size, sizeof(Pos));
</span><span class='line'>    s-&gt;head   = (Posf *)  ZALLOC(strm, s-&gt;hash_size, sizeof(Pos));
</span><span class='line'>
</span><span class='line'>    s-&gt;lit_bufsize = 1 &lt;&lt; (memLevel + 6); /* 16K elements by default */
</span><span class='line'>
</span><span class='line'>    overlay = (ushf *) ZALLOC(strm, s-&gt;lit_bufsize, sizeof(ush)+2);
</span><span class='line'>    s-&gt;pending_buf = (uchf *) overlay;
</span><span class='line'>    s-&gt;pending_buf_size = (ulg)s-&gt;lit_bufsize * (sizeof(ush)+2L);
</span><span class='line'>
</span><span class='line'>    if (s-&gt;window == Z_NULL || s-&gt;prev == Z_NULL || s-&gt;head == Z_NULL ||
</span><span class='line'>        s-&gt;pending_buf == Z_NULL) {
</span><span class='line'>        s-&gt;status = FINISH_STATE;
</span><span class='line'>        strm-&gt;msg = (char*)ERR_MSG(Z_MEM_ERROR);
</span><span class='line'>        deflateEnd (strm);
</span><span class='line'>        return Z_MEM_ERROR;
</span><span class='line'>    }
</span><span class='line'>    s-&gt;d_buf = overlay + s-&gt;lit_bufsize/sizeof(ush);
</span><span class='line'>    s-&gt;l_buf = s-&gt;pending_buf + (1+sizeof(ush))*s-&gt;lit_bufsize;
</span><span class='line'>
</span><span class='line'>    s-&gt;level = level;
</span><span class='line'>    s-&gt;strategy = strategy;
</span><span class='line'>    s-&gt;method = (Byte)method;
</span><span class='line'>
</span><span class='line'>    return deflateReset(strm);
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>上述代码也验证了他们这种关系。</p>

<p>那现在的问题就是找出哪里调用<code>Java_java_util_zip_Deflater_init</code>了，从这方法的命名上知道它是一个java的native方法实现，对应的是<code>java.util.zip.Deflater</code>这个类的<code>init</code>方法，所以要知道<code>init</code>方法哪里被调用了，跟踪调用栈我们会想到btrace工具，但是btrace是通过插桩的方式来实现的，对于native方法是无法插桩的，于是我们看调用它的地方，找到对应的方法，然后进行btrace脚本编写：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>import com.sun.btrace.annotations.*;
</span><span class='line'>import static com.sun.btrace.BTraceUtils.*;
</span><span class='line'>
</span><span class='line'>@BTrace public class Test {
</span><span class='line'>    @OnMethod(
</span><span class='line'>        clazz="java.util.zip.Deflater",
</span><span class='line'>        method="&lt;init&gt;"
</span><span class='line'>    )
</span><span class='line'>    public static void onnewThread(int i,boolean b) {
</span><span class='line'>        jstack();
</span><span class='line'>     }
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>于是跟踪对应的进程，我们能抓到调用Deflater构造函数的堆栈</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>org.apache.commons.compress.compressors.deflate.DeflateCompressorOutputStream.&lt;init&gt;(DeflateCompressorOutputStream.java:47)
</span><span class='line'>com.xxx.unimsg.parse.util.CompressUtil.deflateCompressAndEncode(CompressUtil.java:199)
</span><span class='line'>com.xxx.unimsg.parse.util.CompressUtil.compress(CompressUtil.java:80)
</span><span class='line'>com.xxx.unimsg.UnifyMessageHelper.compressXml(UnifyMessageHelper.java:65)
</span><span class='line'>com.xxx.core.model.utils.UnifyMessageUtil.compressXml(UnifyMessageUtil.java:56)
</span><span class='line'>com.xxx.repository.convert.BatchInDetailConvert.convertDO(BatchInDetailConvert.java:57)
</span><span class='line'>com.xxx.repository.impl.IncomingDetailRepositoryImpl$1.store(IncomingDetailRepositoryImpl.java:43)
</span><span class='line'>com.xxx.repository.helper.IdempotenceHelper.store(IdempotenceHelper.java:27)
</span><span class='line'>com.xxx.repository.impl.IncomingDetailRepositoryImpl.store(IncomingDetailRepositoryImpl.java:40)
</span><span class='line'>sun.reflect.GeneratedMethodAccessor274.invoke(Unknown Source)
</span><span class='line'>sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
</span><span class='line'>java.lang.reflect.Method.invoke(Method.java:597)
</span><span class='line'>org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
</span><span class='line'>org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
</span><span class='line'>org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
</span><span class='line'>com.alipay.finsupport.component.monitor.MethodMonitorInterceptor.invoke(MethodMonitorInterceptor.java:45)
</span><span class='line'>org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
</span><span class='line'>org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
</span><span class='line'>...</span></code></pre></td></tr></table></div></figure>


<p>从上面的堆栈我们找出了调用<code>java.util.zip.Deflate.init()</code>的地方</p>

<h2>问题解决</h2>

<p>上面已经定位了具体的代码了，于是再细致跟踪了下对应的代码，其实并不是代码实现上的问题，而是代码设计上没有考虑到流量很大的场景，当流量很大的时候，不管自己系统是否能承受这么大的压力，都来者不拒，拿到数据就做deflate，而这个过程是需要分配堆外内存的，当量达到一定程度的时候此时会发生oom killer，另外我们在分析过程中发现其实物理内存是有下降的</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>30071.txt:     0.0   0.0% 100.0%     96.7  57.0% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.1   0.0%  99.9%    196.0  72.6% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.1   0.0%  99.9%    290.3  78.5% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.1   0.0%  99.9%    392.7  83.6% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.2   0.0%  99.9%    592.8  88.5% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.2   0.0%  99.9%    700.7  91.0% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.3   0.0%  99.9%    799.1  91.9% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.3   0.0%  99.9%    893.9  92.2% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.0   0.0%  99.9%    114.2  63.7% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.0   0.0% 100.0%    105.1  52.1% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.2   0.0%  99.9%    479.7  87.4% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.3   0.0%  99.9%    782.2  90.1% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.3   0.0%  99.9%    986.9  92.3% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.4   0.0%  99.9%   1086.3  92.9% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.4   0.0%  99.9%   1185.1  93.3% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.3   0.0%  99.9%    941.5  92.1% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.4   0.0% 100.0%   1288.8  94.1% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.5   0.0% 100.0%   1394.8  94.9% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.5   0.0% 100.0%   1492.5  95.1% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.5   0.0% 100.0%   1591.0  95.3% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.3   0.0%  99.9%    874.6  90.0% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.3   0.0%  99.9%    950.7  92.8% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.3   0.0%  99.9%    858.4  92.3% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.3   0.0%  99.9%    818.4  91.9% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.3   0.0%  99.9%    858.7  91.2% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.1   0.0%  99.9%    271.5  77.9% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.4   0.0%  99.9%   1260.4  93.1% Java_java_util_zip_Deflater_init
</span><span class='line'>30071.txt:     0.3   0.0%  99.9%    976.4  90.6% Java_java_util_zip_Deflater_init</span></code></pre></td></tr></table></div></figure>


<p>这也就说明了其实代码使用上并没有错，因此建议将deflate放到队列里去做，比如限制队列大小是100，每次最多100个数据可以被deflate，处理一个放进一个，以至于不会被活活撑死。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之FinalReference完全解读]]></title>
    <link href="http://nijiaben.github.io/blog/2015/07/09/final-reference/"/>
    <updated>2015-07-09T14:35:31+08:00</updated>
    <id>http://nijiaben.github.io/blog/2015/07/09/final-reference</id>
    <content type="html"><![CDATA[<p><code>注:文章首发于InfoQ：</code><a href="" title="http://www.infoq.com/cn/articles/jvm-source-code-analysis-finalreference">JVM源码分析之FinalReference</a></p>

<h2>概述</h2>

<p>JAVA对象引用体系除了强引用之外，出于对性能、可扩展性等方面考虑还特地实现了四种其他引用：SoftReference、WeakReference、PhantomReference、FinalReference，本文主要想讲的是FinalReference，因为我们在使用内存分析工具比如zprofiler、mat等在分析一些oom的heap的时候，经常能看到 <code>java.lang.ref.Finalizer</code>占用的内存大小远远排在前面，而这个类占用的内存大小又和我们这次的主角<code>FinalReference</code>有着密不可分的关系。</p>

<!--more-->


<p>对于FinalReference及关联的内容，我们可能有如下印象：
* 自己代码里从没有使用过
* 线程dump之后，我们能看到一个叫做<code>Finalizer</code>的java线程
* 偶尔能注意到<code>java.lang.ref.Finalizer</code>的存在
* 我们在类里可能会写finalize方法</p>

<p>那FinalReference到底存在的意义是什么，以怎样的形式和我们的代码相关联呢，这是本文要理清的问题。</p>

<h2>JDK中的FinalReference</h2>

<p>首先我们看看FinalReference在JDK里的实现：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>class FinalReference&lt;T&gt; extends Reference&lt;T&gt; {
</span><span class='line'>
</span><span class='line'>    public FinalReference(T referent, ReferenceQueue&lt;? super T&gt; q) {
</span><span class='line'>        super(referent, q);
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>大家应该注意到了类访问权限是package的，这也就意味着我们不能直接去对其进行扩展，但是JDK里对此类进行了扩展实现<code>java.lang.ref.Finalizer</code>，这个类也是我们在概述里提到的，而此类的访问权限也是package的，并且是final的，意味着真的不能被扩展了，接下来的重点我们围绕<code>java.lang.ref.Finalizer</code>展开(PS：后续讲Finalizer相关的其实也就是在说FinalReference)</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>final class Finalizer extends FinalReference { /* Package-private; must be in
</span><span class='line'>                                                  same package as the Reference
</span><span class='line'>                                                  class */
</span><span class='line'>
</span><span class='line'>    /* A native method that invokes an arbitrary object's finalize method is
</span><span class='line'>       required since the finalize method is protected
</span><span class='line'>     */
</span><span class='line'>    static native void invokeFinalizeMethod(Object o) throws Throwable;
</span><span class='line'>
</span><span class='line'>    private static ReferenceQueue queue = new ReferenceQueue();
</span><span class='line'>    private static Finalizer unfinalized = null;
</span><span class='line'>    private static final Object lock = new Object();
</span><span class='line'>
</span><span class='line'>    private Finalizer
</span><span class='line'>        next = null,
</span><span class='line'>        prev = null;
</span><span class='line'>        
</span><span class='line'>    private Finalizer(Object finalizee) {
</span><span class='line'>        super(finalizee, queue);
</span><span class='line'>        add();
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    /* Invoked by VM */
</span><span class='line'>    static void register(Object finalizee) {
</span><span class='line'>        new Finalizer(finalizee);
</span><span class='line'>    }  
</span><span class='line'>    
</span><span class='line'>    private void add() {
</span><span class='line'>        synchronized (lock) {
</span><span class='line'>            if (unfinalized != null) {
</span><span class='line'>                this.next = unfinalized;
</span><span class='line'>                unfinalized.prev = this;
</span><span class='line'>            }
</span><span class='line'>            unfinalized = this;
</span><span class='line'>        }
</span><span class='line'>    }
</span><span class='line'>      
</span><span class='line'>    ...
</span><span class='line'>    
</span><span class='line'>   }    
</span></code></pre></td></tr></table></div></figure>


<h3>Finalizer的构造函数</h3>

<p>从构造函数上我们获得下面的几个关键信息
* private：意味着我们在外面无法自己构建这类对象
* finalizee参数：FinalReference指向的对象引用
* 调用add方法：将当前对象插入到Finalizer对象链里，链里的对象和Finalizer类静态相关联，言外之意是在这个链里的对象都无法被gc掉，除非将这种引用关系剥离掉（因为Finalizer类无法被unload）</p>

<p>虽然外面无法创建Finalizer对象，但是注意到有一个register的静态方法，在方法里会创建这种对象，同时将这个对象加入到Finalizer对象链里，这个方法是被vm调用的，那么问题来了，vm在什么情况下会调用这个方法呢？</p>

<h2>Finalizer对象何时被注册到Finalizer对象链里</h2>

<p>类其实有挺多的修饰，比如final，abstract，public等等，如果一个类有final修饰，我们就说这个类是一个final类，上面列的都是语法层面我们可以显示标记的，在jvm里其实还给类标记其他一些符号，比如finalizer，表示这个类是一个finalizer类（为了和java.lang.ref.Fianlizer类进行区分，下文要提到的finalizer类的地方都说成f类），gc在处理这种类的对象的时候要做一些特殊的处理，如在这个对象被回收之前会调用一下它的finalize方法。</p>

<h3>如何判断一个类是不是一个f类</h3>

<p>在讲这个问题之前，我们先来看下<code>java.lang.Object</code>里的一个方法</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>protected void finalize() throws Throwable { }</span></code></pre></td></tr></table></div></figure>


<p>在Object类里定义了一个名为finalize的空方法，这意味着Java世界里的所有类都会继承这个方法，甚至可以覆写该方法，并且根据方法覆写原则，如果子类覆盖此方法，方法访问权限都是至少是protected级别的，这样其子类就算没有覆写此方法也会继承此方法。</p>

<p>而判断当前类是否是一个f类的标准并不仅仅是当前类是否含有一个参数为空，返回值为void的名为finalize的方法，而另外一个要求是<code>finalize方法必须非空</code>，因此我们的Object类虽然含有一个finalize方法，但是并不是一个f类，Object的对象在被gc回收的时候其实并不会去调用它的finalize方法。</p>

<p>需要注意的是我们的类在被加载过程中其实就已经被标记为是否为f类了（遍历所有方法，包括父类的方法，只要有一个非空的参数为空返回void的finalize方法就认为是一个f类）。</p>

<h3>f类的对象何时传到Finalizer.register方法</h3>

<p>对象的创建其实是被拆分成多个步骤的，比如<code>A a=new A(2)</code>这样一条语句对应的字节码如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>0: new           #1                  // class A
</span><span class='line'>3: dup
</span><span class='line'>4: iconst_2
</span><span class='line'>5: invokespecial #11                 // Method "&lt;init&gt;":(I)V</span></code></pre></td></tr></table></div></figure>


<p>先执行new分配好对象空间，然后再执行invokespecial调用构造函数，jvm里其实可以让用户选择在这两个时机中的任意一个将当前对象传递给Finalizer.register方法来注册到Finalizer对象链里，这个选择依赖于RegisterFinalizersAtInit这个vm参数是否被设置，默认值为true，也就是在调用构造函数返回之前调用Finalizer.register方法，如果通过-XX:-RegisterFinalizersAtInit关闭了该参数，那将在对象空间分配好之后就将这个对象注册进去。</p>

<p>另外需要提一点的是当我们通过clone的方式复制一个对象的时候，如果当前类是一个f类，那么在clone完成的时候将调用Finalizer.register方法进行注册。</p>

<h3>hotspot如何实现f类对象在构造函数执行完毕后调用Finalizer.register</h3>

<p>这个实现比较有意思，在这里简单提一下，我们知道一个构造函数执行的时候，会去调用父类的构造函数，主要是为了能对继承自父类的属性也能做初始化，那么任何一个对象的初始化最终都会调用到Object的空构造函数里（任何空的构造函数其实并不空，会含有三条字节码指令，如下代码所示），为了不对所有的类的构造函数都做埋点调用Finalizer.register方法，hotspot的实现是在Object这个类在做初始化的时候将构造函数里的<code>return</code>指令替换为<code>_return_register_finalizer</code>指令，该指令并不是标准的字节码指令，是hotspot扩展的指令，这样在处理该指令的时候调用Finalizer.register方法，这样就在侵入性很小的情况下完美地解决了这个问题。</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>0: aload_0
</span><span class='line'>1: invokespecial #21                 // Method java/lang/Object."&lt;init&gt;":()V
</span><span class='line'>4: return</span></code></pre></td></tr></table></div></figure>


<h2>f类对象的GC回收</h2>

<h3>FinalizerThread线程</h3>

<p>在Finalizer类的clinit方法（静态块）里我们看到它会创建了一个FinalizerThread的守护线程，这个线程的优先级并不是最高的，意味着在cpu很紧张的情况下其被调度的优先级可能会受到影响</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>  private static class FinalizerThread extends Thread {
</span><span class='line'>        private volatile boolean running;
</span><span class='line'>        FinalizerThread(ThreadGroup g) {
</span><span class='line'>            super(g, "Finalizer");
</span><span class='line'>        }
</span><span class='line'>        public void run() {
</span><span class='line'>            if (running)
</span><span class='line'>                return;
</span><span class='line'>            running = true;
</span><span class='line'>            for (;;) {
</span><span class='line'>                try {
</span><span class='line'>                    Finalizer f = (Finalizer)queue.remove();
</span><span class='line'>                    f.runFinalizer();
</span><span class='line'>                } catch (InterruptedException x) {
</span><span class='line'>                    continue;
</span><span class='line'>                }
</span><span class='line'>            }
</span><span class='line'>        }
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    static {
</span><span class='line'>        ThreadGroup tg = Thread.currentThread().getThreadGroup();
</span><span class='line'>        for (ThreadGroup tgn = tg;
</span><span class='line'>             tgn != null;
</span><span class='line'>             tg = tgn, tgn = tg.getParent());
</span><span class='line'>        Thread finalizer = new FinalizerThread(tg);
</span><span class='line'>        finalizer.setPriority(Thread.MAX_PRIORITY - 2);
</span><span class='line'>        finalizer.setDaemon(true);
</span><span class='line'>        finalizer.start();
</span><span class='line'>    }</span></code></pre></td></tr></table></div></figure>


<p>这个线程主要就是从queue里取Finalizer对象，然后执行该对象的runFinalizer方法，这个方法主要是将Finalizer对象从Finalizer对象链里剥离出来，这样意味着下次gc发生的时候就可能将其关联的f对象gc掉了，最后将这个Finalizer对象关联的f对象传给了一个native方法invokeFinalizeMethod</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>private void runFinalizer() {
</span><span class='line'>        synchronized (this) {
</span><span class='line'>            if (hasBeenFinalized()) return;
</span><span class='line'>            remove();
</span><span class='line'>        }
</span><span class='line'>        try {
</span><span class='line'>            Object finalizee = this.get();
</span><span class='line'>            if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
</span><span class='line'>                invokeFinalizeMethod(finalizee);
</span><span class='line'>                /* Clear stack slot containing this variable, to decrease
</span><span class='line'>                   the chances of false retention with a conservative GC */
</span><span class='line'>                finalizee = null;
</span><span class='line'>            }
</span><span class='line'>        } catch (Throwable x) { }
</span><span class='line'>        super.clear();
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'> static native void invokeFinalizeMethod(Object o) throws Throwable;
</span><span class='line'> </span></code></pre></td></tr></table></div></figure>


<p>其实invokeFinalizeMethod方法就是调了这个f对象的finalize方法，看到这里大家应该恍然大悟了，整个过程都串起来了</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>JNIEXPORT void JNICALL
</span><span class='line'>Java_java_lang_ref_Finalizer_invokeFinalizeMethod(JNIEnv *env, jclass clazz,
</span><span class='line'>                                                  jobject ob)
</span><span class='line'>{
</span><span class='line'>    jclass cls;
</span><span class='line'>    jmethodID mid;
</span><span class='line'>
</span><span class='line'>    cls = (*env)-&gt;GetObjectClass(env, ob);
</span><span class='line'>    if (cls == NULL) return;
</span><span class='line'>    mid = (*env)-&gt;GetMethodID(env, cls, "finalize", "()V");
</span><span class='line'>    if (mid == NULL) return;
</span><span class='line'>    (*env)-&gt;CallVoidMethod(env, ob, mid);
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<h3>f对象的finalize方法抛出异常会导致FinalizeThread退出吗</h3>

<p>不知道大家有没有想过如果f对象的finalize方法抛了一个没捕获的异常，这个FinalizerThread会不会退出呢，细心的读者看上面的代码其实就可以找到答案，在runFinalizer方法里对Throwable的异常都进行了捕获，因此不可能出现FinalizerThread因异常未捕获而退出的情况。</p>

<h3>f对象的finalize方法会执行多次吗</h3>

<p>如果我们在f对象的finalize方法里重新将当前对象赋值出去，变成可达对象，当这个f对象再次变成不可达的时候还会被执行finalize方法吗？答案是否定的，因为在执行完第一次finalize方法之后，这个f对象已经和之前的Finalizer对象关系剥离了，也就是下次gc的时候不会再发现Finalizer对象指向该f对象了，自然也就不会调用这个f对象的finalize方法了。</p>

<h3>Finalizer对象何时被放到ReferenceQueue里</h3>

<p>除了这里要说的环节之外，整个过程大家应该都比较清楚了。</p>

<p>当gc发生的时候，gc算法会判断f类对象是不是只被Finalizer类引用（f类对象被Finalizer对象引用，然后放到Finalizer对象链里），如果这个类仅仅被Finalizer对象引用的时候，说明这个对象在不久的将来会被回收了现在可以执行它的finalize方法了，于是会将这个Finalizer对象放到Finalizer类的ReferenceQueue里，但是这个f类对象其实并没有被回收，因为Finalizer这个类还对他们持有引用，在gc完成之前，jvm会调用ReferenceQueue里的lock对象的notify方法（当ReferenceQueue为空的时候，FinalizerThread线程会调用ReferenceQueue的lock对象的wait方法直到被jvm唤醒），此时就会执行上面FinalizeThread线程里看到的其他逻辑了。</p>

<h2>Finalizer导致的内存泄露</h2>

<p>这里举一个简单的例子，我们使用挺广的socket通信，SocksSocketImpl的父类其实就实现了finalize方法:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>/**
</span><span class='line'> * Cleans up if the user forgets to close it.
</span><span class='line'> */
</span><span class='line'>protected void finalize() throws IOException {
</span><span class='line'>      close();
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>其实这么做的主要目的是万一用户忘记关闭socket了，那么在这个对象被回收的时候能主动关闭socket来释放一些系统资源，但是如果真的是用户忘记关闭了，那这些socket对象可能因为FinalizeThread迟迟没有执行到这些socket对象的finalize方法，而导致内存泄露，这种问题我们碰到过多次，需要特别注意的是对于已经没有地方引用的这些f对象，并不会在最近的那一次gc里马上回收掉，而是会延迟到下一个或者下几个gc时才被回收，因为执行finalize方法的动作无法在gc过程中执行，万一finalize方法执行很长呢，所以只能在这个gc周期里将这个垃圾对象重新标活，直到执行完finalize方法从queue里删除，这样下次gc的时候就真的是漂浮垃圾了会被回收，因此给大家的一个建议是千万不要在运行期不断创建f对象，不然会很悲剧。</p>

<h2>Finalizer的客观评价</h2>

<p>上面的过程基本对Finalizer的实现细节进行完整剖析了，java里我们看到有构造函数，但是并没有看到析构函数一说，Finalizer其实是实现了析构函数的概念，我们在对象被回收前可以执行一些『收拾性』的逻辑，应该说是一个特殊场景的补充，但是这种概念的实现给我们的f对象生命周期以及gc等带来了一些影响：
* f对象因为Finalizer的引用而变成了一个临时的强引用，即使没有其他的强引用了，还是无法立即被回收
* f对象至少经历两次GC才能被回收，因为只有在FinalizerThread执行完了f对象的finalize方法的情况下才有可能被下次gc回收，而有可能期间已经经历过多次gc了，但是一直还没执行f对象的finalize方法
* cpu资源比较稀缺的情况下FinalizerThread线程有可能因为优先级比较低而延迟执行f对象的finalize方法
* 因为f对象的finalize方法迟迟没有执行，有可能会导致大部分f对象进入到old分代，此时容易引发old分代的gc，甚至fullgc，gc暂停时间明显变长
* f对象的finalize方法被调用了，但是这个对象其实还并没有被回收，虽然可能在不久的将来会被回收</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之堆外内存完全解读]]></title>
    <link href="http://nijiaben.github.io/blog/2015/05/12/direct-buffer/"/>
    <updated>2015-05-12T13:49:57+08:00</updated>
    <id>http://nijiaben.github.io/blog/2015/05/12/direct-buffer</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<h3>广义的堆外内存</h3>

<p>说到堆外内存，那大家肯定想到堆内内存，这也是我们大家接触最多的，我们在jvm参数里通常设置-Xmx来指定我们的堆的最大值，不过这还不是我们理解的Java堆，-Xmx的值是新生代和老生代的和的最大值，我们在jvm参数里通常还会加一个参数-XX:MaxPermSize来指定持久代的最大值，那么我们认识的Java堆的最大值其实是-Xmx和-XX:MaxPermSize的总和，在分代算法下，新生代，老生代和持久代是连续的虚拟地址，因为它们是一起分配的，那么剩下的都可以认为是堆外内存(广义的)了，这些包括了jvm本身在运行过程中分配的内存，codecache，jni里分配的内存，DirectByteBuffer分配的内存等等</p>

<h3>狭义的堆外内存</h3>

<p>而作为java开发者，我们常说的堆外内存溢出了，其实是狭义的堆外内存，这个主要是指java.nio.DirectByteBuffer在创建的时候分配内存，我们这篇文章里也主要是讲狭义的堆外内存，因为它和我们平时碰到的问题比较密切</p>

<!--more-->


<h2>JDK/JVM里DirectByteBuffer的实现</h2>

<p>DirectByteBuffer通常用在通信过程中做缓冲池，在mina，netty等nio框架中屡见不鲜，先来看看JDK里的实现：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>DirectByteBuffer(int cap) {                   // package-private
</span><span class='line'>
</span><span class='line'>    super(-1, 0, cap, cap);
</span><span class='line'>    boolean pa = VM.isDirectMemoryPageAligned();
</span><span class='line'>    int ps = Bits.pageSize();
</span><span class='line'>    long size = Math.max(1L, (long)cap + (pa ? ps : 0));
</span><span class='line'>    Bits.reserveMemory(size, cap);
</span><span class='line'>
</span><span class='line'>    long base = 0;
</span><span class='line'>    try {
</span><span class='line'>        base = unsafe.allocateMemory(size);
</span><span class='line'>    } catch (OutOfMemoryError x) {
</span><span class='line'>        Bits.unreserveMemory(size, cap);
</span><span class='line'>        throw x;
</span><span class='line'>    }
</span><span class='line'>    unsafe.setMemory(base, size, (byte) 0);
</span><span class='line'>    if (pa && (base % ps != 0)) {
</span><span class='line'>        // Round up to page boundary
</span><span class='line'>        address = base + ps - (base & (ps - 1));
</span><span class='line'>    } else {
</span><span class='line'>        address = base;
</span><span class='line'>    }
</span><span class='line'>    cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
</span><span class='line'>    att = null;
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>通过上面的构造函数我们知道，真正的内存分配是使用的Bits.reserveMemory方法</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>     static void reserveMemory(long size, int cap) {
</span><span class='line'>        synchronized (Bits.class) {
</span><span class='line'>            if (!memoryLimitSet && VM.isBooted()) {
</span><span class='line'>                maxMemory = VM.maxDirectMemory();
</span><span class='line'>                memoryLimitSet = true;
</span><span class='line'>            }
</span><span class='line'>            // -XX:MaxDirectMemorySize limits the total capacity rather than the
</span><span class='line'>            // actual memory usage, which will differ when buffers are page
</span><span class='line'>            // aligned.
</span><span class='line'>            if (cap &lt;= maxMemory - totalCapacity) {
</span><span class='line'>                reservedMemory += size;
</span><span class='line'>                totalCapacity += cap;
</span><span class='line'>                count++;
</span><span class='line'>                return;
</span><span class='line'>            }
</span><span class='line'>        }
</span><span class='line'>
</span><span class='line'>        System.gc();
</span><span class='line'>        try {
</span><span class='line'>            Thread.sleep(100);
</span><span class='line'>        } catch (InterruptedException x) {
</span><span class='line'>            // Restore interrupt status
</span><span class='line'>            Thread.currentThread().interrupt();
</span><span class='line'>        }
</span><span class='line'>        synchronized (Bits.class) {
</span><span class='line'>            if (totalCapacity + cap &gt; maxMemory)
</span><span class='line'>                throw new OutOfMemoryError("Direct buffer memory");
</span><span class='line'>            reservedMemory += size;
</span><span class='line'>            totalCapacity += cap;
</span><span class='line'>            count++;
</span><span class='line'>        }
</span><span class='line'>
</span><span class='line'>    }</span></code></pre></td></tr></table></div></figure>


<p>通过上面的代码我们知道可以通过-XX:MaxDirectMemorySize来指定最大的堆外内存，那么我们首先引入两个问题</p>

<ul>
<li>堆外内存默认是多大</li>
<li>为什么要主动调用System.gc()</li>
</ul>


<h3>堆外内存默认是多大</h3>

<p>如果我们没有通过-XX:MaxDirectMemorySize来指定最大的堆外内存，那么默认的最大堆外内存是多少呢，我们还是通过代码来分析</p>

<p>上面的代码里我们看到调用了sun.misc.VM.maxDirectMemory()</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> private static long directMemory = 64 * 1024 * 1024;
</span><span class='line'>
</span><span class='line'>    // Returns the maximum amount of allocatable direct buffer memory.
</span><span class='line'>    // The directMemory variable is initialized during system initialization
</span><span class='line'>    // in the saveAndRemoveProperties method.
</span><span class='line'>    //
</span><span class='line'>    public static long maxDirectMemory() {
</span><span class='line'>        return directMemory;
</span><span class='line'>    }</span></code></pre></td></tr></table></div></figure>


<p>看到上面的代码之后是不是误以为默认的最大值是64M？其实不是的，说到这个值得从java.lang.System这个类的初始化说起</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> /**
</span><span class='line'>     * Initialize the system class.  Called after thread initialization.
</span><span class='line'>     */
</span><span class='line'>    private static void initializeSystemClass() {
</span><span class='line'>
</span><span class='line'>        // VM might invoke JNU_NewStringPlatform() to set those encoding
</span><span class='line'>        // sensitive properties (user.home, user.name, boot.class.path, etc.)
</span><span class='line'>        // during "props" initialization, in which it may need access, via
</span><span class='line'>        // System.getProperty(), to the related system encoding property that
</span><span class='line'>        // have been initialized (put into "props") at early stage of the
</span><span class='line'>        // initialization. So make sure the "props" is available at the
</span><span class='line'>        // very beginning of the initialization and all system properties to
</span><span class='line'>        // be put into it directly.
</span><span class='line'>        props = new Properties();
</span><span class='line'>        initProperties(props);  // initialized by the VM
</span><span class='line'>
</span><span class='line'>        // There are certain system configurations that may be controlled by
</span><span class='line'>        // VM options such as the maximum amount of direct memory and
</span><span class='line'>        // Integer cache size used to support the object identity semantics
</span><span class='line'>        // of autoboxing.  Typically, the library will obtain these values
</span><span class='line'>        // from the properties set by the VM.  If the properties are for
</span><span class='line'>        // internal implementation use only, these properties should be
</span><span class='line'>        // removed from the system properties.
</span><span class='line'>        //
</span><span class='line'>        // See java.lang.Integer.IntegerCache and the
</span><span class='line'>        // sun.misc.VM.saveAndRemoveProperties method for example.
</span><span class='line'>        //
</span><span class='line'>        // Save a private copy of the system properties object that
</span><span class='line'>        // can only be accessed by the internal implementation.  Remove
</span><span class='line'>        // certain system properties that are not intended for public access.
</span><span class='line'>        sun.misc.VM.saveAndRemoveProperties(props);
</span><span class='line'>
</span><span class='line'>       ......
</span><span class='line'>       
</span><span class='line'>        sun.misc.VM.booted();
</span><span class='line'>    }</span></code></pre></td></tr></table></div></figure>


<p>上面这个方法在jvm启动的时候对System这个类做初始化的时候执行的，因此执行时间非常早，我们看到里面调用了<code>sun.misc.VM.saveAndRemoveProperties(props)</code>:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>     public static void saveAndRemoveProperties(Properties props) {
</span><span class='line'>        if (booted)
</span><span class='line'>            throw new IllegalStateException("System initialization has completed");
</span><span class='line'>
</span><span class='line'>        savedProps.putAll(props);
</span><span class='line'>
</span><span class='line'>        // Set the maximum amount of direct memory.  This value is controlled
</span><span class='line'>        // by the vm option -XX:MaxDirectMemorySize=&lt;size&gt;.
</span><span class='line'>        // The maximum amount of allocatable direct buffer memory (in bytes)
</span><span class='line'>        // from the system property sun.nio.MaxDirectMemorySize set by the VM.
</span><span class='line'>        // The system property will be removed.
</span><span class='line'>        String s = (String)props.remove("sun.nio.MaxDirectMemorySize");
</span><span class='line'>        if (s != null) {
</span><span class='line'>            if (s.equals("-1")) {
</span><span class='line'>                // -XX:MaxDirectMemorySize not given, take default
</span><span class='line'>                directMemory = Runtime.getRuntime().maxMemory();
</span><span class='line'>            } else {
</span><span class='line'>                long l = Long.parseLong(s);
</span><span class='line'>                if (l &gt; -1)
</span><span class='line'>                    directMemory = l;
</span><span class='line'>            }
</span><span class='line'>        }
</span><span class='line'>
</span><span class='line'>        // Check if direct buffers should be page aligned
</span><span class='line'>        s = (String)props.remove("sun.nio.PageAlignDirectMemory");
</span><span class='line'>        if ("true".equals(s))
</span><span class='line'>            pageAlignDirectMemory = true;
</span><span class='line'>
</span><span class='line'>        // Set a boolean to determine whether ClassLoader.loadClass accepts
</span><span class='line'>        // array syntax.  This value is controlled by the system property
</span><span class='line'>        // "sun.lang.ClassLoader.allowArraySyntax".
</span><span class='line'>        s = props.getProperty("sun.lang.ClassLoader.allowArraySyntax");
</span><span class='line'>        allowArraySyntax = (s == null
</span><span class='line'>                               ? defaultAllowArraySyntax
</span><span class='line'>                               : Boolean.parseBoolean(s));
</span><span class='line'>
</span><span class='line'>        // Remove other private system properties
</span><span class='line'>        // used by java.lang.Integer.IntegerCache
</span><span class='line'>        props.remove("java.lang.Integer.IntegerCache.high");
</span><span class='line'>
</span><span class='line'>        // used by java.util.zip.ZipFile
</span><span class='line'>        props.remove("sun.zip.disableMemoryMapping");
</span><span class='line'>
</span><span class='line'>        // used by sun.launcher.LauncherHelper
</span><span class='line'>        props.remove("sun.java.launcher.diag");
</span><span class='line'>    }</span></code></pre></td></tr></table></div></figure>


<p>如果我们通过-Dsun.nio.MaxDirectMemorySize指定了这个属性，只要它不等于-1，那效果和加了-XX:MaxDirectMemorySize一样的，如果两个参数都没指定，那么最大堆外内存的值来自于<code>directMemory = Runtime.getRuntime().maxMemory()</code>，这是一个native方法</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>JNIEXPORT jlong JNICALL
</span><span class='line'>Java_java_lang_Runtime_maxMemory(JNIEnv *env, jobject this)
</span><span class='line'>{
</span><span class='line'>    return JVM_MaxMemory();
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>JVM_ENTRY_NO_ENV(jlong, JVM_MaxMemory(void))
</span><span class='line'>  JVMWrapper("JVM_MaxMemory");
</span><span class='line'>  size_t n = Universe::heap()-&gt;max_capacity();
</span><span class='line'>  return convert_size_t_to_jlong(n);
</span><span class='line'>JVM_END
</span></code></pre></td></tr></table></div></figure>


<p>其中在我们使用CMS GC的情况下的实现如下，其实是新生代的最大值-一个survivor的大小+老生代的最大值，也就是我们设置的-Xmx的值里除去一个survivor的大小就是默认的堆外内存的大小了</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>size_t GenCollectedHeap::max_capacity() const {
</span><span class='line'>  size_t res = 0;
</span><span class='line'>  for (int i = 0; i &lt; _n_gens; i++) {
</span><span class='line'>    res += _gens[i]-&gt;max_capacity();
</span><span class='line'>  }
</span><span class='line'>  return res;
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>size_t DefNewGeneration::max_capacity() const {
</span><span class='line'>  const size_t alignment = GenCollectedHeap::heap()-&gt;collector_policy()-&gt;min_alignment();
</span><span class='line'>  const size_t reserved_bytes = reserved().byte_size();
</span><span class='line'>  return reserved_bytes - compute_survivor_size(reserved_bytes, alignment);
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>size_t Generation::max_capacity() const {
</span><span class='line'>  return reserved().byte_size();
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<h3>为什么要主动调用System.gc</h3>

<p>既然要调用System.gc，那肯定是想通过触发一次gc操作来回收堆外内存，不过我想先说的是堆外内存不会对gc造成什么影响(这里的System.gc除外)，但是堆外内存的回收其实依赖于我们的gc机制，首先我们要知道在java层面和我们在堆外分配的这块内存关联的只有与之关联的DirectByteBuffer对象了，它记录了这块内存的基地址以及大小，那么既然和gc也有关，那就是gc能通过操作DirectByteBuffer对象来间接操作对应的堆外内存了。DirectByteBuffer对象在创建的时候关联了一个PhantomReference，说到PhantomReference它其实主要是用来跟踪对象何时被回收的，它不能影响gc决策，但是gc过程中如果发现某个对象除了只有PhantomReference引用它之外，并没有其他的地方引用它了，那将会把这个引用放到java.lang.ref.Reference.pending队列里，在gc完毕的时候通知ReferenceHandler这个守护线程去执行一些后置处理，而DirectByteBuffer关联的PhantomReference是PhantomReference的一个子类，在最终的处理里会通过Unsafe的free接口来释放DirectByteBuffer对应的堆外内存块</p>

<p>JDK里ReferenceHandler的实现：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> private static class ReferenceHandler extends Thread {
</span><span class='line'>
</span><span class='line'>        ReferenceHandler(ThreadGroup g, String name) {
</span><span class='line'>            super(g, name);
</span><span class='line'>        }
</span><span class='line'>
</span><span class='line'>        public void run() {
</span><span class='line'>            for (;;) {
</span><span class='line'>
</span><span class='line'>                Reference r;
</span><span class='line'>                synchronized (lock) {
</span><span class='line'>                    if (pending != null) {
</span><span class='line'>                        r = pending;
</span><span class='line'>                        Reference rn = r.next;
</span><span class='line'>                        pending = (rn == r) ? null : rn;
</span><span class='line'>                        r.next = r;
</span><span class='line'>                    } else {
</span><span class='line'>                        try {
</span><span class='line'>                            lock.wait();
</span><span class='line'>                        } catch (InterruptedException x) { }
</span><span class='line'>                        continue;
</span><span class='line'>                    }
</span><span class='line'>                }
</span><span class='line'>
</span><span class='line'>                // Fast path for cleaners
</span><span class='line'>                if (r instanceof Cleaner) {
</span><span class='line'>                    ((Cleaner)r).clean();
</span><span class='line'>                    continue;
</span><span class='line'>                }
</span><span class='line'>
</span><span class='line'>                ReferenceQueue q = r.queue;
</span><span class='line'>                if (q != ReferenceQueue.NULL) q.enqueue(r);
</span><span class='line'>            }
</span><span class='line'>        }
</span><span class='line'>    }</span></code></pre></td></tr></table></div></figure>


<p></p>

<p>可见如果pending为空的时候，会通过lock.wait()一直等在那里，其中唤醒的动作是在jvm里做的，当gc完成之后会调用如下的方法VM_GC_Operation::doit_epilogue()，在方法末尾会调用lock的notify操作，至于pending队列什么时候将引用放进去的，其实是在gc的引用处理逻辑中放进去的，针对引用的处理后面可以专门写篇文章来介绍</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void VM_GC_Operation::doit_epilogue() {
</span><span class='line'>  assert(Thread::current()-&gt;is_Java_thread(), "just checking");
</span><span class='line'>  // Release the Heap_lock first.
</span><span class='line'>  SharedHeap* sh = SharedHeap::heap();
</span><span class='line'>  if (sh != NULL) sh-&gt;_thread_holds_heap_lock_for_gc = false;
</span><span class='line'>  Heap_lock-&gt;unlock();
</span><span class='line'>  release_and_notify_pending_list_lock();
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>void VM_GC_Operation::release_and_notify_pending_list_lock() {
</span><span class='line'>instanceRefKlass::release_and_notify_pending_list_lock(&_pending_list_basic_lock);
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>


<p>对于System.gc的实现，之前写了一篇文章来重点介绍，<a href="http://lovestblog.cn/blog/2015/05/07/system-gc/">JVM源码分析之SystemGC完全解读</a>，它会对新生代的老生代都会进行内存回收，这样会比较彻底地回收DirectByteBuffer对象以及他们关联的堆外内存，我们dump内存发现DirectByteBuffer对象本身其实是很小的，但是它后面可能关联了一个非常大的堆外内存，因此我们通常称之为『冰山对象』，我们做ygc的时候会将新生代里的不可达的DirectByteBuffer对象及其堆外内存回收了，但是无法对old里的DirectByteBuffer对象及其堆外内存进行回收，这也是我们通常碰到的最大的问题，如果有大量的DirectByteBuffer对象移到了old，但是又一直没有做cms gc或者full gc，而只进行ygc，那么我们的物理内存可能被慢慢耗光，但是我们还不知道发生了什么，因为heap明明剩余的内存还很多(前提是我们禁用了System.gc)。</p>

<h2>为什么要使用堆外内存</h2>

<p>DirectByteBuffer在创建的时候会通过Unsafe的native方法来直接使用malloc分配一块内存，这块内存是heap之外的，那么自然也不会对gc造成什么影响(System.gc除外)，因为gc耗时的操作主要是操作heap之内的对象，对这块内存的操作也是直接通过Unsafe的native方法来操作的，相当于DirectByteBuffer仅仅是一个壳，还有我们通信过程中如果数据是在Heap里的，最终也还是会copy一份到堆外，然后再进行发送，所以为什么不直接使用堆外内存呢。对于需要频繁操作的内存，并且仅仅是临时存在一会的，都建议使用堆外内存，并且做成缓冲池，不断循环利用这块内存。</p>

<h2>为什么不能大面积使用堆外内存</h2>

<p>如果我们大面积使用堆外内存并且没有限制，那迟早会导致内存溢出，毕竟程序是跑在一台资源受限的机器上，因为这块内存的回收不是你直接能控制的，当然你可以通过别的一些途径，比如反射，直接使用Unsafe接口等，但是这些务必给你带来了一些烦恼，Java与生俱来的优势被你完全抛弃了&mdash;开发不需要关注内存的回收，由gc算法自动去实现。另外上面的gc机制与堆外内存的关系也说了，如果一直触发不了cms gc或者full gc，那么后果可能很严重。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM源码分析之SystemGC完全解读]]></title>
    <link href="http://nijiaben.github.io/blog/2015/05/07/system-gc/"/>
    <updated>2015-05-07T20:02:51+08:00</updated>
    <id>http://nijiaben.github.io/blog/2015/05/07/system-gc</id>
    <content type="html"><![CDATA[<h2>概述</h2>

<p>JVM的GC一般情况下是JVM本身根据一定的条件触发的，不过我们还是可以做一些人为的触发，比如通过jvmti做强制GC，通过System.gc触发，还可以通过jmap来触发等，针对每个场景其实我们都可以写篇文章来做一个介绍，本文重点介绍下System.gc的原理</p>

<!--more-->


<p>或许大家已经知道如下相关的知识</p>

<ul>
<li>system.gc其实是做一次full gc</li>
<li>system.gc会暂停整个进程</li>
<li>system.gc一般情况下我们要禁掉，使用-XX:+DisableExplicitGC</li>
<li>system.gc在cms gc下我们通过-XX:+ExplicitGCInvokesConcurrent来做一次稍微高效点的GC(效果比Full GC要好些)</li>
<li>system.gc最常见的场景是RMI/NIO下的堆外内存分配等</li>
</ul>


<p>如果你已经知道上面这些了其实也说明你对System.gc有过一定的了解，至少踩过一些坑，但是你是否更深层次地了解过它，比如</p>

<ul>
<li>为什么CMS GC下-XX:+ExplicitGCInvokesConcurrent这个参数加了之后会比真正的Full GC好？</li>
<li>它如何做到暂停整个进程？</li>
<li>堆外内存分配为什么有时候要配合System.gc？</li>
</ul>


<p>如果你上面这些疑惑也都知道，那说明你很懂System.gc了，那么接下来的文字你可以不用看啦</p>

<h2>JDK里的System.gc的实现</h2>

<p>先贴段代码吧（java.lang.System）</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>/**
</span><span class='line'> * Runs the garbage collector.
</span><span class='line'> * &lt;p&gt;
</span><span class='line'> * Calling the &lt;code&gt;gc&lt;/code&gt; method suggests that the Java Virtual
</span><span class='line'> * Machine expend effort toward recycling unused objects in order to
</span><span class='line'> * make the memory they currently occupy available for quick reuse.
</span><span class='line'> * When control returns from the method call, the Java Virtual
</span><span class='line'> * Machine has made a best effort to reclaim space from all discarded
</span><span class='line'> * objects.
</span><span class='line'> * &lt;p&gt;
</span><span class='line'> * The call &lt;code&gt;System.gc()&lt;/code&gt; is effectively equivalent to the
</span><span class='line'> * call:
</span><span class='line'> * &lt;blockquote&gt;&lt;pre&gt;
</span><span class='line'> * Runtime.getRuntime().gc()
</span><span class='line'> * &lt;/pre&gt;&lt;/blockquote&gt;
</span><span class='line'> *
</span><span class='line'> * @see     java.lang.Runtime#gc()
</span><span class='line'> */
</span><span class='line'>public static void gc() {
</span><span class='line'>    Runtime.getRuntime().gc();
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>发现主要调用的是Runtime里的gc方法（java.lang.Runtime）</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>/**
</span><span class='line'> * Runs the garbage collector.
</span><span class='line'> * Calling this method suggests that the Java virtual machine expend
</span><span class='line'> * effort toward recycling unused objects in order to make the memory
</span><span class='line'> * they currently occupy available for quick reuse. When control
</span><span class='line'> * returns from the method call, the virtual machine has made
</span><span class='line'> * its best effort to recycle all discarded objects.
</span><span class='line'> * &lt;p&gt;
</span><span class='line'> * The name &lt;code&gt;gc&lt;/code&gt; stands for "garbage
</span><span class='line'> * collector". The virtual machine performs this recycling
</span><span class='line'> * process automatically as needed, in a separate thread, even if the
</span><span class='line'> * &lt;code&gt;gc&lt;/code&gt; method is not invoked explicitly.
</span><span class='line'> * &lt;p&gt;
</span><span class='line'> * The method {@link System#gc()} is the conventional and convenient
</span><span class='line'> * means of invoking this method.
</span><span class='line'> */
</span><span class='line'>public native void gc();</span></code></pre></td></tr></table></div></figure>


<p>这里看到gc方法是native的，在java层面只能到此结束了，代码只有这么多，要了解更多，可以看方法上面的注释，不过我们需要更深层次地来了解其实现，那还是准备好进入到jvm里去看看</p>

<h2>Hotspot里System.gc的实现</h2>

<h3>如何找到native里的实现</h3>

<p>上面提到了Runtime.gc是一个本地方法，那需要先在jvm里找到对应的实现，这里稍微提一下jvm里native方法最常见的也是最简单的查找，jdk里一般含有native方法的类，一般都会有一个对应的c文件，比如上面的java.lang.Runtime这个类，会有一个Runtime.c的文件和它对应，native方法的具体实现都在里面了，如果你有source，可能会猜到和下面的方法对应</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>JNIEXPORT void JNICALL
</span><span class='line'>Java_java_lang_Runtime_gc(JNIEnv *env, jobject this)
</span><span class='line'>{
</span><span class='line'>    JVM_GC();
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>其实没错的，就是这个方法，jvm要查找到这个native方法其实很简单的，看方法名可能也猜到规则了，Java_pkgName_className_methodName，其中pkgName里的".&ldquo;替换成&rdquo;_&ldquo;，这样就能找到了，当然规则不仅仅只有这么一个，还有其他的，这里不细说了，有机会写篇文章详细介绍下其中细节</p>

<h3>DisableExplicitGC参数</h3>

<p>上面的方法里是调用JVM_GC()，实现如下</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>JVM_ENTRY_NO_ENV(void, JVM_GC(void))
</span><span class='line'>  JVMWrapper("JVM_GC");
</span><span class='line'>  if (!DisableExplicitGC) {
</span><span class='line'>    Universe::heap()-&gt;collect(GCCause::_java_lang_system_gc);
</span><span class='line'>  }
</span><span class='line'>JVM_END</span></code></pre></td></tr></table></div></figure>


<p>看到这里我们已经解释其中一个疑惑了，就是<code>DisableExplicitGC</code>这个参数是在哪里生效的，起的什么作用，如果这个参数设置为true的话，那么将直接跳过下面的逻辑，我们通过-XX:+ DisableExplicitGC就是将这个属性设置为true，而这个属性默认情况下是true还是false呢</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>product(bool, DisableExplicitGC, false,                                   \
</span><span class='line'>          "Tells whether calling System.gc() does a full GC")    </span></code></pre></td></tr></table></div></figure>


<h3>ExplicitGCInvokesConcurrent参数</h3>

<p>这里主要针对CMSGC下来做分析，所以我们上面看到调用了heap的collect方法，我们找到对应的逻辑</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void GenCollectedHeap::collect(GCCause::Cause cause) {
</span><span class='line'>  if (should_do_concurrent_full_gc(cause)) {
</span><span class='line'>#ifndef SERIALGC
</span><span class='line'>    // mostly concurrent full collection
</span><span class='line'>    collect_mostly_concurrent(cause);
</span><span class='line'>#else  // SERIALGC
</span><span class='line'>    ShouldNotReachHere();
</span><span class='line'>#endif // SERIALGC
</span><span class='line'>  } else {
</span><span class='line'>#ifdef ASSERT
</span><span class='line'>    if (cause == GCCause::_scavenge_alot) {
</span><span class='line'>      // minor collection only
</span><span class='line'>      collect(cause, 0);
</span><span class='line'>    } else {
</span><span class='line'>      // Stop-the-world full collection
</span><span class='line'>      collect(cause, n_gens() - 1);
</span><span class='line'>    }
</span><span class='line'>#else
</span><span class='line'>    // Stop-the-world full collection
</span><span class='line'>    collect(cause, n_gens() - 1);
</span><span class='line'>#endif
</span><span class='line'>  }
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>bool GenCollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) {
</span><span class='line'>  return UseConcMarkSweepGC &&
</span><span class='line'>         ((cause == GCCause::_gc_locker && GCLockerInvokesConcurrent) ||
</span><span class='line'>          (cause == GCCause::_java_lang_system_gc && ExplicitGCInvokesConcurrent));
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>collect里一开头就有个判断，如果should_do_concurrent_full_gc返回true，那会执行collect_mostly_concurrent做并行的回收</p>

<p>其中should_do_concurrent_full_gc中的逻辑是如果使用CMS GC，并且是system gc且ExplicitGCInvokesConcurrent==true，那就做并行full gc，当我们设置-XX:+ ExplicitGCInvokesConcurrent的时候，就意味着应该做并行Full GC了，不过要注意千万不要设置-XX:+DisableExplicitGC，不然走不到这个逻辑里来了</p>

<h2>并行Full GC相对正常的Full GC效率高在哪里</h2>

<h3>stop the world</h3>

<p>说到GC，这里要先提到VMThread，在jvm里有这么一个线程不断轮询它的队列，这个队列里主要是存一些VM_operation的动作，比如最常见的就是内存分配失败要求做GC操作的请求等，在对gc这些操作执行的时候会先将其他业务线程都进入到安全点，也就是这些线程从此不再执行任何字节码指令，只有当出了安全点的时候才让他们继续执行原来的指令，因此这其实就是我们说的stop the world(STW)，整个进程相当于静止了</p>

<h3>CMS GC</h3>

<p>这里必须提到CMS GC，因为这是解释并行Full GC和正常Full GC的关键所在，CMS GC我们分为两种模式background和foreground，其中background顾名思义是在后台做的，也就是可以不影响正常的业务线程跑，触发条件比如说old的内存占比超过多少的时候就可能触发一次background式的cms gc，这个过程会经历CMS GC的所有阶段，该暂停的暂停，该并行的并行，效率相对来说还比较高，毕竟有和业务线程并行的gc阶段；而foreground则不然，它发生的场景比如业务线程请求分配内存，但是内存不够了，于是可能触发一次cms gc，这个过程就必须是要等内存分配到了线程才能继续往下面走的，因此整个过程必须是STW的，因此CMS GC整个过程都是暂停应用的，但是为了提高效率，它并不是每个阶段都会走的，只走其中一些阶段，这些省下来的阶段主要是并行阶段，Precleaning、AbortablePreclean，Resizing这几个阶段都不会经历，其中sweep阶段是同步的，但不管怎么说如果走了类似foreground的cms gc，那么整个过程业务线程都是不可用的，效率会影响挺大。CMS GC具体的过程后面再写文章详细说，其过程确实非常复杂的</p>

<h3>正常的Full GC</h3>

<p>正常的Full GC其实是整个gc过程包括ygc和cms gc(这里说的是真正意义上的Full GC，还有些场景虽然调用Full GC的接口，但是并不会都做，有些时候只做ygc，有些时候只做cms gc)都是由VMThread来执行的，因此整个时间是ygc+cms gc的时间之和，其中CMS GC是上面提到的foreground式的，因此整个过程会比较长，也是我们要避免的</p>

<h3>并行的Full GC</h3>

<p>并行Full GC也通样会做YGC和CMS GC，但是效率高就搞在CMS GC是走的background的，整个暂停的过程主要是YGC+CMS_initMark+CMS_remark几个阶段</p>

<h2>堆外内存常配合使用System GC</h2>

<p>这里说的堆外内存主要针对java.nio.DirectByteBuffer，这些对象的创建过程会通过Unsafe接口直接通过os::malloc来分配内存，然后将内存的起始地址和大小存到java.nio.DirectByteBuffer对象里，这样就可以直接操作这些内存。这些内存只有在DirectByteBuffer回收掉之后才有机会被回收，因此如果这些对象大部分都移到了old，但是一直没有触发CMS GC或者Full GC，那么悲剧将会发生，因为你的物理内存被他们耗尽了，因此为了避免这种悲剧的发生，通过-XX:MaxDirectMemorySize来指定最大的堆外内存大小，当使用达到了阈值的时候将调用System.gc来做一次full gc，以此来回收掉没有被使用的堆外内存，具体堆外内存是如何回收的，其原理机制又是怎样的，还是后面写篇详细的文章吧</p>
]]></content>
  </entry>
  
</feed>
