SegmentFault 好好学java最新的文章
2021-12-01T09:46:06+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
国企和互联网的那些二三事。。。
https://segmentfault.com/a/1190000041043862
2021-12-01T09:46:06+08:00
2021-12-01T09:46:06+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
3
<p>最近经常看到很多人讨论事业单位还是互联网好,其实,这个问题在我看来没有准确的答案,面对这两个选择时,每个人的选择都应该会不一样,所以,今天我主要来讲讲我对事业单位(或国企)和互联网的感受,给大家一些借鉴吧!</p><p>在大学刚刚毕业的时候,那个时候找了很久的工作,最终,找到了一家国企,具体是什么类型的就不说了,总的来说就是比较稳定的,不会有太大的压力那种。</p><p>在大学那会,我找到这份国企的工作其实对我自己来说是比较满意的,熟悉我的读者都知道,我本科的学历并不好,所以,当时,经过面试,找到一份国企的工作,对我自己也算是一种肯定了,后来,我也是带着一份憧憬去了这家国企工作。</p><p>国企给我的第一印象是充满激动和向往,激动更多的来自于这是我的第一份正式的工作,对于工作环境、同事以及工作的压力等充满了未知。因此,刚刚去公司的前几天我都是有一点不适应的,但是,还好当时的同事都特别好,去了之后给了我特别多的帮助与指导,让我很快的就融入了我们当初的那个团队。</p><p>当初我进的国企也算是很大的国企了,虽然当初没有给编制,但是也算是特别的稳定,只要长期的做下去,以后拿个编制还是没有很大的问题的,所以,我对这份工作还是充满了期待和向往的,毕竟,对于那时的我来说,找一份这样的工作还是十分不容易的,甚至,很多时候我会认为我是有运气加成才找到的。</p><p>首先,我还是来谈谈我当初的待的国企的作息时间吧。</p><p>在那里待的那段时间,机会每天的作息都是这样的,早上8.30上班,中午12点准时吃饭,然后,中午可以休息到2.30-3点左右,下午工作到6点,就可以下班了,这基本就是国企的标准作息时间,现在,我也有同学去了国企,基本的作息时间也大概是这样的,所以,从整个作息时间来看,还是比较轻松的,也是很多在互联网工作的人向往的作息时间,至少,不管怎么说,工作的时间还是比较短的。</p><p>这个作息时间,对我来说,最重要的是中午的休息时间和下午下班早,一般我中午都是有午休的习惯的,这一点可以保证我下午到晚上的工作效率,另外,下午一般6点下班,给了我很多自由的时间,可以进行自由支配,这也是十分完美的。</p><p>其次,还想谈谈国企工作饮食方面。</p><p>当时,我所在的国企和分公司是有单独的食堂的,所以我觉得还是不错的,当然,这也可能是受限于当时我的认知,觉得这一点很加分,但是,回到现在,我去过腾讯工作,经历也更加丰富了,回想当初的经历,我依然觉得那时候的饮食方面,已经是不错的待遇了。</p><p>因为有食堂的原因,三餐都可以在公司解决,而且也不贵,这应该是给员工的福利,最重要的是,每一餐的伙食真的不错,那个时候每天吃的特别多,每天的菜的种类也是不重复的,可能在那里工作的时候都重了好几斤。</p><p>再次,国企的工作环境和压力。</p><p>这么说吧,国企的工作环境是非常不错的,当初我们部门虽然人不多,但是,我们的整个部门的工作环境是很好的,回想当初,可能跟研究生的实验室旗鼓相当。</p><p>也许工作压力才是大家关系的问题,毕竟互联网是压力十分大的,每天的紧张程度也是很高,那么,对于国企来说,压力,在我看来是没有的,当然,这也不能一概而论,但至少整体的情况就是这样的。</p><p>当初我在的国企,每天干的活其实是非常轻松的,整体就是对一些系统的维护的工作,甚至,很多时候这些活也不需要干,一天开开会,坐在办公室聊聊天一天就没了,是不是挺美滋滋的。</p><p>可能大家怀疑是不是每个国企都是这样的,在我看来,任何事情都不可能是一样的,肯定是有一定的差别的,但是,从整体看,和互联网比较肯定是一个天一个地。现在,我也有同学研究生毕业去了国企工作,从聊天中就可以看出来他的潇洒,每天下午就是等着5.30下班,这也许就是生活吧。</p><p>第四,国企的技术方面。</p><p>作为我们技术人员来说,可能会关注技术的提升。据我的了解以及当初我的个人经历,国企的技术一般是比较落后的,跟互联网肯定是没有办法相提并论的,就算是你去一些银行的技术部门工作,一些技术的更新也是较为缓慢的,就更别说国企了。</p><p>拿我当初在的国企举个例子吧,我在那里工作时,一项基本的工作就是维护一个网上商城,有问题的时候修复一下,或者解决一些历史遗留下来的问题。那时候也比较闲,就看了看项目的源码,发现使用的框架还是Struts,还不是Struts2,而是Struts1,还好当时我学过这个比较古老的框架,这个框架拿到现在来说简直是古董,因为一些漏洞,这个框架现在基本上被弃用了。</p><p>所以,大家就应该知道国企用的技术都是什么了吧。</p><p>因此,我想说的是,如果你选择进入到国企,技术方面肯定是要落后的,跟最新的技术前沿会有很大的差距,就算有一些项目的开发工作,对你的技术方面的能力也不会有很大的提升,而且技术也是比较老的。</p><p>第五,国企的晋升发展。</p><p>这一点,可能还是看你所在的国企的类型吧,但是,就我所看到的,在国企中,人脉可能会大于你的能力。</p><p>为什么这么说呢?</p><p>想必大家也都明白,当初我所在的国企,里面的人际关系其实非常明显,也是非常复杂的,这么说你应该明白吧,如果你没有好的人脉,想往上晋升是比较困难的。这一点与互联网有很大的区别,并不是能力第一!</p><p>最后,对国企(事业单位)和互联网做一个小的总结。</p><p>在我看来,国企意味着稳定、轻松、技术停滞、工资一般、发展困难;而互联网意味着奋斗、压力山大、技术提升快、工资高、发展潜力大。</p><p>这两种工作是完全不同的,选择国企意味着稳定压力小,发展机会不大;选择互联网意味着压力大,发展潜力无限大。</p><p>因此,当大家做选择的时候,一定得想清楚你想要的是什么,如果你要的是稳定,一眼就看到头的生活,那大可选择国企(事业单位);如果你想要的是发展,拼搏与奋斗,互联网一定是你的菜。选择没有好与坏,只有你喜不喜欢,在我看来,快乐是生活的一切,一切都是为了每天能快乐!</p><p>因为我喜欢拼搏,喜欢充满未知,喜欢高薪,希望通过自己的双手改变生活的轨迹,所以,我选择了互联网!</p><p>那么,你也会有你的选择!</p>
回溯算法的题目,这样做,秒杀!!
https://segmentfault.com/a/1190000023033118
2020-06-28T09:54:51+08:00
2020-06-28T09:54:51+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
4
<blockquote>点个赞,看一看,好习惯!本文 <strong>GitHub</strong> <a href="https://link.segmentfault.com/?enc=pBVk2Ix9325XLXZU0uI5CQ%3D%3D.Jkm4SF2JhwVENvgXpRK4hgAoutcZLgdpu0jOdxHWhkjltsM0SEZ8X6pG0jLUM1eF" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a> 已收录,这是我花了 3 个月总结的一线大厂 Java 面试总结,本人已拿大厂 offer。<br>另外,原创文章首发在我的个人博客:<a href="https://link.segmentfault.com/?enc=xjY3qDOzsSzSje%2B%2BvxY4Cw%3D%3D.WWB%2BUJouy6zrg9dvQWaq%2FU8oo%2B%2FRgBckstlWBrHNqJw%3D" rel="nofollow">blog.ouyangsihai.cn</a>,欢迎访问。</blockquote>
<p>这一篇文章来讲解一下如何做leetcode回溯算法题目,这一段时间我把leetcode上面的回溯算法的题目都刷了个遍,发现了其中一些规律,所以,就想写一篇文章来总结一下,怕以后忘记。</p>
<p>刷完回溯算法的题目,我发现其实可以总结为三大类:子集问题、组合问题、排列问题,那这三大类都是什么意思呢,我分别举一个例子来说明。</p>
<p><strong>子集问题</strong>,比如说,数组<code>[1,2,3]</code>,那么对应的子集问题就是,这个数组的子集有:<code>[],[1],[2],[3],[1,3],[2,3],[1,2],[1,2,3]</code>,这就是这个数组的子集,这一类问题在leetcode上面很多个,而且有些题目数组中的元素是可以重复的,然后来求子集问题。</p>
<p><strong>组合问题</strong>,比如说,数组<code>[1,2,3]</code>,组合出target为3的可能的选择,那么就有:<code>[1,2],[3]</code>,这就是leetcode中的组合问题。</p>
<p><strong>排列问题</strong>,排列问题就比较简单了,比如,我们常见的全排列问题,leetcode也有这一类问题。</p>
<p>这篇文章,我们就来讲讲,怎么用回溯的算法去解决这些问题。</p>
<p>另外,这些知识的话,我都写了原创文章,比较系统的讲解了,大家可以看看,会有一定得收获的。</p>
<table>
<thead><tr>
<th>序号</th>
<th>原创精品</th>
</tr></thead>
<tbody>
<tr>
<td>1</td>
<td><a href="https://link.segmentfault.com/?enc=XnefI3d0SkhO6oM1%2FH%2FGMA%3D%3D.YPu2tNYnDwHjo83YTAdIuGNCgpSoC9S37Psy3u6LSZuXflRo0zANctXzev1yoL4IEEn9ppVoOEG8qEepQNB17%2BRXQdDKQorzKJ2Grw4SHT0%3D" rel="nofollow">【原创】分布式架构系列文章</a></td>
</tr>
<tr>
<td>2</td>
<td><a href="https://link.segmentfault.com/?enc=gBBsfusxRpks3VwC2ZMpVw%3D%3D.j4g4bO%2FBWv1kss6nUv8SCuMuLs5h%2BhyQZ7QzSChsUY528Ufz4vo4Sr5u0siqqnDieAE%2BcsA8j0t166s11p29Qm66aUD7k77qqo6Gz4iK8sM%3D" rel="nofollow">【原创】实战 Activiti 工作流教程</a></td>
</tr>
<tr>
<td>3</td>
<td><a href="https://link.segmentfault.com/?enc=zcN8yHNFdwHiAjQz%2FAqzEg%3D%3D.uUB6oObhWr%2FMtrj2gh9JH0Rx7b%2FTYF1LH4Uc5rBdkzBHGclGYePLtVDlIv%2FszptXcM7bd9bsY1Bgk93Z6Npmv7Ap9jUTzCx7VG7V21EM2uo%3D" rel="nofollow">【原创】深入理解Java虚拟机教程</a></td>
</tr>
<tr>
<td>4</td>
<td><a href="https://link.segmentfault.com/?enc=i8Qhr6WGNojIrJp%2F2s3ZSQ%3D%3D.9W6kWaF0OrQM4shPfcu%2B5%2FubHzkb0umCn75xhXxDJQMv3OwfnZ0r3vfeieW68lZdcupHvOvh0q33UCnVSiN49lO603xYNKuFh3k11d5mz94%3D" rel="nofollow">【原创】Java8最新教程 </a></td>
</tr>
<tr>
<td>5</td>
<td><a href="https://link.segmentfault.com/?enc=rEm28E6Tj6ZRTNdvgIIOpg%3D%3D.dNBDumv4WJe%2BBymTdLWiqUzivACKew1xNFpC7xZUKvKefTITBDiKe4NHSEeNPqxsrbwFcMjNH1W%2FJP%2FnfQjetApdT80KmwLZdP7e%2BPYIfyPta513ffX%2FOVYnx8xz5mS3" rel="nofollow">【原创】MySQL的艺术世界</a></td>
</tr>
</tbody>
</table>
<h3>1 一步一步讲解回溯算法框架</h3>
<p>最开始,我还是想通过一个简单的例子,一步一步的带大家看一下回溯算法的题目应该是怎么一步一步解决的,最终,通过这个题目,我们就可以大致的整理出一个回溯算法的解题框架;先来看下面这个题目,是一个子集的题目,题目难度中等。</p>
<p><img src="/img/bVbIN7L" alt="" title=""></p>
<p>这个题目,题目给的框架是这样的。</p>
<pre><code class="java"> public List<List<Integer>> subsets(int[] nums) {
}</code></pre>
<p>所以,我们就知道,我们先构建一个<code>List<List<Integer>></code>类型的返回值。</p>
<pre><code class="java"> List<List<Integer>> list = new ArrayList<>();</code></pre>
<p>接下来,我们就开始写回溯方法。</p>
<pre><code class="java"> public void backTrace(int start, int[] nums, List<Integer> temp){
for(int j = 0; j < nums.length; j++){
temp.add(nums[j]);
backTrace(j+1,nums,temp);
temp.remove(temp.size()-1);
}
}</code></pre>
<p>最开始,可能写成上面这个样子,传入数组<code>nums</code>,<code>start</code>和<code>temp集合</code>用于保存结果,然后,每次遍历数组nums的时候,都加入当前元素,在递归回来的时候再回溯,删除刚刚加入的元素,这不就是回溯的思想吗。</p>
<p>这样把基本的框架写完了,还有一个需要思考的问题就是<strong>base case</strong>,那么这个题目的base case是什么呢?其实,因为是子集,每一步都是需要加入到结果集合temp的,所以就没有什么限制条件了。</p>
<pre><code class="java"> public void backTrace(int start, int[] nums, List<Integer> temp){
//每次都保存结果
list.add(new ArrayList<>(temp));
for(int j = 0; j < nums.length; j++){
temp.add(nums[j]);
backTrace(j+1,nums,temp);
temp.remove(temp.size()-1);
}
}</code></pre>
<p>最后,我们再补充完整一下,就完整的代码出来了。</p>
<pre><code class="java"> List<List<Integer>> list = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
if(nums.length == 0){
return null;
}
List<Integer> temp = new ArrayList<>();
backTrace(0, nums, temp);
return list;
}
public void backTrace(int start, int[] nums, List<Integer> temp){
list.add(new ArrayList<>(temp));
for(int j = 0; j < nums.length; j++){
temp.add(nums[j]);
backTrace(j+1,nums,temp);
temp.remove(temp.size()-1);
}
}</code></pre>
<p>ok,我们去运行一下,看看如何。</p>
<p><img src="/img/bVbIN7M" alt="" title=""></p>
<p>他说我超出时间限制,说明算法是有问题的,我们再看一下上面我们写的代码,我们发现,其实我们每次遍历数组的时候都是从0开始遍历的,导致很多重复的元素遍历了,也就是我们得<code>start</code>变量并没有用到,最后,我们把遍历的时候不每次从0开始,而是从当前的start开始遍历,<strong>选过的元素我们排除</strong>,看一下结果。</p>
<pre><code class="java"> List<List<Integer>> list = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
if(nums.length == 0){
return null;
}
List<Integer> temp = new ArrayList<>();
backTrace(0, nums, temp);
return list;
}
public void backTrace(int start, int[] nums, List<Integer> temp){
list.add(new ArrayList<>(temp));
//从start开始遍历,避免重复
for(int j = start; j < nums.length; j++){
temp.add(nums[j]);
backTrace(j+1,nums,temp);
temp.remove(temp.size()-1);
}
}</code></pre>
<p>发现完美通过,good job!!</p>
<p><img src="/img/bVbIN7N" alt="" title=""></p>
<p>另外,我们要注意一个点就是:<code>list.add(new ArrayList<>(temp))</code>;不要写成<code>list.add(temp);</code>,否则,输出的结果就是空集,你思考一下应该就知道为什么了。</p>
<p>通过,这个题目,其实,我们就把回溯算法的一个大致的框架可以整理出来了,以后做其他题目,照猫画虎,一顿操作就可以了。</p>
<p>回到<strong>backTrace</strong>函数,其实就是一个<strong>选择/撤销选择</strong>的过程,其中的for循环也是一个选择的过程,还有一个点就是base case需要在这个函数来处理。那么,我们就可以把框架整理出来。</p>
<pre><code class="java"> public void backTrace(int start, int[] nums, List<Integer> temp){
base case处理
//选择过程
for(循环选择){
选择
backTrace(递归);
撤销选择
}
}</code></pre>
<p>ok,上面已经讲了一个子集的问题,接下来,再来一个更有点意思的子集的题目。</p>
<h3>2 子集问题</h3>
<p>用于引入回溯算法框架的那个题目其实比较简单,但是,思想是不变的,这个框架很重要,其他的题目基本上都是在上面的框架上进行修改的,比如,剪枝操作等。</p>
<h5>90. 子集 II 中等难度</h5>
<p><img src="/img/bVbIN7O" alt="" title=""></p>
<p>这个题目与前面的子集题目相比较,差别就在于补鞥呢包含重复的子集,也就是不能顺序改变而已,元素一样的子集出现。</p>
<p>这个题目框架还是不变的,但是,要做一下简单的剪枝操作:<strong>怎么排除掉重复的子集</strong>。</p>
<p>这里有两种方法可以解决这个问题,而且,后面其他的题目出现<strong>不能出现重复子集</strong>这样的限制条件的时候,都是可以用这两种方法进行解决的。</p>
<ul><li>方法一:利用Set去重特性解题</li></ul>
<p>我们还是先把上面的框架搬下来,然后再进行修改。</p>
<pre><code class="java"> List<List<Integer>> list = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
if(nums.length == 0){
return null;
}
List<Integer> temp = new ArrayList<>();
backTrace(0, nums, temp);
return list;
}
public void backTrace(int start, int[] nums, List<Integer> temp){
list.add(new ArrayList<>(temp));
//从start开始遍历,避免重复
for(int j = start; j < nums.length; j++){
temp.add(nums[j]);
backTrace(j+1,nums,temp);
temp.remove(temp.size()-1);
}
}</code></pre>
<p>因为我们要利用Set的特性去重,所以需要加入这个变量<code>Set<List<Integer>> set = new HashSet<>();</code>,另外,为了保证顺序,我们再进行排序<code>Arrays.sort(nums)</code>,这样能避免元素一样,但是顺序不一样的重复子集问题。</p>
<p>所以,结果就出来了。</p>
<pre><code class="java"> List<List<Integer>> list = new ArrayList<>();
Set<List<Integer>> set = new HashSet<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
if(nums.length == 0){
return null;
}
//排序
Arrays.sort(nums);
List<Integer> temp = new ArrayList<>();
backTrace(0, nums, temp);
return list;
}
public void backTrace(int start, int[] nums, List<Integer> temp){
//set去重操作
if(!set.contains(temp)){
set.add(new ArrayList<>(temp));
list.add(new ArrayList<>(temp));
}
for(int j = start; j < nums.length; j++){
temp.add(nums[j]);
backTrace(j+1,nums,temp);
temp.remove(temp.size()-1);
}
}</code></pre>
<p>看一下结果发现效率不是很好。</p>
<p><img src="/img/bVbIN7V" alt="" title=""></p>
<p>那我们再来看一下另外一种剪枝的策略用来去重。</p>
<ul><li>方法二:<code>i > start && nums[i-1] == nums[i]</code>
</li></ul>
<p>这种剪枝策略为什么是可以的呢,别急,我来画张图解释一下。</p>
<p><img src="/img/bVbIN7Y" alt="" title=""></p>
<p>所以,我们这种方法就可以做出来了。</p>
<pre><code class="java"> List<List<Integer>> list = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
if(nums.length == 0){
return null;
}
Arrays.sort(nums);
List<Integer> temp = new ArrayList<>();
backTrace(0, nums, temp);
return list;
}
public void backTrace(int start, int[] nums, List<Integer> temp){
list.add(new ArrayList<>(temp));
for(int i = start; i < nums.length; i++){
//剪枝策略
if(i > start && nums[i] == nums[i-1]){
continue;
}
temp.add(nums[i]);
backTrace(i+1,nums,temp);
temp.remove(temp.size()-1);
}
}</code></pre>
<p><img src="/img/bVbIN7Z" alt="" title=""></p>
<p>哎呦,好像还可以哦。</p>
<h3>3 组合问题</h3>
<p>把前面的子集问题搞定之后,你会发现,后面的组合问题,排列问题就都不是什么大问题了,基本上都是套路了。</p>
<h5>39. 组合总和 难度中等</h5>
<p><img src="/img/bVbIN70" alt="" title=""></p>
<p>这个题目跟之前的没有什么太大的区别,只是需要注意一个点:<strong>每个数字可以被无限制重复被选取</strong>,我们要做的就是在递归的时候,<code>i</code>的下标不是从<code>i+1</code>开始,而是从<code>i</code>开始。</p>
<pre><code class="java"> backTrace(i,candidates,target-candidates[i], temp);</code></pre>
<p>我们看看完整代码。</p>
<pre><code class="java"> List<List<Integer>> list = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
if(candidates.length == 0 || target < 0){
return list;
}
List<Integer> temp = new ArrayList<>();
backTrace(0,candidates,target,temp);
return list;
}
public void backTrace(int start, int[] candidates, int target, List<Integer> temp){
//递归的终止条件
if (target < 0) {
return;
}
if(target == 0){
list.add(new ArrayList<>(temp));
}
for(int i = start; i < candidates.length; i++){
temp.add(candidates[i]);
backTrace(i,candidates,target-candidates[i], temp);
temp.remove(temp.size()-1);
}
}</code></pre>
<p>就是这么简单!!!</p>
<p>那么,再来一个组合问题。</p>
<h5>40. 组合总和 II 难度中等</h5>
<p><img src="/img/bVbIN71" alt="" title=""></p>
<p>你一看题目是不是就发现,差不多啊,确实,这里只是每个数字只能用一次,同时也是不能包含重复的组合,所以,用上面的去重方法解决咯。话不多说,上代码。</p>
<pre><code class="java"> List<List<Integer>> lists = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
if(candidates.length == 0 || target < 0){
return lists;
}
Arrays.sort(candidates);
List<Integer> list = new LinkedList<>();
backTrace(candidates,target,list, 0);
return lists;
}
public void backTrace(int[] candidates, int target, List<Integer> list, int start){
if(target == 0){
lists.add(new ArrayList(list));
}
for(int i = start; i < candidates.length; i++){
if(target < 0){
break;
}
//剪枝:保证同一层中只有1个相同的元素,不同层可以有重复元素
if(i > start && candidates[i] == candidates[i-1]){
continue;
}
list.add(candidates[i]);
backTrace(candidates,target-candidates[i],list,i+1);
list.remove(list.size()-1);
}
}</code></pre>
<p>也是完美解决!!</p>
<h3>4 全排列问题</h3>
<p>先来一个最基本的全排列问题,快速解决。</p>
<h5>46. 全排列 难度中等</h5>
<p><img src="/img/bVbIN72" alt="" title=""></p>
<p>这是全排列,只是元素的顺序不一样,所以,我们要做的剪枝就是:temp集合中有的就排除。</p>
<p>上代码。</p>
<pre><code class="java"> List<List<Integer>> lists = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
if(nums.length == 0){
return lists;
}
List<Integer> list = new ArrayList<>();
backTrace(nums,list,0);
return lists;
}
public void backTrace(int[] nums, List<Integer> temp, int start){
if(temp.size() == nums.length){
lists.add(new ArrayList(temp));
return;
}
for(int i = 0; i < nums.length; i++){
//排除已有元素
if(temp.contains(nums[i])){
continue;
}
temp.add(nums[i]);
backTrace(nums,temp,i+1);
temp.remove(temp.size() - 1);
}
}</code></pre>
<p>是不是不带劲,安排!!</p>
<h5>47. 全排列 II 难度中等</h5>
<p>这个题目虽然也是全排列,但是,就要比前面这个难一些了,有两个限定条件:<strong>有重复元素,但是不能包含重复排列</strong>。</p>
<p><img src="/img/bVbIN73" alt="" title=""></p>
<p>不重复的全排列这个我们知道怎么解决,用前面的去重方法即可,但是,怎么保证有相同元素的集合不出现重复的排列呢?</p>
<p>这里我们需要<strong>加一个visited数组,来记录一下当前元素有没有被访问过</strong>,这样就可以解题了。</p>
<pre><code class="java"> public List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
if(nums.length == 0){
return result;
}
Arrays.sort(nums);
findUnique(nums,new boolean[nums.length],new LinkedList<Integer>());
return result;
}
public void findUnique(int[] nums, boolean[] visited,List<Integer> temp){
//结束条件
if(temp.size() == nums.length){
result.add(new ArrayList<>(temp));
return ;
}
//选择列表
for(int i = 0; i<nums.length; i++){
//已经选择过的不需要再放进去了
if(visited[i]) continue;
//去重
if(i>0 && nums[i] == nums[i-1] && visited[i-1]) break;
temp.add(nums[i]);
visited[i] = true;
findUnique(nums,visited,temp);
temp.remove(temp.size()-1);
visited[i] = false;
}
}</code></pre>
<p>这样就搞定了这个题目。</p>
<h3>5 不是总结</h3>
<p>至此,就把子集、组合、全排列问题给解决了。从一步一步讲解框架,到具体问题分析,面面俱到,哈哈,当然,还有一些没有考虑周到的地方,望大家指教。</p>
<p>这篇文章写了两天了,到这里差不多了,原创不易,点个赞吧!</p>
<p>最后,再分享我历时<strong>三个月</strong>总结的 <strong>Java 面试 + Java 后端技术学习指南</strong>,这是本人这几年及春招的总结,已经拿到了大厂 offer,整理成了一本电子书,拿去不谢,目录如下:</p>
<p><img src="/img/remote/1460000022439334" alt="" title=""></p>
<p>现在免费分享大家,在下面我的公众号 <strong>程序员的技术圈子</strong> 回复 <strong>面试</strong> 即可获取。</p>
<p><img src="/img/remote/1460000023000114" alt="" title=""></p>
dfs题目这样去接题,秒杀leetcode题目
https://segmentfault.com/a/1190000022991340
2020-06-22T08:55:54+08:00
2020-06-22T08:55:54+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
7
<blockquote>点个赞,看一看,好习惯!本文 <strong>GitHub</strong> <a href="https://link.segmentfault.com/?enc=m1%2FHXb40CbikX8RUyjRgpQ%3D%3D.RebJ088ZBsWFQqXniqlkmKTzarxNOq9t3RM3LhA%2BuJ2pOeQ6S1ue%2Bm9PcZ8uTm3q" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a> 已收录,这是我花了 3 个月总结的一线大厂 Java 面试总结,本人已拿大厂 offer。<br>另外,原创文章首发在我的个人博客:<a href="https://link.segmentfault.com/?enc=yyK%2FhWiqnOvk3tsKQCOaGw%3D%3D.3JDSYg1fIQKVqPMe21L4YS7EIV5KFDWVP24o6lvXHLE%3D" rel="nofollow">blog.ouyangsihai.cn</a>,欢迎访问。</blockquote>
<p>今天来聊聊 dfs 的解题方法,这些方法都是总结之后的出来的经验,有值得借鉴的地方。</p>
<h3>1 从二叉树看 dfs</h3>
<p>二叉树的思想其实很简单,我们刚刚开始学习二叉树的时候,在做二叉树遍历的时候是不是最常见的方法就是递归遍历,其实,你会发现,二叉树的题目的解题方法基本上都是递归来解题,我们只需要走一步,其他的由递归来做。</p>
<p><img src="/img/bVbIDf4" alt="" title=""></p>
<p>我们先来看一下二叉树的前序遍历、中序遍历、后序遍历的递归版本。</p>
<pre><code class="java">//前序遍历
void traverse(TreeNode root) {
System.out.println(root.val);
traverse(root.left);
traverse(root.right);
}
//中序遍历
void traverse(TreeNode root) {
traverse(root.left);
System.out.println(root.val);
traverse(root.right);
}
//后续遍历
void traverse(TreeNode root) {
traverse(root.left);
traverse(root.right);
System.out.println(root.val);
}</code></pre>
<p>其实你会发现,二叉树的遍历的过程就能够看出二叉树遍历的一个整体的框架,其实这个也是二叉树的解题的整体的框架就是下面这样的。</p>
<pre><code class="java">void traverse(TreeNode root) {
//这里将输出变成其他操作,我们只完成第一步,后面的由递归来完成。
traverse(root.left);
traverse(root.right);
}</code></pre>
<p>我们在解题的时候,我们只需要去想当前的操作应该怎么实现,后面的由递归去实现,至于用前序中序还是后序遍历,由具体的情况来实现。</p>
<p>下面来几个二叉树的热身题,来体会一下这种解题方法。</p>
<p>另外,这些知识的话,我都写了原创文章,比较系统的讲解了,大家可以看看,会有一定得收获的。</p>
<table>
<thead><tr>
<th>序号</th>
<th>原创精品</th>
</tr></thead>
<tbody>
<tr>
<td>1</td>
<td><a href="https://link.segmentfault.com/?enc=ysXdzCjzOibyZuWoJ7WUDQ%3D%3D.iL%2F3I6BHVtodUaXrMIUrIxFBxD%2BM%2FNnmeG6TNoxskumgcMAfpreuy2SfF6f1OUEZuHLZOQ%2FlUo%2F0ZdDd0rsY1t4B5yNOzITkGfr2OVHJP9w%3D" rel="nofollow">【原创】分布式架构系列文章</a></td>
</tr>
<tr>
<td>2</td>
<td><a href="https://link.segmentfault.com/?enc=Rhvz4A16O316CTzWUYJhkQ%3D%3D.WpgdjRUEnKDUHj7s9abgR%2Fh3ypF5BqVkJbUuErQSA25r0RPJAbunNWk%2FSwYkgT1OqkMatGJkczbCEh27ynPRtKBVCrWcGXA8KBcWPiAHEfo%3D" rel="nofollow">【原创】实战 Activiti 工作流教程</a></td>
</tr>
<tr>
<td>3</td>
<td><a href="https://link.segmentfault.com/?enc=cxAONL3y4QHuxCeQxuJU5w%3D%3D.0UHB2G9CKt55UKn%2BmD%2BEBNV6fkbW6e5CzTdFB4wGrZb8h7UvLy%2BOPs%2BQoNtZbpCIAGZ61K7U2BEK8s9SvKWI7Ef7QzTeadVWmhiV8T4htBY%3D" rel="nofollow">【原创】深入理解Java虚拟机教程</a></td>
</tr>
<tr>
<td>4</td>
<td><a href="https://link.segmentfault.com/?enc=b0JTcBCKjH7DfZYGPmwC1w%3D%3D.9O6t8BTqNTlNPYMMeqhl8%2Fq43SwlNAlS8SSuJ0rphc2u8gow%2F7jGrygtKKdf7DncWvLYMojaLGQCbojryyRbZHvU9oeXKUPn8ktCoP8jHfc%3D" rel="nofollow">【原创】Java8最新教程 </a></td>
</tr>
<tr>
<td>5</td>
<td><a href="https://link.segmentfault.com/?enc=Bhk4XS8oi9nY3u54bH0jrA%3D%3D.jR3sYBoMrbNKQ8IshiHmXY8lx6vuBDKY5AaS2RmMtYhSB60wnMXmVtVuyR2qOtK0%2BrakrfUo8XkkPt9vx3WzSOQd735%2FWeLremakvj15zKRX8EFldb0WnzdBqmYo9D90" rel="nofollow">【原创】MySQL的艺术世界</a></td>
</tr>
</tbody>
</table>
<h4>1. 如何把⼆叉树所有的节点中的值加⼀</h4>
<p>首先还是一样,我们先写出框架。</p>
<pre><code class="java">void traverse(TreeNode root) {
//这里将输出变成其他操作,我们只完成第一步,后面的由递归来完成。
traverse(root.left);
traverse(root.right);
}</code></pre>
<p>接下来,考虑当前的一步需要做什么事情,在这里,当然是给当前的节点加一。</p>
<pre><code class="java">void traverse(TreeNode root) {
if(root == null) {
return;
}
//这里改为给当前的节点加一。
root.val += 1;
traverse(root.left);
traverse(root.right);
}</code></pre>
<p>发现是不是水到渠成?</p>
<p>不爽?再来一个简单的。</p>
<h4>2. 如何判断两棵⼆叉树是不是同一棵二叉树</h4>
<p>这个问题我们直接考虑当前一步需要做什么,也就是什么情况,这是同一颗二叉树?</p>
<p>1)两棵树的当前节点等于空:root1 == null && root2 == null,这个时候返回 true。<br>2)两棵树的当前节点任意一个节点为空:root1 == null || root2 == null,这个时候当然是 false。<br>3)两棵树的当前节点都不为空,但是 val 不一样:root1.val != root2.val,返回 false。</p>
<p>所以,答案就显而易见了。</p>
<pre><code class="java">boolean isSameTree(TreeNode root1, TreeNode root2) {
// 都为空的话
if (root1 == null && root2 == null) return true;
// ⼀个为空,⼀个⾮空
if (root1 == null || root2 == null) return false;
// 两个都⾮空,但 val 不⼀样
if (root1.val != root2.val) return false;
// 递归去做
return isSameTree(root1.left, root2.left) && isSameTree(root1.right, root2.right);
}</code></pre>
<p>有了上面的讲解,我相信你已经有了基本的思路了,下面我们来点有难度的题目,小试牛刀。</p>
<h4>3. leetcode中等难度解析</h4>
<h5>114. 二叉树展开为链表</h5>
<p>这个题目是二叉树中的中等难度题目,但是通过率很低,那么我们用上面的思路来看看是否可以轻松解决这个题目。</p>
<p><img src="/img/bVbIDf5" alt="" title=""></p>
<p>这个题目乍一看,根据前面的思路,你可以能首先会选择前序遍历的方式来解决,是可以的,但是,比较麻烦,因为前序遍历的方式会改变右节点的指向,导致比较麻烦,那么,如果前序遍历不行,就考虑中序和后序遍历了,由于,在展开的时候,只需要去改变左右节点的指向,所以,这里其实最好的方式还是用后续遍历,既然是后续遍历,那么我们就可以快速的把后续遍历的框架写出来了。</p>
<pre><code class="java">public void flatten(TreeNode root) {
if(root == null){
return;
}
flatten(root.left);
flatten(root.right);
//考虑当前一步做什么
}</code></pre>
<p>这样,这个题目的基本思路就出来了,那么,我们只需要考虑当前一步需要做什么就可以把这个题目搞定了。</p>
<p>当前一步:由于是后序遍历,所以顺序是<code>左右中</code>,从展开的顺序我们可以看出来,明显是先连接左节点,后连接右节点,所以,我们肯定要先保存右节点的值,然后连接左节点,同时,我们的展开之后,只有右节点,所以,左节点应该设置为null。</p>
<p>经过分析,代码直接就可以写出来了。</p>
<pre><code class="java">public void flatten(TreeNode root) {
if(root == null){
return;
}
flatten(root.left);
flatten(root.right);
//考虑当前一步做什么
TreeNode temp = root.right;//
root.right = root.left;//右指针指向左节点
root.left = null;//左节点值为空
while(root.right != null){
root = root.right;
}
root.right = temp;//最后再将右节点连在右指针后面
}</code></pre>
<p>最终这就是答案了,这不是最佳的答案,但是,这可能是解决二叉树这种题目的最好的理解方式,同时,非常有助于你理解dfs这种算法的思想。</p>
<h5>105. 从前序与中序遍历序列构造二叉树</h5>
<p>这个题目也是挺不错的题目,而且其实在我们学习数据结构的时候,这个题目经常会以解答题的方式出现,让我们考试的时候来做,确实印象深刻,这里,我们看看用代码怎么解决。</p>
<p><img src="/img/bVbIDf6" alt="" title=""></p>
<p>还是同样的套路,同样的思路,已经同样的味道,再来把这道菜炒一下。</p>
<p>首先,确定先序遍历、中序遍历还是后序遍历,既然是由前序遍历和中序遍历来推出二叉树,那么,前序遍历是更好一些的。</p>
<p>这里我们直接考虑当前一步应该做什么,然后直接做出来这道菜。</p>
<p><strong>当前一步</strong>:回想一下以前做这个题目的思路你会发现,我们去构造二叉树的时候,思路是这样的,前序遍历第一个元素肯定是根节点a,那么,当前前序遍历的元素a,在中序遍历中,在a这个元素的左边就是左子树的元素,在a这个元素右边的元素就是左子树的元素,这样是不是就考虑清楚了当前一步,那么我们唯一要做的就是在中序遍历数组中找到a这个元素的位置,其他的递归来解决即可。</p>
<p>话不多说,看代码。</p>
<pre><code class="java">public TreeNode buildTree(int[] preorder, int[] inorder) {
//当前前序遍历的第一个元素
int rootVal = preorder[0];
root = new TreeNode();
root.val = rootVal;
//获取在inorder中序遍历数组中的位置
int index = 0;
for(int i = 0; i < inorder.length; i++){
if(rootVal == inorder[i]){
index = i;
}
}
//递归去做
}</code></pre>
<p>这一步做好了,后面就是递归要做的事情了,让计算机去工作吧。</p>
<pre><code class="java">public TreeNode buildTree(int[] preorder, int[] inorder) {
//当前前序遍历的第一个元素
int rootVal = preorder[0];
root = new TreeNode();
root.val = rootVal;
//获取在inorder中序遍历数组中的位置
int index = 0;
for(int i = 0; i < inorder.length; i++){
if(rootVal == inorder[i]){
index = i;
}
}
//递归去做
root.left = buildTree(Arrays.copyOfRange(preorder,1,index+1),Arrays.copyOfRange(inorder,0,index));
root.right = buildTree(Arrays.copyOfRange(preorder,index+1,preorder.length),Arrays.copyOfRange(inorder,index+1,inorder.length));
return root;
}</code></pre>
<p>最后,再把边界条件处理一下,防止root为null的情况出现。</p>
<pre><code class="java">TreeNode root = null;
if(preorder.length == 0){
return root;
}</code></pre>
<p>ok,这道菜就这么按照模板炒出来了,相信你,后面的菜你也会抄着炒的。</p>
<h3>2 从leetcode的岛屿问题看dfs</h3>
<h4>1. 步步为营</h4>
<p>这一类题目在leetcode还是非常多的,而且在笔试当中你都会经常遇到这种题目,所以,找到解决的方法很重要,其实,最后,你会发现,这类题目,你会了之后就是不再觉得难的题目了。</p>
<p>我们先来看一下题目哈。</p>
<p><img src="/img/bVbIDf7" alt="" title=""></p>
<p>题目的意思很简单,有一个二维数组,里面的数字都是0和1,0代表水域,1代表陆地,让你计算的是陆地的数量,也就是岛屿的数量。</p>
<p>那么这类题目怎么去解决呢?</p>
<p>其实,我们可以从前面说的从二叉树看dfs的问题来看这个问题,二叉树的特征很明显,就是只有两个分支可以选择。</p>
<p><img src="/img/bVbIDf4" alt="" title=""></p>
<p>所以,就有了下面的遍历模板。</p>
<pre><code class="java">//前序遍历
void traverse(TreeNode root) {
System.out.println(root.val);
traverse(root.left);
traverse(root.right);
}</code></pre>
<p>但是,回归到这个题目的时候,你会发现,我们的整个数据结构是一张二维的图,如下所示。</p>
<p><img src="/img/bVbIDf8" alt="" title=""></p>
<p>当你遍历这张图的时候,你会怎么遍历呢?是不是这样子?</p>
<p><img src="/img/bVbIDf9" alt="" title=""></p>
<p>在(i,j)的位置,是不是可以有四个方向都是可以进行遍历的,那么是不是这个题目就有了新的解题思路了。</p>
<p>这样我们就可以把这个的dfs模板代码写出来了。</p>
<pre><code class="java">void dfs(int[][] grid, int i, int j) {
// 访问上、下、左、右四个相邻方向
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}</code></pre>
<p>你会发现是不是和二叉树的遍历很像,只是多了两个方向而已。</p>
<p>最后还有一个需要考虑的问题就是:<strong>base case</strong>,其实二叉树也是需要讨论一下base case的,但是,很简单,当<code>root == null</code>的时候,就是base case。</p>
<p>这里的base case其实也不难,因为这个二维的图是有边界的,当dfs的时候发现超出了边界,是不是就需要判断了,所以,我们再加上边界条件。</p>
<pre><code class="java">void dfs(int[][] grid, int i, int j) {
// 判断 base case
if (!inArea(grid, i, j)) {
return;
}
// 如果这个格子不是岛屿,直接返回
if (grid[i][j] != 1) {
return;
}
// 访问上、下、左、右四个相邻方向
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
// 判断坐标 (r, c) 是否在网格中
boolean inArea(int[][] grid, int i, int j) {
return 0 <= i && i < grid.length
&& 0 <= j && j < grid[0].length;
}</code></pre>
<p>到这里的话其实这个题目已经差不多完成了,但是,还有一点我们需要注意,<strong>当我们访问了某个节点之后,是需要进行标记的,可以用bool也可以用其他数字标记,不然可能会出现循环递归的情况。</strong></p>
<p><img src="/img/bVbIDga" alt="" title=""></p>
<p>所以,最后的解题就出来了。</p>
<pre><code class="java">void dfs(int[][] grid, int i, int j) {
// 判断 base case
if (!inArea(grid, i, j)) {
return;
}
// 如果这个格子不是岛屿,直接返回
if (grid[i][j] != 1) {
return;
}
//用2来标记已经遍历过
grid[i][j] = 2;
// 访问上、下、左、右四个相邻方向
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
// 判断坐标 (r, c) 是否在网格中
boolean inArea(int[][] grid, int i, int j) {
return 0 <= i && i < grid.length
&& 0 <= j && j < grid[0].length;
}</code></pre>
<p>没有爽够?再来一题。</p>
<h4>2. 再来一发</h4>
<p><img src="/img/bVbIDgb" alt="" title=""></p>
<p>这个题目跟上面的那题很像,但是这里是求最大的一个岛屿的面积,由于每一个单元格的面积是1,所以,最后的面积就是单元格的数量。</p>
<p>这个题目的解题方法跟上面的那个基本一样,我们把上面的代码复制过去,改改就可以了。</p>
<pre><code class="java">class Solution {
public int maxAreaOfIsland(int[][] grid) {
if(grid == null){
return 0;
}
int max = 0;
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(grid[i][j] == 1){
max = Math.max(dfs(grid, i, j), max);
}
}
}
return max;
}
int dfs(int[][] grid, int i, int j) {
// 判断 base case
if (!inArea(grid, i, j)) {
return 0;
}
// 如果这个格子不是岛屿,直接返回
if (grid[i][j] != 1) {
return 0;
}
//用2来标记已经遍历过
grid[i][j] = 2;
// 访问上、下、左、右四个相邻方向
return 1 + dfs(grid, i - 1, j) + dfs(grid, i + 1, j) + dfs(grid, i, j - 1) + dfs(grid, i, j + 1);
}
// 判断坐标 (r, c) 是否在网格中
boolean inArea(int[][] grid, int i, int j) {
return 0 <= i && i < grid.length
&& 0 <= j && j < grid[0].length;
}
}</code></pre>
<p><strong>基本思路:</strong> 每次进行dfs的时候都对岛屿数量进行+1的操作,然后再求所有岛屿中的最大值。</p>
<p>我们看一下我们代码的效率如何。</p>
<p><img src="/img/bVbIDgc" alt="" title=""></p>
<p>看起来是不是还不错哟,对的,就是这么搞事情!!!</p>
<p>最后,这篇文章前前后后写了快一周的时间把,不知道写的怎么样,但是,我尽力的把自己所想的表达清楚,主要是一种思路跟解题方法,肯定还有很多其他的方法,去LeetCode去看就明白了。</p>
<p>好了,写的也够久了,下篇文章再来看看其他的,希望对大家有帮助,再次再见!!</p>
<p>最后,再分享我历时<strong>三个月</strong>总结的 <strong>Java 面试 + Java 后端技术学习指南</strong>,这是本人这几年及春招的总结,已经拿到了大厂 offer,整理成了一本电子书,拿去不谢,目录如下:</p>
<p><img src="/img/remote/1460000022439334" alt="" title=""></p>
<p>现在免费分享大家,在下面我的公众号 <strong>程序员的技术圈子</strong> 回复 <strong>面试</strong> 即可获取。</p>
<p><img src="/img/remote/1460000023000114" alt="" title=""></p>
我去,Java面试的思维导图,全部给你准备好了,拿走不谢
https://segmentfault.com/a/1190000022941513
2020-06-16T08:34:12+08:00
2020-06-16T08:34:12+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
3
<blockquote>点个赞,看一看,好习惯!本文 <strong>GitHub</strong> <a href="https://link.segmentfault.com/?enc=Yhy7S4G05VfAbSf3kdvgZA%3D%3D.GmsPC38M6AJF0zqwNe6vJplbTkkUyw9B31aSuQ8ggKVkJ0NQQVlA8HXkEL08Sfuw" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a> 已收录,这是我花了 3 个月总结的一线大厂 Java 面试总结,本人已拿大厂 offer。<br>另外,原创文章首发在我的个人博客:<a href="https://link.segmentfault.com/?enc=AWGtrxqGWKE%2F2ceaNI8KJg%3D%3D.UKY3dTKVsrp8Ytr2gcahdRZUoFePGnh4uWRlWIUJ3vw%3D" rel="nofollow">blog.ouyangsihai.cn</a>,欢迎访问。</blockquote>
<p>大家好,又跟大家见面了,非常高兴能够跟大家分享一些Java相关的知识,也是很久没有分享视频,在上一期当中我就分享了一个非科班的学生,或者说从来没有接触到Java行业的从业人员的话,怎么能够更快的进入到Java这个行业,并且通过自己的一些学习的方法,更快的找到一份自己满意的工作。</p>
<p>这一期,就想更详细的来介绍一下找工作当中的一些问题。</p>
<p>我们都知道在找工作的过程中,需要去复习很多的知识,很多的技术,很多的知识点,那么这些知识点怎么去跟面试官讲的时候更加的清晰,我这里介绍一个方法。</p>
<p>我在面试的过程中,在复习的过程中,就把很多的知识点都整理成了思维导图,比如说,<strong>jvm的,多线程,数据库,数据网络</strong>,这些相关的一块一块的知识的话,我都会把它分类汇总成为一个思维导图,今天我就想给大家分享一些我整理过的思维导图,也希望在以后大家的面试过程中有一定的帮助。</p>
<p><img src="/img/bVbIqiw" alt="" title=""></p>
<p>从上面的图可以看得到,这个的话就是我把jvm相关的一些知识点整理成了一个思维导图,每一个点都列得非常的清晰,在面试中会遇到的知识点都已经整理出来了,非常的清晰。如果你有这个思维导图的话,那么你在面试当中碰到jvm的一些相关问题,根本就不用慌。首先的话,你对于jvm相关的一些知识都是有了解的,同时你对jvm有这样一整个思维导图的梳理的话,你的思路是比较清晰的,那么面试官肯定会觉得你对jvm这一块的相关的知识掌握的不错的,这样的话你在面试当中也会增加信心,同时也会更加有底气。</p>
<p><img src="/img/bVbIqix" alt="" title=""></p>
<p>另外,这些知识的话,我都写了原创文章,比较系统的讲解了,大家可以看看,会有一定得收获的。</p>
<table>
<thead><tr>
<th>序号</th>
<th>原创精品</th>
</tr></thead>
<tbody>
<tr>
<td>1</td>
<td><a href="https://link.segmentfault.com/?enc=2Lr2IvrDi5FizElQhRkysA%3D%3D.hz4nlEkB%2BVJsuCdolRTTccjxhAMjUAwBS2KQshAGYRPGJTa%2BnXcGiUPXr%2FyBoeAtOeq45R1PVxTUhGwaNTPrOm8kksJzAiAAQ6ZD66k4mcs%3D" rel="nofollow">【原创】分布式架构系列文章</a></td>
</tr>
<tr>
<td>2</td>
<td><a href="https://link.segmentfault.com/?enc=VYEgUThRU4KGDGJ5rH1wrA%3D%3D.%2FKn9ccm6VL5GJXqu%2F0LfHXP2cB9pCHUFOKFHgdG1UkrUJJfCzUzE3YmjI7vHeSkW86RV%2FuuzfmexZ%2Fl7qtnVaqVIhlg7U4cwpSN4ffve2%2B0%3D" rel="nofollow">【原创】实战 Activiti 工作流教程</a></td>
</tr>
<tr>
<td>3</td>
<td><a href="https://link.segmentfault.com/?enc=Z1lTwH6C9KOFj7WmsFJMng%3D%3D.VeDIuUjmcr3BpEkG3jBcDMkKk9JfitfEKLUC6bP91%2B6lJ1jGcxdv2fbRfmhSSuC6yn3XvsZyavKlg7O5tKN7dPyP5mETfG88DDu3urMalRE%3D" rel="nofollow">【原创】深入理解Java虚拟机教程</a></td>
</tr>
<tr>
<td>4</td>
<td><a href="https://link.segmentfault.com/?enc=5Fnl%2FyrN4Vr0heRANq%2BAaQ%3D%3D.1roBNlA2%2Bf6PM5j%2FYL3Pwry3j7t42fCkfN%2B0b51M%2FCA601ClEBo%2BUma7168GuIL5R8uRSYR5dNRGUlM3gjGtyZ%2Bg0I3E9BfXgoqiNV7aREA%3D" rel="nofollow">【原创】Java8最新教程 </a></td>
</tr>
<tr>
<td>5</td>
<td><a href="https://link.segmentfault.com/?enc=7z5x%2FVXWQV70I5TL%2BV2Gjw%3D%3D.J75c7PoonBaz5AMexO4KVrMZ0pfcARYnDMUZ8CDMRltJM9yLOEUgc2ZMsOcivuKhiFM3t%2Fn%2FGJ7VkOJOA8iBKY9tyoYcJYm7IzFsPbmkbYZf8UiAZj9LMmJsLWB3aAuS" rel="nofollow">【原创】MySQL的艺术世界</a></td>
</tr>
</tbody>
</table>
<p>这个的话,是我在面试当中觉得集合这方面确实在面试当中问得非常多,你在每一次面试的过程当中,你基本上都会遇到集合相关的面试问题,比如说list,set这两个最基本集合就不用说了,基本上会问到。另外一个问的最多的可以说就是HashMap了。HashMap在面试当中问的特别多,不管哪个大厂,他基本都会问你HashMap,有一些变态的问题,还甚至会让你去实现一个HashMap,就在面试过程中让你去自己实现一个HashMap,或者你讲一下你怎么去实现一个HashMap,其实这个问题还是比较难的,如果你对于整个HashMap不了解的话还是比较难回答的。</p>
<p>另外一个的话,除了HashMap,当然就是ConcurrentHashMap,这个也是面试必备的一个知识点,如果你这个不会,那么你肯定会在一些面试当中被面试官怼烂的。</p>
<p>最后一个就是线程安全的集合了,这一块也不用多说,以上这些都是在面试中问得非常多的,一定要好好掌握,这里都给大家总结了,其他的话可能还会更加详细的进行总结。</p>
<p>最后一块的话,这个也是我在面试当中问的,也是遇到的被问的特别多的一个问题,就是多线程的相关的一些知识,synchronized关键字,从哪些方面去回答,volitale关键字,从哪些方面去回答,怎么展开,思路是怎样的,怎样回答才会让面试官满意呢?</p>
<p>还有就是JUC相关的一些知识了,比如说</p>
<ul>
<li>ReentrantLock和synchronized的区别,ReentrantLock在面试中-怎样去回答面试官的问题,</li>
<li>AQS有哪些知识点是可以被问到的,CAS是怎样的一个概念?在面试中,面试官会怎么样去问CAS的问题呢?</li>
</ul>
<p>最后的话就是大名鼎鼎的线程池,这一个知识点在面试当中也是问的特别多,我在每一次面试大厂的过程中基本上都会问到线程池相关的一些知识,比如说:</p>
<ul><li>Java线程池是怎么样的,有什么参数</li></ul>
<p>你一一的都要去解释清楚,另外的话,他不会去问Java线程池有哪些参数,有什么意义,在遇到实际问题当中怎么去运用这些参数,而是它会让你去设计一个线程池,其实这种思路,跟你去解释这些基本的参数,是一样的道理的。</p>
<p>这一期就先给大家介绍这么多相关的知识点,下期再见。</p>
<p>最后,如果大家需要这些思维导图的话,可以<strong>扫描下面的二维码</strong>,在我的公众号回复:<strong>思维导图</strong>,就可以获取思维导图的源文件了,原创不易,点个赞吧。</p>
<p><img src="/img/bVbIqiy" alt="" title=""></p>
<p>最后,再分享我历时<strong>三个月</strong>总结的 <strong>Java 面试 + Java 后端技术学习指南</strong>,这是本人这几年及春招的总结,已经拿到了大厂 offer,整理成了一本电子书,拿去不谢,目录如下:</p>
<p><img src="/img/bVbGjEV" alt="" title=""></p>
<p>现在免费分享大家,在下面我的公众号 <strong>程序员的技术圈子</strong> 回复 <strong>面试</strong> 即可获取。</p>
我在实际工作中用的最多的 git 命令,全在这里了,使用简单!
https://segmentfault.com/a/1190000022778510
2020-05-29T08:41:04+08:00
2020-05-29T08:41:04+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
14
<blockquote>原创文章首发在我的个人博客:<a href="https://link.segmentfault.com/?enc=gpJwMIONIxLhp3BSFmKyYA%3D%3D.ttI0wmqqsM9paMBjeC3SNrbfAcXBao%2FOUjgnpRSWT3omgwN22DZ1l0pOH5wKdjvOFXZsPMSacQNjcNVOvPUHb4%2FvltjsnIqfK5rK9%2FRGEgXVSItUfB051%2FrQ0bLcqAQz" rel="nofollow">blog.ouyangsihai.cn</a>,欢迎访问。</blockquote>
<h3>前言</h3>
<p>最近在工作中频繁用到git版本管理,期间也遇到了很多的问题,平时也会使用,但是,在没有遇到什么大的问题的时候,还是用的不是特别的熟练,最近,自己在玩的时候,发现了很多问题,同时,也用git解决了这些问题,发现git真的是个好东西,用的熟练了,真的棒!!!</p>
<p>所以,今天就花点时间总结一下最最常用的一些git命令,不管什么时候,这些命令都是需要熟练的使用的,这样才能保证不出错,那就开始吧!!!</p>
<h3>创建仓库/初始化/提交操作</h3>
<h5>1、git init</h5>
<p>初始化仓库操作,这样才能用git进行代码管理。</p>
<p><img src="/img/bVbHJS9" alt="" title=""></p>
<p>这里分享一下自己最近的原创文章合集。</p>
<table>
<thead><tr>
<th>序号</th>
<th>文章详情</th>
</tr></thead>
<tbody>
<tr>
<td>1</td>
<td><a href="https://link.segmentfault.com/?enc=7vLJ5LezmNhTSTMxOOJ%2BmA%3D%3D.CI3ohmK7iM%2FYP41OLr8KekB%2F40JYyMoW05bXVv%2BMoyt9HqlvFbnYacNX767KGxfZ9te%2Bk0isLRlMyWMwGftZ6NQlQsq87RO7O6f3gna3ouo%3D" rel="nofollow">【原创】分布式架构系列文章</a></td>
</tr>
<tr>
<td>2</td>
<td><a href="https://link.segmentfault.com/?enc=OWw7sfFQ4cPKqs7PD2OfSw%3D%3D.JqsPgyQSY1tT7o8FUJWPTUax%2FTk%2FFsHr4H%2B70BPB2eHxtSswM0aKMZ8j2Jd8MU5Z3aw0HnQU5cL7nZPbhFC3dAoybrG0PTHBqYQCCs3xar8%3D" rel="nofollow">【原创】实战 Activiti 工作流教程</a></td>
</tr>
<tr>
<td>3</td>
<td><a href="https://link.segmentfault.com/?enc=GuCeq8ZYucEX9pEmMabjQA%3D%3D.iX3FgFhQmsJ8d4RIllbqAMn2bttpthVbVezGPY0qCKRfDp%2Bzbuf8OC4usRKlQw2KrztVRxaguGRDxvq0fJPMezBwSgCZ7a3sjt7fCV1RmnE%3D" rel="nofollow">【原创】深入理解Java虚拟机教程</a></td>
</tr>
<tr>
<td>4</td>
<td><a href="https://link.segmentfault.com/?enc=AVUQ4%2BwXS8kkqiMkHmz47Q%3D%3D.nAmaAUHH4sk2ZEoHg962oiHfSC%2BM6hS2sT5UH2sOrh9bHO7kix%2B46JnvDU5p2z0gygydqqnDNjs20dsnsRpd8kcZhgFVEoKCCiTELDJBAIg%3D" rel="nofollow">【原创】Java8最新教程 </a></td>
</tr>
<tr>
<td>5</td>
<td><a href="https://link.segmentfault.com/?enc=iZ7klA%2B6JsilhuM0rQotlg%3D%3D.FLmwKe7BlhYWO38Rsg1dJXIlA1TpWhPT%2FxJKhV56oA%2FoZgAdtpfDarYGmA8r%2B3sAsq6rJmUxzLqGHQMG1jgPIyKCVZEmrfiiNgXGWB1NAethbL%2BVEOX0dv1ZymYZAR1D" rel="nofollow">【原创】MySQL的艺术世界</a></td>
</tr>
</tbody>
</table>
<h5>2、git clone 仓库地址</h5>
<p>复制远程仓库的代码到本地。</p>
<p><img src="/img/bVbHJTa" alt="" title=""></p>
<h5>3、git add XXX</h5>
<p>添加本地的<strong>某个新文件</strong>到本地仓库,但是,此时只是提交到了本地仓库,并没有提交到远程仓库。</p>
<p><img src="/img/bVbHJTb" alt="" title=""></p>
<p><img src="/img/bVbHJTc" alt="" title=""></p>
<h5>4、git add .</h5>
<p>这个操作和上面的区别在于,这个命令会添加所有的新文件,也就是当前目录下的。</p>
<p><img src="/img/bVbHJTd" alt="" title=""></p>
<h5>5、git commit -m 'message'</h5>
<p>提交代码到本地仓库,并没有到远程仓库,不理解的可以去了解一下git的原理。</p>
<p><img src="/img/bVbHJTe" alt="" title=""></p>
<h5>6、git commit -am 'message'</h5>
<p>这个命令将上面两个步骤 add 和 commit 合二为一。</p>
<p><img src="/img/bVbHJTf" alt="" title=""></p>
<h3>日志查看/信息显示</h3>
<h5>1、git log</h5>
<p>这个命令主要用于查看提交日志</p>
<p><img src="/img/bVbHJTg" alt="" title=""></p>
<h5>2、git status</h5>
<p>可以用来查看仓库的状态,在开发中,使用最多的可能就是这个命令了,建议开发过程中没事就 git status 一下。</p>
<p><img src="/img/bVbHJTh" alt="" title=""></p>
<p>如果当你不知道你的git分支或者仓库的状态的时候,记得一定git status一下,不然,可能就会出问题哈。</p>
<h3>分支管理</h3>
<p>这个是重头戏哈,在实际的工作中,分支创建开发新功能,切换分支简直能再多了,如果操作不当,可能造成很大的麻烦,我在工作中就遇到过很多不必要的麻烦,而且解决起来特别难受。</p>
<h5>1、创建分支 git branch XXX</h5>
<p>可以在远程界面创建分支,或者使用命令<code>git branch XXX</code>。</p>
<p><img src="/img/bVbHJTi" alt="" title=""></p>
<p>创建的新分支的代码一般是来自于master的,所以,比如你创建了新分支test,那么test分支的代码是和master的代码是一样的。</p>
<p>我们还可以使用<code>git branch</code>查看分支。</p>
<p><img src="/img/bVbHJTj" alt="" title=""></p>
<h5>2、切换分支 git checkout XXX</h5>
<p>切换分支:<code>git checkout XXX</code>,这样就切换到了XXX分支。然后我们再到XXX分支进行功能的开发工作。</p>
<p><img src="/img/bVbHJTk" alt="" title=""></p>
<h5>3、 创建分支并且切换分支 git checkout -b XXX</h5>
<p>命令:<code>git checkout -b XXX</code>,这条命令就是执行了前面的两条分支,git branch XXX和git checkout XXX,创建并且直接切换到XXX分支,这个命令的好处在于,当你需要进行新的功能开发的时候,你直接创建新分支,然后直接切换了,就可以直接开搞了。</p>
<p><img src="/img/bVbHJTl" alt="" title=""></p>
<p>其实,我们在开发的过程中会遇到很多技术,如果大家有兴趣,可以看看我的博客<a>blog.ouyangsihai.cn</a>,里面都是我的原创文章,技术干货。</p>
<h5>4、查看处于哪个分支 git branch</h5>
<p>命令:<code>git branch</code>,可以直接查看本地的所有分支,并且当前处于哪个分支。</p>
<p><img src="/img/bVbHJTl" alt="" title=""></p>
<p>如果你问,我想查看本地和远程的所有分支用哪个命令呢?</p>
<h5>5、查看本地和远程所有分支 git branch -a</h5>
<p><code>git branch -a</code>,是不是很简单。</p>
<p><img src="/img/bVbHJTm" alt="" title=""></p>
<h5>6、合并分支 git merge</h5>
<p>本地有这么多的分支当我们完成功能开发,需要合并到maste的时候,应该怎么办呢?</p>
<ul>
<li>切换到master分支,git checkout master</li>
<li>合并XXX分支,git merge XXX</li>
<li>这时候如果有冲突就需要解决冲突了。</li>
</ul>
<p><img src="/img/remote/1460000022778513" alt="" title=""></p>
<h5>7、删除本地分支 git branch -D XXX</h5>
<p>当我们完成了功能开发,且合并到了master的时候,我们就可以删除我们当前的分支了,命令<code>git branch -D XXX</code>。</p>
<p><img src="/img/bVbHJTn" alt="" title=""></p>
<p>注意:当前处于XXX分支,XXX分支是不能被删除的,需要先切换到其他分支。</p>
<h5>8、删除远程分支 git push origin --delete XXX</h5>
<p>删除远程分支属于危险操作,如果权限不合理,可能会出现大问题。</p>
<p>建议:git branch -a 查看所有分支,再进行操作。</p>
<p><img src="/img/bVbHJTo" alt="" title=""></p>
<h3>更新管理</h3>
<h5>1、提交代码到远程 git push origin XXX</h5>
<p>本地代码写好,提交到远程,最常用的操作,XXX就是远程的仓库名称。最常用:<code>git push origin master</code>,添加到master。</p>
<h5>2、拉取远程代码到本地 git pull origin XXX</h5>
<p>将远程代码下拉到本地并进行合并,等价于 fetch 和 merge 两步一起执行,但是,这个其实是在平时最常用的命令,一般拉取新代码的时候,都直接用这个命令操作。</p>
<p><img src="/img/bVbHJTp" alt="" title=""></p>
<p>另外,本文 <strong>GitHub</strong> <a href="https://link.segmentfault.com/?enc=9LVJiRlrp3mw4zpoK79Lig%3D%3D.LsOExyGeuIxJdJX21LCMI5zWcLMHG6MOKwhhJTyknsKxYOknTElQZED%2F3ydOvwOS" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a> 已收录,这是我花了3个月总结的一线大厂Java面试总结,本人已拿大厂offer。</p>
<h3>版本回退</h3>
<p>其实在平时的开发中还是会遇到一些操作不当,导致分支出现问题,这个时候,版本管理的作用就凸显出来了,我们可以通过git提供的版本管理进行版本回退操作,这样可以很快的解决我们得问题。</p>
<p>场景:当我们开发一段时间之后,发现现在跟master分支出了很大的问题,我们很有可能需要回退到一个我们比较合适的代码版本,然后再进行相关的开发工作。</p>
<h5>1、 git reset --hard XXX</h5>
<p>这个命令使用需要注意,会把当前分支的代码全部回退到以前的一个版本,不可逆转,需要谨慎使用。</p>
<p>这个命令虽然不太常用,但是,当出现大的问题的时候,却能发挥很大的作用,直接回退到一个以前的版本。</p>
<p><img src="/img/bVbHJTq" alt="" title=""></p>
<p><img src="/img/bVbHJTr" alt="" title=""></p>
<p>当然,有时候当我们错误的回退的时候,又想回到最初的样子怎么办呢,难道代码中的我们也不能回到18吗?</p>
<p>当然可以。我们可以使用<code>git reflog</code>,查看所有的head的记录。</p>
<p><img src="/img/bVbHJTs" alt="" title=""></p>
<p>最后,我们在通过<code>git reset --hard 766f905f</code>,重新回到回退之前的版本。</p>
<p><img src="/img/bVbHJTt" alt="" title=""></p>
<p>ok,以上就是工作中最最常用的git命令大全了。</p>
<p>给俺点个赞呗,最近系列原创文章。</p>
<table>
<thead><tr>
<th>序号</th>
<th>原创精品</th>
</tr></thead>
<tbody>
<tr>
<td>1</td>
<td><a href="https://link.segmentfault.com/?enc=gdVITiNNsOPIQ48Tyd0SdA%3D%3D.yo1XjxRDpWlLhpT9Q90pDiO7vHtufGt3jvUvkmGHSK3CBAt7Yvq3h7RZEFoQVkjbiw70Gdgl%2BeLp2%2BvDSnD%2FN%2FSeKBR4Rm0ZmmPK1%2BvuKHQ%3D" rel="nofollow">【原创】分布式架构系列文章</a></td>
</tr>
<tr>
<td>2</td>
<td><a href="https://link.segmentfault.com/?enc=aMzG%2F%2BQTyU7dVFKAztVy%2Fg%3D%3D.COMRK2vclrVxqUhNe%2Brzog%2F9e7lSzXH%2FGubqIy4A%2FRQs%2BeLN1fc6mvdBmyJJARHJZg2ZdeRR%2BchFCQTnD0f1LDBtLhNVJ3MmDlmrA5GLvfo%3D" rel="nofollow">【原创】实战 Activiti 工作流教程</a></td>
</tr>
<tr>
<td>3</td>
<td><a href="https://link.segmentfault.com/?enc=UvgrsUus4xdQ2S%2BnqABqEA%3D%3D.hzGVjWBYDLASin7zymmhCJageqXb9yFxApXUfEkGDOIAp9unbwsyiH84rHm0YaelxxKGhVommA20AlW13JNd5Ug4T5JVMbbu9%2BJS6nE2sHg%3D" rel="nofollow">【原创】深入理解Java虚拟机教程</a></td>
</tr>
<tr>
<td>4</td>
<td><a href="https://link.segmentfault.com/?enc=oCkjJWMCKi5FLC2dRA5JIg%3D%3D.0tMU2i4oVEndXUI%2FRO%2FYO%2FtwvI88QjhGQDEVZ94pAH8CcaKYfoJvo91yLgG2uacwI%2Ff3I%2BPq0mTSokrBQ9jPKCL8%2BNWeBcSs%2FZuwjjK6kdQ%3D" rel="nofollow">【原创】Java8最新教程 </a></td>
</tr>
<tr>
<td>5</td>
<td><a href="https://link.segmentfault.com/?enc=dB0xPAC%2Bmg4VUARPUJDCkA%3D%3D.d3FmQrsDI%2BMsOd6wsXveeaE5DKyxUGuAZHaNROjzkjgc4E9jByr5wX8heAdSNJDI0MH1XkaVru41NblFlI7FYnMfifpsfohW9I%2BrwCLeRbjVyIJhA1VTAvJEQkcf1V5D" rel="nofollow">【原创】MySQL的艺术世界</a></td>
</tr>
</tbody>
</table>
我的程序员生活的前六年,是这样度过的!
https://segmentfault.com/a/1190000022722337
2020-05-23T09:38:49+08:00
2020-05-23T09:38:49+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
4
<blockquote>点个赞,看一看,好习惯!本文 <strong>GitHub</strong> <a href="https://link.segmentfault.com/?enc=vsxRoEbXy4Qo6uCVTS7b0Q%3D%3D.sC9i1R2hE1mHffiLgBOhdALKzJvq8p2T7dwN9hEa5DataR%2B9lX45UsxmxdUNNed2" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a> 已收录,这是我花了 3 个月总结的一线大厂 Java 面试总结,本人已拿大厂 offer。<br>另外,原创文章首发在我的个人博客:<a href="https://link.segmentfault.com/?enc=ZHEuQRbekMXxUxpJaLHJAA%3D%3D.E3qU8ZDzNi25rM5xq0TziHCSp1ytdJidyeBXinKjtXQ%3D" rel="nofollow">blog.ouyangsihai.cn</a>,欢迎访问。</blockquote>
<h3>误打误撞</h3>
<p>今天突然想写一下题为《我的每一天都是这样度过的》的这样一篇文章,是为了记录现在自己所做的事情,也是为了激励自己做的更好,当然,也是希望能够激励大家一起努力。</p>
<p>但是,一不小心写成了我的“辛酸史”,哈哈。</p>
<h3>开始</h3>
<p>我,一个从遥远的乡村坎坎坷坷的走过来的大学生,其实,我们都知道这一路上并不轻松,或者说充满了许多的辛酸,辛酸的是,从农村走出来的孩子,一步走错,可能就不可逆转的,也就是一步错步步错,虽然,过程中没有感受到这种心惊胆跳,但是,回过头去看过往,无一不是如此。</p>
<p>从初中到高中,考上了我们县当时认为最好的高中,其实,当时由于发挥失常,差点没有考上,但是,幸运之神眷顾了我,我上了县最好的高中;高中考大学,都说是人一辈子最重要的一次考试,确实如此,知道现在,我也觉得,高考就是人生的第一道分水岭,如果是爬山的话,爬到山顶风景一定是最美的,不可否认,我错过了最美的风景,“<strong>顺顺利利的考上了一所二本的学校</strong>”。</p>
<p>就这样,我从一个美丽的山村,来到了一个不算繁华的城市,这座城市在我们省还算不错,但是,放眼全国,仅仅是一座四线城市而已,当时的房价也只是8K,是不是觉得很低,是的,这座城市确实很一般,但是,对于当时的我来说,却已经是不错的城市了,是充满灯红酒绿的城市,在读大学以前,我去做的唯一一个城市就是深圳,当时,是高三毕业的时候,然后去了深圳的表姐家,然后去了肯德基打暑假工,这是我第一次离开家乡,去大城市生活,虽然以前没有去过大城市,但是,我还是很容易适应生活,很快就熟悉了深圳这个繁华的都市,也喜欢上了这个城市。</p>
<h3>青春</h3>
<p>回到正题,回到大学,现在回想起大学还是觉得这是最美好的四年,因为我的大学生活我很满意,大学的四年的时间,我没有浪费,从大一开始的懵懂,到大学制定明确的目标,到大三渐入佳境,最后,到大四考研,一切看起来都很一帆风顺,很多人都说我这是开挂的人生,我想做的事情最终都做好了,也都做成了,但是,他们却不知道,我的大学的每一天是怎么度过的。</p>
<p>我想说说我的大学的每一天,把我的经历告诉大家,分享给大家,希望可以激励到跟我一样的普通的大学生,和我一样在努力,在奋斗的大学生,我想告诉你们的是,不管你们现在所处的位置在哪里,只要你做的事情感兴趣,你努力,总有一天一定会成功的。大学的生活,我是快乐的,第一,我找到了我的兴趣,那就是我的专业-数字媒体技术,这是个很不错的专业,可以培养学生的不一样的兴趣,因为它,我开始了我的程序员的生活,第二,我的生活非常的丰富多彩,大学,我参加过社团,参加过学生会,我拿过校运会800米、1500米、5000米的前三名,我参加过歌唱比赛,我参加过编程比赛,我也去过5个城市旅游,虽然是穷游,但是很快乐,第三,我学到了技能,这是是最重要的,这也是我大学一直在努力的,因为大学的努力,学到的技能成为了我现在吃饭的家伙,也成为了我以后一直奋斗的目标。</p>
<p>这一切看似很简单,但是,其实做起来并不容易,很多人都明白一个道理,我要努力,但是,又有多少人是真的可以4年如一日的不断努力呢,我想,在大学真的很少。在大学,从大一的暑假开始,我从来没有一天离开过程序,每一天我都会学习5个小时,不管是看书,还是看视频,不管是写代码,还是其他跟学习相关的事情,正是因为这样一天一天的努力,一天一天的积累,才有了现在的我,作为一名研究生,拥有了4-5年学习Java的时间,我觉得我比别人领先了一步。</p>
<p>我想对普通的大学生说,不管你在一本学校,二本学校,三本学校,还是专科学校,<strong>只要你努力,朝着你感兴趣的方向努力,终有一天,你会发现,你已经超过了很多211、985的学生了,真的是这样的,现在我也是一名211学校的研究生,我从一个二本学校到一个211的学校,我丝毫不觉得我的能力差,反而,我的能力却是比较出众的。</strong> 所以,作为普通的大学生的我们,我们只有奋起直追,才能超过同龄人。</p>
<p>在大三的时候,我参加过蓝桥杯,当时拿了全国二等奖,但是在比赛的时候,我看到了我跟旁边的北京理工大学的差距,于是,我决定了我要考研,第一,我要通过研究生弥补我的学历的不足,第二,还要提高我的能力,算法的能力。</p>
<p>于是,在大三到大四的6个月的时间里,每天10个小时的学习,最终以初试中等成绩,进入了复试,由于我大学的对于技术的积累,复试我几乎以第一名的成绩拿到了211研究生的名额,最终以总成绩第7名考入了某211的计算机学院,其实,过程是很艰苦的,也是<strong>有很多不确定性因素的,但是,确定的是自己的实力</strong>。</p>
<h3>奋斗</h3>
<p>到现在,研究生的生活已经过去一半了,我可以说我已经做到了第一点,拿到了211研究生的学历,第二点做到了70%,我的能力又提高了,我开阔了眼界,进一步学习了机器学习,深度学习,虽然只是入门,但是,又打开了一个看世界的窗户;算法,通过这大半年的努力,真的提高了很多,一线互联网公司的手撕代码我是可以解决的,在大学的时候的我看来,这是不可能的,但是,现在我做到了,也进入到了最好的互联网公司实习。</p>
<p>另外一件事情,可能大家都知道,从大学开始,由于长时间的关注技术的动态,在大学的时候我就开始做公众号了,经过两年多的运营,不懈的努力,从2018年2月20日开始的20个粉丝,每篇文章的阅读量不到20,这还要告诉大家,这20个阅读量还是朋友圈的好朋友看的,是不是真的辛酸,但是,我没有放弃,一步一个脚印,到2019年的总结的时候,我拥有2w粉丝,再到2020年的总结的6w粉丝,再到现在,我拥有了超过10w的粉丝,这一切都不容易,但是,一切也是意料之中的,因为我一直在努力,做到问心无愧。</p>
<p>今年开始,我一直在努力做一件事情就是<strong>原创</strong>,虽然到现在的文章还不是特别多的,但是,每一篇文章都是我认认真真的推敲出来的,我觉得没有思考过的文章是没有价值的,只有自己认真思考过,才会给别人带来帮助,带来应有的价值,现在,我每一周都会利用自己的有限的时间,尽可能的写两篇以上的原创文章,原创真的不容易,所以,还需要大家的支持,有大家的支持,就会更有动力写文章了,当然,<strong>我的初心还是不变的,每一篇文章都希望能够给大家带来帮助,在你们看来是有价值的</strong>。</p>
<p>另一件事情,经过这两年的写作,我发现我的写作能力真的提高了很多,从2018年的5月20日,到2020年的5月20日,整整两年的时间,发现我写文章虽然做不到信手拈来,但是也是很得心应手了,不想2018年5月20日的第一篇分享我的想法的文章一样整整写了两三个小时。</p>
<h3>不是结束</h3>
<p>最后,2020年,还有一个小目标,在2020年的年终总结告诉大家,希望可以实现我的目标。对了,今天是520,祝大家都开开心心过节!</p>
<p>以上!</p>
<p>最后,再分享我历时<strong>三个月</strong>总结的 <strong>Java 面试 + Java 后端技术学习指南</strong>,这是本人这几年及春招的总结,已经拿到了大厂 offer,整理成了一本电子书,拿去不谢,目录如下:</p>
<p><img src="/img/bVbGjEV" alt="" title=""></p>
<p>现在免费分享大家,在下面我的公众号 <strong>程序员的技术圈子</strong> 回复 <strong>面试</strong> 即可获取。</p>
<p><img src="/img/bVbGVZk" alt="" title=""></p>
<h4>有收获?希望老铁们来个三连击,给更多的人看到这篇文章</h4>
<p>1、老铁们,关注我的原创微信公众号「<strong>程序员的技术圈子</strong>」,专注于 Java、数据结构和算法、微服务、中间件等技术分享,保证你看完有所收获。</p>
<p>2、给俺点个赞呗,可以让更多的人看到这篇文章,顺便激励下我继续写作,嘻嘻。</p>
<p>3、另外,原创文章首发在我的个人博客:<a href="https://link.segmentfault.com/?enc=7CDBTj9EvFY1i4n%2Fp4UOfg%3D%3D.fg7MqG3SjLyt0dxX3For7a9jOO7P3j%2Fo03oNRuCMRpQ%3D" rel="nofollow">blog.ouyangsihai.cn</a>,欢迎访问。</p>
<p><strong>点赞是对我最大的鼓励</strong><br>↓↓↓↓↓↓</p>
用这样的方法,我解决了leetcode的大部分的这种题型!
https://segmentfault.com/a/1190000022679787
2020-05-19T08:07:49+08:00
2020-05-19T08:07:49+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
2
<blockquote>点个赞,看一看,好习惯!本文 <strong>GitHub</strong> <a href="https://link.segmentfault.com/?enc=F0DgAcm9zHk3OhA6UI7YsA%3D%3D.H%2FpYfedRwADVbBYsqDUzZG710fz%2BVPXluPVnrwpZUu%2FJ9ypHJf%2FmwQkwqY7Rqfb%2F" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a> 已收录,这是我花了 3 个月总结的一线大厂 Java 面试总结,本人已拿大厂 offer。<br>另外,原创文章首发在我的个人博客:<a href="https://link.segmentfault.com/?enc=JP7Q%2BUw91Z7y4AteyqYCOQ%3D%3D.64s0ok2YGqcQg%2FkuSUiNUwuxrsQTSQMdCF6KEHnSevk%3D" rel="nofollow">blog.ouyangsihai.cn</a>,欢迎访问。</blockquote>
<p>今天介绍一种解决常规的贪心策略或者字典排序的题目的通用解题方法。</p>
<h4>第一题,leetcode中等难度题目</h4>
<p><img src="/img/bVbHkc8" alt="" title=""></p>
<p>先来一道简单的字典序排列的问题,这个题目我这里不会用最优解来解决这个问题,这个是leetcode的中等难度的题目,最优解还是需要再思考一下的,这道题目作为文章开头只是为了介绍我想要介绍的贪心的解题的一种思路而已,大佬请勿喷!!</p>
<p>看到这个题目,我就是想用暴力的方法解决,以便更好的理解这种解题思路。</p>
<p>先给出我的答案,非常暴力,但是非常好理解。</p>
<pre><code class="java">public List<Integer> lexicalOrder(int n) {
List<String> list = new ArrayList<>();
for(int i = 1; i <= n; i++){
list.add(i + "");
}
Collections.sort(list,(o1,o2)->{
return o1.compareTo(o2);
});
List<Integer> iList = new ArrayList<>();
list.stream().forEach((str)->{
iList.add(Integer.parseInt(str));
});
return iList;
}</code></pre>
<p>这个解题方法很简单,<strong>用的就是Collections.sort()方法的排序,然后重写一下Comparator类而已,这里用的是lambda表达式,使得代码更加的简洁</strong>。</p>
<p>最优解大家可以去leetcode看看哈,自己动手,丰衣足食。</p>
<p>所以,通过这个题目我想给出的信息就是:<code>通常涉及到字符串排序,字典序,数字排序等等的题目,都是可以用这种思路来解决问题的</code>。</p>
<p>不信,我们再看看其他题目。</p>
<h4>第二题,leetcode中等难度题目</h4>
<p><img src="/img/bVbHkc9" alt="" title=""></p>
<p>这是一道常见的topk问题,最优解也不是我给出的答案,目的只是为了阐述这种解题方法。</p>
<p>我的解题方法:<strong>用优先级队列,维护一个大小为k小顶堆,每次堆的元素到达k时,先弹出堆顶元素,这样就堆总是维持着k个最大值,最终可以的到前k高的元素。</strong></p>
<p>下面看看我的解答(注意:我的答案绝对不是最优解,只是为了阐述这种方法)</p>
<pre><code class="java">class Solution {
public int[] topKFrequent(int[] nums, int k) {
Queue<Obj> queue = new PriorityQueue<>(k,(o1,o2)->{
return o2.num - o1.num;
});
HashMap<Integer,Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++){
map.put(nums[i],map.getOrDefault(nums[i],0) + 1);
}
for(int key : map.keySet()){
queue.offer(new Obj(key,map.get(key)));
}
int[] ans = new int[k];
int i = 0;
while(i < k){
ans[i] = queue.poll().target;
i++;
}
return ans;
}
class Obj {
public int target;
public int num;
public Obj(int target, int num){
this.target = target;
this.num = num;
}
}
}</code></pre>
<p>这种方法没有维护k的最大的堆。</p>
<pre><code class="java">class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
HashMap<Integer, Integer> map = new HashMap();
for (int n: nums) {
map.put(n, map.getOrDefault(n, 0) + 1);
}
PriorityQueue<Integer> heap =
new PriorityQueue<Integer>((n1, n2) -> map.get(n1) - map.get(n2));
for (int n: map.keySet()) {
heap.add(n);
if (heap.size() > k)
heap.poll();
}
List<Integer> top_k = new LinkedList();
while (!heap.isEmpty())
top_k.add(heap.poll());
Collections.reverse(top_k);
return top_k;
}
}
</code></pre>
<p>这种方法维护k的最大的堆。</p>
<p>对比发现:<strong>不管维护k的最大堆还是不维护</strong>,核心的思想都是</p>
<pre><code class="java">Queue<Obj> queue = new PriorityQueue<>(k,(o1,o2)->{
return o2.num - o1.num;
});</code></pre>
<p>和这段代码</p>
<pre><code class="java">PriorityQueue<Integer> heap =
new PriorityQueue<Integer>((n1, n2) -> map.get(n1) - map.get(n2));</code></pre>
<p>对比第一题中的</p>
<pre><code class="java">Collections.sort(list,(o1,o2)->{
return o1.compareTo(o2);
});</code></pre>
<p>用的都是内部类:<code>Comparator</code>,然后进行<strong>构建符合题意的排序规则</strong>。</p>
<h4>第三题,更复杂点的</h4>
<p><img src="/img/bVbHkda" alt="" title=""></p>
<p>这个题目就更能明白什么是<strong>构建符合题意的排序规则</strong>。</p>
<p>因为很多题目不止让你根据一个字段进行排序,可能是两个字段进行排序,或者三个字段进行排序,所以就需要进行“构建”。</p>
<p>这个题目的解题思路:<strong>先排序再插入</strong></p>
<ul>
<li>排序规则:按照先H高度降序,K个数升序排序</li>
<li>遍历排序后的数组,根据K插入到K的位置上</li>
</ul>
<p><strong>核心思想</strong>:高个子先站好位,矮个子插入到K位置上,前面肯定有K个高个子,矮个子再插到前面也满足K的要求。</p>
<p>再看看解答</p>
<pre><code class="java">public int[][] reconstructQueue(int[][] people) {
// [7,0], [7,1], [6,1], [5,0], [5,2], [4,4]
// 再一个一个插入过程
// [7,0]
// [7,0], [7,1]
// [7,0], [6,1], [7,1]
// [5,0], [7,0], [6,1], [7,1]
// [5,0], [7,0], [5,2], [6,1], [7,1]
// [5,0], [7,0], [5,2], [6,1], [4,4], [7,1]
Arrays.sort(people, (o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0]);
LinkedList<int[]> list = new LinkedList<>();
for (int[] i : people) {
//在i位置,插入数:i[1]是[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]的第一个数,表示前面有几个比我高的。
list.add(i[1], i);
}
return list.toArray(new int[list.size()][2]);
}</code></pre>
<p>你会发现,核心代码还是跟第一题和第二题一样,只是复杂一点点。</p>
<pre><code class="java">Arrays.sort(people, (o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0]);</code></pre>
<p>这是什么意思呢:o1和o2是一个类似这样<code>[7,0]</code>的一位数组,当第一个数相等时,再比较一维数组的第二个数的大小,不相等,当然先比较第一个数了。</p>
<p>这个就是多个字段比较的例子,是不是还是跟前面的思路是一样的。</p>
<h4>总结</h4>
<p>最后发现,关于排序的,不管是,数组的排序,数字的排序,字符串的排序,还是优先级队列的排序,我们都是可以用Java的Comparator来解决的。</p>
<p>就说这么多,只是思路,不要死磕最优解!!!</p>
<p>最后,再分享我历时<strong>三个月</strong>总结的 <strong>Java 面试 + Java 后端技术学习指南</strong>,这是本人这几年及春招的总结,已经拿到了大厂 offer,整理成了一本电子书,拿去不谢,目录如下:</p>
<p><img src="/img/bVbGjEV" alt="" title=""></p>
<p>现在免费分享大家,在下面我的公众号 <strong>程序员的技术圈子</strong> 回复 <strong>面试</strong> 即可获取。</p>
<p><img src="/img/bVbGVZk" alt="" title=""></p>
<h4>有收获?希望老铁们来个三连击,给更多的人看到这篇文章</h4>
<p>1、老铁们,关注我的原创微信公众号「<strong>程序员的技术圈子</strong>」,专注于 Java、数据结构和算法、微服务、中间件等技术分享,保证你看完有所收获。</p>
<p>2、给俺点个赞呗,可以让更多的人看到这篇文章,顺便激励下我继续写作,嘻嘻。</p>
<p>3、另外,原创文章首发在我的个人博客:<a href="https://link.segmentfault.com/?enc=g0sMEuo0VewwlkMx886klw%3D%3D.KjC6urj9LEyYY2piK%2FjRoAX0swQvC8cxIZ%2FSX01JMqI%3D" rel="nofollow">blog.ouyangsihai.cn</a>,欢迎访问。</p>
<p><strong>点赞是对我最大的鼓励</strong><br>↓↓↓↓↓↓</p>
【大学到研究生自学Java的学习路线】这是一份最适合普通大众、非科班的路线,帮你快速找到一份满意的工作
https://segmentfault.com/a/1190000022563030
2020-05-07T08:30:06+08:00
2020-05-07T08:30:06+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
5
<p>这一部分,其实,以前在自己的公众号写过,但是,今天,还是想自己重新理清头绪,重新分享一下,关于<strong>我的程序员4年的那些事</strong>。</p>
<blockquote>点个赞,看一看,好习惯!本文 <strong>GitHub</strong> <a href="https://link.segmentfault.com/?enc=AHxJ1RavQupN%2FUClI%2FUqVw%3D%3D.KSjF6zWe%2BF%2FOv697FW6jKt0T72pfQsqQ%2B8hJciLvcVkNtDCzLkkvoVXcEA3NwSQ5" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a> 已收录,这是我花了3个月总结的一线大厂Java面试总结,本人已拿腾讯等大厂offer。<br>另外,原创文章首发在我的个人博客:<a href="https://link.segmentfault.com/?enc=9hWcqHNWabsHVq%2FJ979oiw%3D%3D.7iT864j5kyAUUTwmqPKoVsj00md7%2Fi0%2F01Pz%2BZew9rQ%3D" rel="nofollow">blog.ouyangsihai.cn</a>,欢迎访问。</blockquote>
<h3>00 大学学习经历</h3>
<p>本科之前,我其实从未想过自己会成为一名程序员,报了本科的专业之后,我也没有意识到我会成为一名程序员,直到大一的时候,我正式的成为了一名程序员,开始我的程序员生涯,在这里,我非常的感谢大学的班主任和物联网的一名老师,是他们让我进入了这个我喜欢的行业。</p>
<p>从大一的暑假开始,我真的开始我的程序员的事业,那个暑假开始,大学的每一天的生活,都没有脱离过Coding,每一天,几乎至少会有5个小时的时间,我是一个人在寝室或者在实验室学习的,因为,跟大多数的同行一样,作为一名普本的学生,<strong>我给自己的目标就是需要比别人更努力,成功没有诀窍,只有10年磨一剑的决心跟努力</strong>,因此,在大学的很多时候,一天的所有时间几乎都是学习学习,<code>Coding,Coding</code>,甚至每一个暑假和寒假,我几乎一天待在家里学习,一天的学习至少10小时,那时候,我觉得我很疯狂,但是,我也很开心。</p>
<p>因为,我真的喜欢Coding,我真的为本科那时候我的感到自豪,也庆幸大学我的没有浪费我的宝贵的大学时光。</p>
<p>经过一到两年的努力,我的努力也没有白费,在大三的时候,我开始去参加一些比赛,同时,也在实验室开始接手一些项目,在大三那一年,我去参加了一个名为“<strong>蓝桥杯</strong>”的比赛,最终,通过自己的努力,获得了,江西省的一等奖,最终在全国总决赛中,获得了二等奖,也许这样的比赛对于很多大佬来说,不值得一提,但是,对于当时我的来说,我自己是满意的。</p>
<p>后来,通过自己的努力,也拿下来几项软件著作权,让自己在大学的生活中,有了更多的项目经验,能力提高的同时,也让自己更加的有自信。</p>
<p><img src="/img/bVbGPPN" alt="" title=""></p>
<h3>01 如何学习Java呢?</h3>
<p>前面说了这么多,都是我的个人经历,所以,今天,我想把我大学学习的方法分享出来,避免跟我一样的大多数人走弯路。</p>
<h4>如何高效的看视频?</h4>
<p>在大学的那段时间,我摸索了很多的学习方法,最终我发现,最好的方法还是<strong>看视频</strong>,我尝试过<code>看书,做项目,看视频</code>等等,但是,最终,效率最好的方法还是看视频。</p>
<p>视频那么多,怎么看才是有效的呢?</p>
<p>这一点尤其重要,现在网上的免费的视频很多,但是,能够让自己事半功倍的却不多,因为现在网上的资源太杂了,导致你不知道从哪里开始看起。</p>
<p>所以,<strong>我今天给大家总结一个Java最佳的入门方式,看了你一定不后悔,因为我就是这么走过来的。</strong></p>
<h5>Java学习最佳路线如下(入门到项目实战的阶段)</h5>
<p><strong>1、Java基础知识</strong></p>
<p>这些知识包括:基本语法,IO,多线程,Java网络编程等。</p>
<p><img src="/img/bVbGPPO" alt="" title=""></p>
<p><strong>2、Web前端知识</strong></p>
<p>为什么需要学习这些知识呢,虽然现在很多都是前后端分离的项目,但是,有时候你还是避免不了要接触前端的知识,所以,学到了总是没有坏处,技多不压身!</p>
<p>这些知识包括:html、css,JavaScript、vue(可选)等</p>
<p><img src="/img/bVbGPPP" alt="" title=""></p>
<p><strong>3、JavaWeb知识</strong></p>
<p>这些知识都是Java最原始的技术,但是,学了这些对后面的一些框架的理解非常有帮助,虽然现在已经不用这些技术了,但是,底层框架都是这么写的呀。</p>
<p>包括:jsp、servlet等等。</p>
<p><strong>4、数据库知识学习</strong></p>
<p>数据库就不用说了,做后端开发的,离不开数据库,不管是MySQL、Oracle学一个,推荐学习MySQL,因为用的最多。</p>
<p><img src="/img/bVbGPPQ" alt="" title=""></p>
<p><strong>5、利用JavaWeb知识进行项目实战</strong></p>
<p>学完这么多的技术,如果不用起来,肯定学的枯燥,而且容易忘记,所以,做一个小的项目实战是最好的方法,这样才能体会到开发的乐趣,哈哈。</p>
<p><strong>6、ssh框架学习</strong></p>
<p>spring、springmvc、hibernate,这几个框架还是需要学习一下的,本来想说struts的,但是这个框架已经么有人用了,所以,基本不用考虑。</p>
<p><img src="/img/bVbGPPS" alt="" title=""></p>
<p><strong>7、ssm框架学习</strong></p>
<p>经典的,也是现在很常用的框架:springmvc、spring、mybatis,这几个框架基本上就是企业用的最多的,一定得好好学,当时我学习这几个框架的时候就花费了挺多时间的。</p>
<p><strong>8、Java开发常用技术学习</strong></p>
<p>这些技术在开发的过程中用的非常的多,比如,git、maven、tomcat、jekins等等。</p>
<p><strong>9、利用ssm框架进行项目实战</strong></p>
<p><img src="/img/bVbGPPT" alt="" title=""></p>
<p><strong>10、进阶:微服务学习</strong></p>
<p><img src="/img/bVbGPPU" alt="" title=""></p>
<h3>02 Java学习视频资源推荐</h3>
<p>是不是一脸懵逼,这么多,我怎么学?不用担心,我都为你准备好了,<strong>我给大家推荐一些我以前学习用过的优质的视频,一定会非常的好。</strong></p>
<table>
<thead><tr>
<th>Java学习路线</th>
<th>口令</th>
</tr></thead>
<tbody>
<tr>
<td>Java 基础:Java基础语法、JavaIO、Java多线程、Java网络、Java集合</td>
<td>Java资源</td>
</tr>
<tr>
<td>前端知识:html、js、css、vue等</td>
<td>Java资源</td>
</tr>
<tr>
<td>Javaweb:jsp、jdbc、servlet等</td>
<td>Java资源</td>
</tr>
<tr>
<td>ssh:spring、springmvc、hibernate</td>
<td>Java资源</td>
</tr>
<tr>
<td>ssh项目实战</td>
<td>Java资源</td>
</tr>
<tr>
<td>ssm:spring、springmvc、mybatis</td>
<td>Java资源</td>
</tr>
<tr>
<td>ssm项目实战</td>
<td>Java资源</td>
</tr>
<tr>
<td>Java常用技术</td>
<td>Java资源</td>
</tr>
<tr>
<td>数据库技术</td>
<td>Java资源</td>
</tr>
<tr>
<td>Java大型项目实战</td>
<td>Java资源</td>
</tr>
</tbody>
</table>
<p>这些资源去我的公众号 <strong>程序员的技术圈子</strong>,回复 <strong>Java资源</strong>,即可获取。</p>
<h3>03 看视频学习的经验</h3>
<p>这个问题主要是来源于公众号的粉丝咨询的问题,一般初学者在学习的时候都会遇到下面的问题。</p>
<h5>1、刚刚看了视频,但是,过一两天又忘记了,感觉不是自己的东西。</h5>
<h5>2、看了之后,发现还是不知道怎么写,比如说,一些api的使用,在项目中如何自己独立的去开发。</h5>
<h5>3、看了一个知识点之后,还是一知半解,总是想搞明白,陷入死循环。</h5>
<p>上面几个问题一定都是Java入门初学者常遇到的问题,如何解决这几个问题,我给大家支个招。</p>
<p>1、看视频的时候,一定不要只看,不动手,眼高手低,只有动手写了,你才会越来越熟练,所以,给大家的第一个技巧就是:<code>敲代码</code>。<br>2、还是不知道怎么写的问题,这是因为你写的还不够多,只有熟能生巧,坚持下去。<br>3、一知半解不重要,刚刚开始会用才是真理,后面再去搞明白原理,不要钻牛角尖。<br>4、<strong>写博客</strong>,这点很重要,记不住很正常,但是,如果记不住,还是不用键盘记下来的话,就真的忘记了,整理好自己学过的知识,形成博客,<strong>形成自己的知识体系,也就是自己的技术栈</strong>。</p>
<p>你只要把上面几点做好,一定可以更加轻松的学习,但是,要记住一点,<strong>学习是寂寞的,要想学好,就得耐得住寂寞,要想进步,就得坚持。</strong></p>
<h3>04 Java面试问题解决</h3>
<p>恭喜你,如果你走到了这一步,说明你已经坚持下来了,已经超越了很大部分的人,那么,当你学完了Java的基本技术,做了一些项目,有了一点项目经验之后,肯定是找工作了,毕竟我们是为了恰饭哈。</p>
<p>如果你还在为如何找到一份Java的好工作烦恼,这不,我都替您想好了。</p>
<p>经过三个月的面试,花了三个月总结,我总结了【<strong>Java 面试 + Java 后端技术学习指南</strong>】:一份通向理想互联网公司的面试指南,包括 Java,技术面试必备基础知识、Leetcode、计算机操作系统、计算机网络、系统设计、分布式、数据库(MySQL、Redis)、Java 项目实战等, 新鲜出炉!</p>
<p>此手册内容专注 <strong>Java面试</strong> ,这是本人花费了 3 个月的时间总结的【<strong>Java 面试 + Java 后端技术学习指南</strong>】,目前本人已经拿到了腾讯等大厂offer,只要大家把这上面的知识点都搞明白,进入前 50 的互联网公司是绝对没有问题的,希望对大家的面试有一定的帮助。</p>
<p>主要包括以下内容。</p>
<p><img src="/img/bVbGPPV" alt="" title=""><br><img src="/img/bVbGPPW" alt="" title=""></p>
<h5>获取方式</h5>
<p>这个<strong>Java 面试 + Java 后端技术学习指南</strong>是我花了半年的时间总结的,非常的有价值,只要把这个上面的知识都掌握好,去一个你想去的互联网公司是没有问题的,今天我免费分享出来,希望可以帮助到大家。</p>
<p>直接去我的公众号 <strong>程序员的技术圈子</strong> 回复 <strong>Java面试</strong> 即可获取 PDF 版本或者 github 版本。</p>
<h3>05 手册内容展示</h3>
<p><strong>JVM相关内容(10篇详解)</strong></p>
<p><img src="/img/bVbGM6k" alt="" title=""></p>
<p><strong>Spring Boot教程(11篇,5W+阅读量)</strong></p>
<p><img src="/img/bVbGM6l" alt="" title=""></p>
<p><strong>优质博文:SSM框架实现支付宝支付</strong></p>
<p><img src="/img/bVbGM6m" alt="" title=""></p>
<p><strong>MySQL面试解析(8篇)</strong></p>
<p><img src="/img/bVbGM6n" alt="" title=""></p>
<h5>获取方式</h5>
<p>这个<strong>Java 面试 + Java 后端技术学习指南</strong>是我花了半年的时间总结的,非常的有价值,只要把这个上面的知识都掌握好,去一个你想去的互联网公司是没有问题的,今天我免费分享出来,希望可以帮助到大家。</p>
<p>直接去我的公众号 <strong>程序员的技术圈子</strong> 回复 <strong>Java面试</strong> 即可获取 PDF 版本或者 github 版本。</p>
<h3>06 小小总结</h3>
<p>这一篇文章从Java入门,如何学习,再到项目实战,最后,再分享了一份Java面试指南,这些都是自己这4-5年的时间的积累,都是自己的亲身的经历,<strong>如果觉得文章不错,点个赞吧,原创不易</strong>!</p>
推荐一个很牛逼的 Github 项目:本人历时半年完成的【Java 面试 + Java 后端技术学习指南】,已拿大厂offer
https://segmentfault.com/a/1190000022552514
2020-05-06T09:48:29+08:00
2020-05-06T09:48:29+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
7
<p>今天给大家分享一份【<strong>Java 面试 + Java 后端技术学习指南</strong>】:一份通向理想互联网公司的面试指南,包括 Java,技术面试必备基础知识、Leetcode、计算机操作系统、计算机网络、系统设计、分布式、数据库(MySQL、Redis)、Java 项目实战等, 新鲜出炉!</p>
<p><strong>Github 地址</strong> 如下:<a href="https://link.segmentfault.com/?enc=dUJy2I%2FDSdFkxg0bTZWuZw%3D%3D.opfcCIPRGhBNd0t1k5m56XCI9QocGPBsB5hceTDP7STkCNyEhsm6TDmeyjyvoAbs" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a>,不要脸求个 <strong>star</strong> !</p>
<p>目前,我已经我这半年来的总结的知识全部都总结到了github上面,可以说基本上涉及到了Java面试的方方面面,如果想找到一份不错的工作,把我总结的这些知识点都整明白,去一个你想去的公司基本上是没有什么问题的,当然,这还是要多努力,坚持下来了,才会有更好的结果,我也希望大家都能有不错的收获。</p>
<p>最后,在2020这么困难的一年当中,还能够有进步,有收获,我时常感觉,只有每天进步,才会更加充实,更加充实,你才会更加努力,有时候也需要看到差距,看到跟别人的差距,才会督促自己更加努力地迎头赶上。</p>
<p>此手册内容专注 <strong>Java面试</strong> ,这是本人花费了半年的时间总结的【<strong>Java 面试 + Java 后端技术学习指南</strong>】,目前本人已经拿到了腾讯等大厂offer,只要大家把这上面的知识点都搞明白,进入前 50 的互联网公司是绝对没有问题的,希望对大家的面试有一定的帮助。</p>
<p><strong>Github 地址</strong> 如下:<a href="https://link.segmentfault.com/?enc=gn5b5ZX9Y2RxtAn6wmn%2Fzw%3D%3D.HLma0Ez7QwyfTQqt8quS9M3biTFW1%2B3W5Nu44bRF5j5yyO%2BbnG02yvPy5wOl33Qp" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a>,不要脸求个 <strong>star</strong> !</p>
<p>主要包括以下内容。</p>
<p><img src="/img/bVbGM6i" alt="" title=""></p>
<h3>下载方式</h3>
<p><strong>1.</strong> 首先扫描下方二维码</p>
<p><strong>2.</strong> 后台回复「<strong>Java面试</strong>」即可获取</p>
<p><img src="/img/bVbGM6j" alt="" title=""></p>
<h3>内容展示</h3>
<p><strong>JVM相关内容(10篇详解)</strong></p>
<p><img src="/img/bVbGM6k" alt="" title=""></p>
<p><strong>Spring Boot教程(11篇,5W+阅读量)</strong></p>
<p><img src="/img/bVbGM6l" alt="" title=""></p>
<p><strong>优质博文:SSM框架实现支付宝支付</strong></p>
<p><img src="/img/bVbGM6m" alt="" title=""></p>
<p><strong>MySQL面试解析(8篇)</strong></p>
<p><img src="/img/bVbGM6n" alt="" title=""></p>
<h3>下载方式</h3>
<p><strong>1.</strong> 首先扫描下方二维码</p>
<p><strong>2.</strong> 后台回复「<strong>Java面试</strong>」即可获取</p>
<p><img src="/img/bVbGM6j" alt="" title=""></p>
<p><strong>Github 地址</strong> 如下:<a href="https://link.segmentfault.com/?enc=A8uN6B6aZwc5%2BxWQDyRn9g%3D%3D.dvR6a0POlqelDe1y7QphkFw8ps50tpsOV46hnA%2FvTOSKieHewQA3Fbca7Td2cE5d" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a>,不要脸求个 <strong>star</strong> 。</p>
本人面试两个月真实经历:面试了20家大厂之后,发现这样介绍项目经验,显得项目很牛逼!
https://segmentfault.com/a/1190000022514104
2020-04-30T09:30:18+08:00
2020-04-30T09:30:18+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
17
<blockquote>点个赞,看一看,好习惯!本文 <strong>GitHub</strong> <a href="https://link.segmentfault.com/?enc=hAdLWjOuq8bJYYk72vgjRw%3D%3D.oFgurCPQIi1%2BNopyU0JxYx2Yo07ATFTlt5cscoiAK0RZ3jS13IQYCH17a8nZZz%2Ba" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a> 已收录,这是我花了3个月总结的一线大厂Java面试总结,本人已拿腾讯等大厂offer。</blockquote>
<p>上面文章写完已经0点过后了,就没有精力写完,其实,精力还是有的,主要还是怜惜自己的小命,保住头发要紧,不然双11又得团购生发洗发水了。</p>
<p>这两天发布了上一篇文章之后,有朋友说最想看的竟然在下一篇文章,这不,我快马加鞭,赶紧把第二篇文章给放出来。</p>
<p>这篇文章主要是接着上篇文章的思路写,所以,从以下几个方面展开。</p>
<ul>
<li>怎么介绍项目?</li>
<li>怎么介绍项目难点与亮点?</li>
<li>你负责的模块?</li>
<li>怎么让面试官满意?</li>
</ul>
<h3>怎么介绍项目?</h3>
<p>我在刚刚开始面试的时候,也遇到了这个问题,也是我第一个思考的问题,如何介绍自己的项目,既可以比较全面的让面试官了解这个项目,同时,也不会让面试官觉得废话太多。经过这么多的面试,我发现,一般这样的套路来介绍项目比较合适,当然,这只是我的感觉,没有最好的方式,只有最适合的方法。</p>
<p><strong>介绍项目背景 -> 介绍项目的技术栈 -> 介绍项目自己负责的模块及功能</strong></p>
<p>按照这样的思路去介绍项目。</p>
<p>首先<strong>介绍项目背景</strong>,可以让面试官简单的了解这个项目背景,当然,一定要记住的是不要本末倒置,背景只需要简单的介绍,后面的才是重点。</p>
<p>其次,<strong>介绍项目中的技术栈</strong>,比如你使用的是什么框架,是ssm,还是springboot等,还有有没有用缓存框架redis,分布式是否有涉及,这些都需要介绍,最好简要的介绍一下用这些技术的原因,这样会让面试官感觉你对这个项目很熟悉,比如可以这样介绍。</p>
<blockquote>我这个项目使用的框架是ssm,其中用到了activiti工作流框架,这是因为项目中有很多的工作审批业务,同时用到了shiro框架,另外,为了实现单点登录功能,加入了redis框架。</blockquote>
<p>上面只是一个示例,只要是介绍一下为什么要用这个技术,不会很生硬。</p>
<p>最后,<strong>介绍你在这个项目中负责的模块</strong>,这一点还是要特别注意的,介绍这个的时候就能够看出你所负责的模块的难度,也就是你所做的工作的难度。</p>
<p>如果你这样介绍:<strong>我负责的是一个用户模块,包括用户的增删改查,还有就是用户的登录功能</strong>。</p>
<p>面试官一听,你这做的工作没有任何难度,体现不了你的能力,跟你在这个项目中的作用跟自己的价值,面试官肯定是不想听到这样的答案的。</p>
<p>因此,在介绍自己负责的模块的时候,尽量是一些业务比较复杂的,工作量比较大的模块,整个模块的难度是比较大的,这样的话,面试官听了之后才会有兴趣继续往下问下去。</p>
<p>做到以上三点,面试官心里估计已经对你的项目已经有了很大的认可,至少不会认为是去忽悠他的,哈哈。</p>
<p><strong>我们都是很诚实的,不存在忽悠的哈。。。</strong></p>
<p><img src="/img/bVbGC6G" alt="" title=""></p>
<h3>怎么介绍项目难点与亮点?</h3>
<p><strong>本来是分为难点与亮点两个来讲的,但是思考了一下,发现其实还是有很大的共性的,所以就放在一起来讲。</strong></p>
<p>面试了这么长的时间,不,应该说被怼了这么长的时间,我就是这惨,每每问到项目的时候,当我介绍完了项目,然后,就有了面试官的一句经典难忘的话:“<strong>你能介绍一下你的项目的难点吗</strong>”?</p>
<p>我。。。犹豫一下之后,怎么又是这个问题,我太难了!</p>
<p><img src="/img/bVbGC6H" alt="" title=""></p>
<p>但是,经过我这么多次的面试经验之后,我总算是把这个问题给解决了,后来面试官问我的时候,都把它安排的服服帖帖的,哈哈,玩归玩,闹归闹,千万别拿面试开玩笑!!!</p>
<p>我这里说一个思路:<strong>其实,面试官问你的项目难点,无非就是想要看看你对于这个项目的了解程度,所以,其实,很多时候只要把我们学过的知识,然后再跟实际的项目进行融会贯通,解决这个问题就非常的简单了。</strong></p>
<p>这里给出一个例子:</p>
<blockquote>比如你说,我在项目中有对数据库进行优化,然后,你把整个优化的过程说一遍,你再说以前没有接触过这方面的工作,有一定的挑战,而且数据量特别大,但是,通过自己的摸索,最终解决了问题,取得了不错的效果,当然,最主要的还是你讲述你优化的过程,能够让面试官感觉你真的有做这个工作;讲述的期间最好能够把你学过的知识用上,比如说,索引,数据库的设计等等。</blockquote>
<p>就这样,难点也给你解决了。</p>
<p><img src="/img/bVbGC6I" alt="" title=""></p>
<h3>你负责的模块?</h3>
<p>对于这一点,其实还是比较好说的,只要注意不要把自己做的很简单的模块说出来,然后给面试官的感觉就是没有任何的技术含量,所以,你应该这样介绍你在这个项目中负责的模块。</p>
<p>1、这个你负责的模块应该是这个项目中比较<strong>核心的模块</strong>,记住,不要是,用户模块,这样的简单的增删改查。<br>2、你负责的模块应该是<strong>有一定的难度的</strong>,或者说对于你来说有一定得难度,最后你克服了难关,实现了功能。<br>3、你负责的模块应该是<strong>有一些难点的</strong>,这样好让面试官问问你啊,然后,你就可以接着吹牛皮了。</p>
<p>当然,面试官之前有一件事一定要做,那就是要熟悉整个项目,同时,也要把项目中用到的技术非常熟悉,当面试官问到项目的时候,可能会随便问一项技术的问题,如果你没有做好准备,你肯定回答不出来,这样的结果就会导致面试官会觉得你对于这个项目不够熟悉。</p>
<pre><code class="java">if(熟悉)
next
else
out</code></pre>
<p>自己体会体会哈哈,</p>
<h3>怎么让面试官满意?</h3>
<p>这个问题看到这里其实就已经解决了,在做到上面的几面之外,只要自己注意交流沟通得当,那么,面试官应该会对这次面试满意,所以,你应该就有好消息了,<strong>恭喜你喜提offer</strong>!</p>
<p><img src="/img/bVbGC6N" alt="" title=""></p>
<p><strong>为了写这篇文章马不停蹄,如果有不足的地方欢迎指教,如果有一丁点作用,麻烦点个赞,原创不易!</strong></p>
<p>最后,再分享我历时<strong>三个月</strong>总结的 <strong>Java 面试 + Java 后端技术学习指南</strong>,这是本人这几年及春招的总结,已经拿到了大厂offer,整理成了一本电子书,拿去不谢,目录如下:</p>
<p><img src="/img/bVbGjEV" alt="" title=""></p>
<p>现在免费分享大家,在我的公众号 <strong>好好学java</strong> 回复 <strong>Java面试</strong> 即可获取。</p>
<h4>有收获?希望老铁们来个三连击,给更多的人看到这篇文章</h4>
<p>1、老铁们,关注我的原创微信公众号「<strong>好好学java</strong>」,专注于Java、数据结构和算法、微服务、中间件等技术分享,保证你看完有所收获。</p>
<p>2、给俺点个赞呗,可以让更多的人看到这篇文章,顺便激励下我继续写作,嘻嘻。</p>
<p><strong>点赞是对我最大的鼓励</strong><br>↓↓↓↓↓↓</p>
【拥抱大厂系列】百度面试官问过的 “JVM内存分配与回收策略原理”,我用这篇文章搞定了
https://segmentfault.com/a/1190000022503399
2020-04-29T09:43:45+08:00
2020-04-29T09:43:45+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
18
<p>在前面的一篇文章<a href="https://link.segmentfault.com/?enc=xwxDuyNLn2hz0XXm%2BR0efg%3D%3D.ju%2FyB9W5MeGkfhGm9Snwq9%2BAG%2F8m38aROrXZoGEO2vGDfSg8kFZrHqmOso0hpl6nzjKlBJ9EdT72r0wBAs9X3wl7EKOAMck9znF1SPH37Fe4PFQwGO%2BVe68CvezxEoDMR%2BfAM0v5z33GyzQ0jd5i17%2FBMDA6Kr3ezRkgDMQrjfKi16L9nlOBkErQTNIzkxPk" rel="nofollow">深入理解Java虚拟机-如何利用VisualVM进行性能分析</a>中讲到了一些关于JVM调优的知识,但是,其实,还是有一些问题没有非常清楚的可以回答的,这里先给出几个问题,然后,我们再展开这篇文章需要讲解的知识。</p>
<ul>
<li>我们生成的对象最开始在哪分配?Eden?Survivor?还是老年代呢?</li>
<li>进入到老年代需要满足什么条件呢?</li>
</ul>
<p>接下来,我们就带着这两个问题展开全文。</p>
<h3>1 对象优先在哪分配</h3>
<p>其实,通过前面几篇文章的讲解,这个问题其实已经见怪不怪了,在大多数的情况下,对象都是在新生代<strong>Eden区</strong>分配的,在前面的文章我们提到,在Eden区中如果内存不够分配的话,就会进行一次<code>Minor GC</code>。同时,我们还知道年轻代中默认下<code>Eden:Survivor0:Survivor2 = 8:1:1</code>,同时,还能通过参数<code>-XX:SurvivorRatio</code>来设置这个比例(关于这些参数的分析都可以查看这篇文章:<a href="https://link.segmentfault.com/?enc=LzeaWf%2BdUsqfwksM89ev9Q%3D%3D.HUrWjZcIVM9jHGOp3NpMNYxcrsgcVuZQtMLXbl1uNfNll%2BkOIIvyERN3jIew8hGkRr92nctgm6ufe2qw3nfWSaig3WFSGCF9lsD3BxByID86zRkfg%2Froxs4jllfN2o0%2F" rel="nofollow">深入理解Java虚拟机-常用vm参数分析</a>)。</p>
<p>下面我们通过一个例子来分析是不是这样的。</p>
<h4>1.1 实例</h4>
<blockquote>给定JVM参数:-Xms40M -Xmx40M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=4</blockquote>
<p>前面三个参数设置Java堆的大小为40M,新生代为10M,紧跟着后面两个是用于输入GC信息。更多参数可以查看这篇文章:<a href="https://link.segmentfault.com/?enc=Pg1vC74DY2npD8S9aEhBfA%3D%3D.qU2K1k%2Bbnk1KvZQktqbWbhhFsNSsJJEe2aDestvL%2FUUy0nFF%2F2G%2B79gof18bwMpcvcTqy3vaPvgJi%2Bmgs4QPIVYtfJ0mCx1EjXFvRYcLRg3xgWEbjj2lUjIH2h540kKy" rel="nofollow">深入理解Java虚拟机-常用vm参数分析</a>。</p>
<pre><code class="java">/**
* @ClassName Test_01
* @Description 参数:-Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:+UseSerialGC -XX:SurvivorRatio=8
* @Author 欧阳思海
* @Date 2019/12/3 16:00
* @Version 1.0
**/
public class Test_01 {
private static final int M = 1024 * 1024;
public static void test() {
byte[] alloc1, alloc2, alloc3, alloc4;
alloc1 = new byte[5 * M];
alloc2 = new byte[5 * M];
alloc3 = new byte[5 * M];
alloc4 = new byte[10 * M];
}
public static void main(String[] args) {
test();
}
}
</code></pre>
<p>输入结果:<br><img src="/img/bVbGAj8" alt="" title=""></p>
<p><strong>分析</strong></p>
<ul>
<li>eden:from:to=8:1:1,这个因为前面设置了参数<code>-XX:SurvivorRatio=8</code>。</li>
<li>新生代分配了20M的内存,所以前面三个<code>byte</code>数组可以分配,但是,分配第四个的时候,空间不够,所以,需要进行一次<code>Minor GC</code>,GC之后,新生代从<code>12534K</code>变为<code>598K</code>。</li>
<li>前面在新生代分配的内存<code>Minor GC</code>之后,进入到了<code>Survivor</code>,但是,Survivor不够分配,所以进入到了<code>老年代</code>,老年代已用内存达到了<code>50%</code>。</li>
</ul>
<h4>1.2 回答问题</h4>
<p>所以,经过上面的例子我们发现,对象一般优先在新生代分配的,如果新生代内存不够,就进行Minor GC回收内存。</p>
<blockquote>点个赞,看一看,好习惯!本文 <strong>GitHub</strong> <a href="https://link.segmentfault.com/?enc=58NYAruOUoCwK23jVCMarw%3D%3D.sq3DwgzcYSKDMN0xRJiH4Pd7SQedc2UZc4a5d1uRJZmNFq%2BLpRWi0j%2B5GMr%2B95sT" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a> 已收录,这是我花了3个月总结的一线大厂Java面试总结,本人已拿腾讯等大厂offer。</blockquote>
<h3>2 进入到老年代需要满足什么条件</h3>
<p>先给出答案,分为几点。</p>
<ul>
<li>
<strong>条件①</strong>:大对象直接进入到老年代</li>
<li>
<strong>条件②</strong>:长期存活的对象可以进入到老年代</li>
<li>
<strong>条件③</strong>:如果在Survivor空间中相同年龄所有对象的大小的总和大于Survivor空间的一半,年龄大于等于该年龄的对象直接进入到老年代</li>
</ul>
<h4>2.1 分析条件①</h4>
<ul><li>哪些属于大对象呢?</li></ul>
<p>一般来说大对象指的是<strong>很长的字符串及数组</strong>,或者<strong>静态对象</strong>。</p>
<ul><li>那么需要满足多大才是大对象呢?</li></ul>
<p>这个虚拟机提供了一个参数<code>-XX:PretenureSizeThreshold=n</code>,只需要大于这个参数所设置的值,就可以直接进入到老年代。</p>
<p><strong>step1:</strong> 解决了这两个问题,首先,我们不设置上面的参数的例子,将对象的内存大于Eden的大小看看情况。</p>
<pre><code>/**
* @ClassName Test_01
* @Description 参数:-Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
* @Author 欧阳思海
* @Date 2019/12/3 16:00
* @Version 1.0
**/
public class Test_01 {
private static final int M = 1024 * 1024;
public static void test() {
byte[] alloc1, alloc2, alloc3, alloc4;
// alloc1 = new byte[5 * M];
// alloc2 = new byte[5 * M];
// alloc3 = new byte[5 * M];
alloc4 = new byte[22 * M];
}
public static void main(String[] args) {
test();
}
}</code></pre>
<p><img src="/img/bVbGAj9" alt="" title=""></p>
<p>我们发现分配失败,Java堆溢出,因为超过了最大值。</p>
<p><strong>step2:</strong> 下面我们看一个例子:设置<code>-XX:PretenureSizeThreshold=104,857,600</code>,这个单位是B字节(Byte/bait),所以这里是<code>100M</code>。</p>
<pre><code>/**
* @ClassName Test_01
* @Description 参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:PretenureSizeThreshold=104,857,600
* @Author 欧阳思海
* @Date 2019/12/3 16:00
* @Version 1.0
**/
public class Test_01 {
private static final int M = 1024 * 1024;
public static void test() {
byte[] alloc1, alloc2, alloc3, alloc4;
// alloc1 = new byte[5 * M];
// alloc2 = new byte[5 * M];
// alloc3 = new byte[5 * M];
alloc4 = new byte[500 * M];
}
public static void main(String[] args) {
test();
}
}</code></pre>
<p><img src="/img/bVbGAka" alt="" title=""></p>
<p>发现新生代没有分配,直接在老年代分配。</p>
<blockquote>
<strong>注意:</strong> 参数<code>PretenureSizeThreshold</code>只对<code>Serial</code>和<code>ParNew</code>两款收集器有效。</blockquote>
<h4>2.2 分析条件②</h4>
<p>进入老年代规则:<strong>这里需要知道虚拟机对每个对象有个对象年龄计数器,如果对象在Eden出生经过第一次Minor GC后任然存活,并且能够被Survivor容纳,将被移动到Survivor空间中,并且年龄设置为1。接下来,对象在Survivor中每次经过一次Minor GC,年龄就增加1,默认当年龄达到15,就会进入到老年代。</strong></p>
<p>晋升到老年代的年龄阈值,可以通过参数<code>-XX:MaxTenuringThreshold</code>设置。</p>
<p>在下面的实例中,我们设置<code>-XX:MaxTenuringThreshold=1</code>。</p>
<pre><code>/**
* @ClassName Test_01
* @Description 参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:MaxTenuringThreshold=1
* @Author 欧阳思海
* @Date 2019/12/3 16:00
* @Version 1.0
**/
public class Test_01 {
private static final int M = 1024 * 1024;
public static void test() {
byte[] alloc1, alloc2, alloc3, alloc4;
alloc1 = new byte[300 * M];
alloc2 = new byte[300 * M];
alloc3 = new byte[300 * M];
alloc4 = new byte[500 * M];
}
public static void main(String[] args) {
test();
}
}
</code></pre>
<p><img src="/img/bVbGAkb" alt="" title=""></p>
<p>从结果可以看出,from和to都没有占用内存,而老年代则占用了很多内存。</p>
<h4>2.3 分析条件③</h4>
<p><strong>条件③是</strong>:如果在Survivor空间中相同年龄所有对象的大小的总和大于Survivor空间的一半,年龄大于等于该年龄的对象直接进入到老年代,而不需要等到参数<code>-XX:MaxTenuringThreshold</code>设置的年龄。</p>
<p><strong>实例分析</strong></p>
<pre><code>/**
* @ClassName Test_01
* @Description 参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
* @Author 欧阳思海
* @Date 2019/12/3 16:00
* @Version 1.0
**/
public class Test_01 {
private static final int M = 1024 * 1024;
public static void test() {
byte[] alloc1, alloc2, alloc3, alloc4;
alloc1 = new byte[100 * M];
alloc2 = new byte[100 * M];
//分配alloc3之前,空间不够,所以minor GC,接着分配alloc3=900M大于Survivor空间一半,直接到老年代。
alloc3 = new byte[900 * M];
// alloc4 = new byte[500 * M];
}
public static void main(String[] args) {
test();
}
}
</code></pre>
<p>输入结果:<br><img src="/img/bVbGAkc" alt="" title=""></p>
<p>分配alloc3之前,空间不够,所以minor GC,接着分配alloc3=900M大于Survivor空间一半,直接到老年代。从而发现,survivor占用0,而老年代占用900M。</p>
<h3>3 总结</h3>
<p>这篇文章主要讲解了JVM内存分配与回收策略的原理,回答了下面的这两个问题。</p>
<ul>
<li>我们生成的对象最开始在哪分配?Eden?Survivor?还是老年代呢?</li>
<li>进入到老年代需要满足什么条件呢?</li>
</ul>
两个月的面试真实经历,告诉大家如何能够进入到大厂工作?
https://segmentfault.com/a/1190000022493080
2020-04-28T10:19:23+08:00
2020-04-28T10:19:23+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
5
<p>为什么今天想说说这个话题呢?</p>
<p>并不是因为我进入了大厂工作,哈哈哈!!!</p>
<p>只是因为今天在我的<strong>Java开发交流微信群</strong>里面分享了关于我整个面试过程的总结及经验,这些总结都已经整合到 github 去了,在短短的几天的时间里,github 的 star 已经超过了 100+,说明还是对大家有帮助的,如果大家想要去看看,地址在这里:<a href="https://link.segmentfault.com/?enc=YnV9HuGOwZQciUHdGRjVOw%3D%3D.bvdc492CKTeOL4FpEBeK%2FVD36ePB4OVJp1xkfDg1IJU4k9AAf%2BeLm6RZn0Gcr3Ml" rel="nofollow">https://github.com/OUYANGSIHA...</a>(注意:现在还不是很完善,很多是自己的原创文章,有一些是自己看到的比较有质量的文章,欢迎 star。)</p>
<p>那么,今天在微信群里面分享了之后,也看到了大家很多的疑问,所以,这篇文章就来聊聊可能很多校招或者部分社招都会遇到的问题,如果有不恰当的地方,欢迎讨论。</p>
<p>我先来聊聊第一个问题。</p>
<h3>对于很多人来说,我觉得最大的难点是刷题</h3>
<p><img src="http://image.ouyangsihai.cn/FsJfnyQaZp7UeNBuzwWfB0B3jd9J" alt="" title=""></p>
<p>今天在群里分享经验的时候,当我谈到至少刷 leetcode 100 题的时候,很多人都觉得还是很难的,确实,其实这一点对于很多同学来说是有一定的困难的,如果是转专业来的非科班的同学,面对这么苦涩难懂的题目,怕的是头发渐渐的稀少。</p>
<p>其实,这个问题我是感同身受的,当我在读大学的时候,我就自己尝试去找工作,最终也是找到了一份不错的国企,但是,那时候,我最缺乏的能力应该就是算法的能力,也就是面试的时候大家所说的手撕代码环节,那个时候在面试的时候遇到一个很简单的问题,我竟然会没有思路,在现在看来很简单,那个时候面试遇到的手撕代码简直就是送分题好不好,在大三的时候,我去厦门找实习的时候,也遇到一些手撕代码的,那时候我是绞尽脑汁,还是被面试官一顿怼,说你编码能力太差了,那时候我是崩溃的好吧。</p>
<blockquote><strong>为什么我现在觉得那时候的题目很简单呢?</strong></blockquote>
<p>是不是现在的面试出的题目更难了呢,那是肯定的,因为我现在面试的公司都是互联网公司前20以内的,其他的都没有考虑,不吹牛不会舒服是吧。。哈哈,以前我面试的公司最多也就是中型企业,国企这些,所以手撕代码题目难度肯定是加大了的。</p>
<p>其实,我想告诉大家的是,是我从去年10月份开始,我就开始准备面试的事情,年前,我就刷了一本左神的《程序员代码面试指南》,非常建议大家刷刷这本书,到现在这本书我已经刷了3遍了,而且告诉大家,这本书中的代码都是 Java 编写的,市面上是不是很少这样的书,真的发现宝藏了。</p>
<p>你以为我就看了这本书吗,那肯定是不够的,当我刷了3遍这本书之后,第一次面试快手,就被快手面试官怼了,当时他是这样说的:“<strong>嗯,同学,你掌握的技术广度和深度,还有你的表达能力都是相当不错的,但是,你的编码能力,也就是手撕代码好像不是提特别熟练</strong>”。</p>
<p>我。。。难受啊,就这样结束了我的第一次面试,虽然后面进入到了hr面,但是,这次经历还是让我难以忘怀啊,怎么能让别人说我写代码能力不行呢,我就是代码机器啊,表示不服。</p>
<p><img src="http://image.ouyangsihai.cn/FtPxyguIbdprniTlid3lL0kJ9MKG" alt="" title=""></p>
<p>所以,这次面试之后,我就痛定思痛,一定要多到线上写写leetcode代码。</p>
<p>结果就有了长达10天的牛客网《剑指offer》刷题训练。</p>
<p><img src="http://image.ouyangsihai.cn/FiQDTPtJu7yzp23UpnkSzxOATql5" alt="" title=""></p>
<p>没错,就是它,因为他们都说这个是必刷的,所以,我也就从它开始呗。</p>
<blockquote>讲了这么多,不是给大家讲故事哦,而是告诉大家刷题的重要性,我就是因为没有“刷到位”,而遇到了面试的“悲惨故事”。</blockquote>
<p>到现在,虽然我已经刷了300+了,但是,现在每天还是会花2个小时刷5道题。</p>
<p>最后总结一下:<strong>这个我自己真实的故事告诉我,手撕代码其实不难,你看我也不是从一点也不会,到熟练的手撕代码吗,是的,只要你坚持三个月刷题,这个问题就不是问题,听我的准没错,当然,也有技巧,后面再分享。</strong></p>
<h3>另外一个问题,我是学生,没有项目怎么办?</h3>
<p><img src="http://image.ouyangsihai.cn/FrivgqC-YCICS66px98CQhYWxUZL" alt="" title=""></p>
<p>刷题的问题都给你解决了,你还怕没有项目吗?</p>
<p>首先,如果你没有看过我以前的那篇介绍如何准备项目的文章,这里再贴一下地址(建议看看):<a href="https://link.segmentfault.com/?enc=sb6YBjzujzb%2FCh6l8ClUVA%3D%3D.KHtjH81SAhZE6%2F4Nshz2zfkjrTSnxFKUUFQMDo0VCHp7xnGYSrIJUMDrMk65i09SBimuhOTfhWZvtGr%2B20Aqnw%3D%3D" rel="nofollow">找工作,没有上的了台面的项目怎么办?</a></p>
<p>没有条件,创造条件,没有困难,创造困难,没有项目,当然,我们得找或者创建项目了。</p>
<h5>首先,项目哪里来?</h5>
<p>现在网上好多免费的项目,开源的项目,clone下来,自己研究研究,整体把握一下,就可以变成自己的项目经验了,是不是so easy呢?</p>
<p>忘了告诉你,我就是这样搞的,照样可以把把面试官安排的明明白白的。</p>
<p><img src="http://image.ouyangsihai.cn/FlY-So2khQK_g_s1XMnZa1oags6I" alt="" title=""></p>
<p>你是不是想说,有没有视频教程的呢,那我肯定为你准备好了,去我的github:<a href="https://link.segmentfault.com/?enc=BvVibb2wkw2jza%2FuvkzjGw%3D%3D.twYaMstZWVLiogdQeQtcn27LM4iUIdeOawn3UxBVpuOcEeWL7OcekTESK2ZJgSV7" rel="nofollow">https://github.com/OUYANGSIHA...</a>,项目实战推荐模块下,就给你准备好了,别忘记偷偷的给我一个<strong>星星</strong>哦。</p>
<h5>怎么介绍项目?</h5>
<h5>怎么介绍项目难点?</h5>
<h5>怎么介绍项目亮点?</h5>
<h5>你负责的模块?</h5>
<h5>怎么让面试官满意?</h5>
<p>这些问题我都思考过,不过,这篇文章先不说了,现在已经晚上0点了,我得怜惜我的头发,下次通通解决。</p>
<h3>不是总结的总结</h3>
<p>你说你手撕代码撕得好,项目经验丰富,对于我们Java程序员来说,Java知识能难倒我吗,不存在的,那么问题来了,我们能进大厂吗?</p>
<p><img src="http://image.ouyangsihai.cn/Ftek33tRVe7ebUsI3hTeBNNWE-lq" alt="" title=""></p>
【拥抱大厂系列】几个面试官常问的垃圾回收器,下次面试就拿这篇文章怼回去!
https://segmentfault.com/a/1190000022438925
2020-04-23T09:51:55+08:00
2020-04-23T09:51:55+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
1
<blockquote>点个赞,看一看,好习惯!本文 <strong>GitHub</strong> <a href="https://link.segmentfault.com/?enc=WnowYHo%2FFfo2%2BzEHT%2BdAaA%3D%3D.9ZVnFHsykX597L8YOjMUHef6puK8JdRfIeKGp0B29%2FS1IKu7fIopom1cbHWN%2BdqH" rel="nofollow">https://github.com/OUYANGSIHAI/JavaInterview</a> 已收录,这是我花了3个月总结的一线大厂Java面试总结,本人已拿腾讯等大厂offer。</blockquote>
<p>先瞎比比一下,上一篇文章已经过去2个多月了,你大概会问我这段时间干什么去了,怎么没有更新文章,那我告诉你,当然是面试去了,经过了一个多月的面试,身经百战,已经拿了几个offer,现在是时候把前段时间的知识储备给大家分享出来了,顺便也换种形式来给大家讲讲相关的知识。</p>
<p>接下来的每篇文章都会是先给出几个问题,然后再知识点,然后再回答问题这样的形式。</p>
<h3>1 问题</h3>
<p>这一段面试的时间面了很多的互联网公司的大厂,也很幸运拿了几个offer,现在也还是面试的过程中,可以说,这么多的面试,Java虚拟机是一个必问的知识点,而垃圾回收器更是重中之重,如果面试官抛出一个垃圾回收器的问题,你一脸懵逼,那估计这个面试是凉了。</p>
<p>比如,面试官一上来就狠狠的问了这几个问题。</p>
<ul>
<li>你可以介绍一下Java虚拟机的垃圾回收器吗?</li>
<li>你可以介绍一下CMS垃圾回收器的原理吗?</li>
<li>你可以介绍一下G1垃圾回收器的原理吗,跟CMS有什么区别?</li>
</ul>
<p>再来个深一点的问题?</p>
<ul><li>CMS垃圾回收器哪个阶段最耗时,会不会出现stw的问题呢?</li></ul>
<p>没有看过这些知识点是不是一脸懵逼。</p>
<p>好了,接下来我来讲讲这些垃圾回收器都是什么神仙,面试官为什么喜欢死磕这个呢?</p>
<h3>2 死磕垃圾回收器</h3>
<p>先上一张图,这张图是Java虚拟机的jdk1.7及以前版本的所有垃圾回收器,也可以说是比较成熟的垃圾回收器,除了这些垃圾回收器,面试的时候最多也就再怼怼G1和ZGC了。</p>
<p><img src="http://image.ouyangsihai.cn/Fh4MDY7P-TO1tQgtUQQJyykjibCZ" alt="" title=""></p>
<p>上面的表示是年轻代的垃圾回收器:Serial、ParNew、Parallel Scavenge,下面表示是老年代的垃圾回收器:CMS、Parallel Old、Serial Old,以及不分老年代和年轻代的G1。之间的相互的连线表示可以相互配合使用。</p>
<p>说完是不是一篇明朗,其实也就是那么回事。</p>
<h4>2.1 新生代垃圾回收器</h4>
<h5>2.1.1 Serial</h5>
<p>Serial(串行)收集器是<strong>最基本、发展历史最悠久</strong>的收集器,它是采用<strong>复制算法</strong>的新生代收集器,曾经(JDK 1.3.1之前)是虚拟机新生代收集的唯一选择。它是一个<strong>单线程收集器</strong>,只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集时,必须暂停其他所有的工作线程,直至Serial收集器收集结束为止(“Stop The World”)。</p>
<p>其实对于这个垃圾回收器,你只要记住是一个<strong>单线程、采用复制算法的,会进行“Stop The World”</strong> 即可,因为面试官一般不问这个,为什么,因为太简单了,没什么可问的呗。</p>
<p>好了,再放一张图好吧,说明一下Serial的回收过程,完事。</p>
<p><img src="http://image.ouyangsihai.cn/FtIw-Pz3PDNJlRp2v89XfIzxJckG" alt="" title=""></p>
<p>说明:这张图的意思就是<strong>单线程,新生代使用复制算法标记、老年代使用标记整理算法标记</strong>,就是这么简单。</p>
<h5>2.1.2 ParNew</h5>
<p><strong>ParNew收集器就是Serial收集器的</strong>多线程<strong>版本</strong>,它也是一个新生代收集器。除了使用多线程进行垃圾收集外,其余行为包括Serial收集器可用的所有控制参数、收集算法(复制算法)、Stop The World、对象分配规则、回收策略等与Serial收集器完全相同。</p>
<p>需要注意一点是:<strong>除了Serial收集器外,目前只有它能和CMS收集器(Concurrent Mark Sweep)配合工作。</strong></p>
<p>最后再放一张回收过程图;</p>
<p><img src="http://image.ouyangsihai.cn/FvsKnXGzEQd6WYdUmIgLcWoSHG4H" alt="" title=""></p>
<p><em>*</em> 是不是很简单,我在这里讲这些知识点并不是为了深入去了解这些原理,基本的知道对于工作已经够了,其实,主要还是应付面试官,哈哈。</p>
<h5>2.1.3 Parallel Scavenge</h5>
<p>Parallel Scavenge收集器也是一个<strong>并行</strong>的<strong>多线程</strong>新生代收集器,它也使用<strong>复制算法</strong>。</p>
<p>Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间。</p>
<p>这里需要注意的唯一的区别是:Parallel Scavenge收集器的目标是<strong>达到一个可控制的吞吐量(Throughput)</strong>。</p>
<p>我们知道,停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验。而<strong>高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务</strong>。</p>
<h4>2.2 老年代垃圾回收器</h4>
<h5>2.2.1 Serial Old</h5>
<p>Serial Old 是Serial收集器的老年代版本,它同样是一个<strong>单线程</strong>收集器,使用“<strong>标记-整理</strong>”(Mark-Compact)算法。</p>
<p>在这里就可以出一个面试题了。</p>
<ul><li>为什么Serial使用的是<strong>复制算法</strong>,而Serial Old使用是<strong>标记-整理</strong>算法?</li></ul>
<p>同一个爸爸,儿子长的天差地别,当然也有啊,哈哈。</p>
<blockquote>其实,看了我前面的文章你可能就知道了,因为在新生代绝大多数的内存都是会被回收的,所以留下来的需要回收的垃圾就很少了,所以复制算法更合适,你可以发现,基本的老年代的都是使用标记整理算法,当然,CMS是个杂种哈。</blockquote>
<p>它的工作流程与Serial收集器相同,下图是Serial/Serial Old配合使用的工作流程图:</p>
<p><img src="http://image.ouyangsihai.cn/FtIw-Pz3PDNJlRp2v89XfIzxJckG" alt="" title=""></p>
<h5>2.2.2 Parallel Old</h5>
<p>Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用<strong>多线程</strong>和“<strong>标记-整理</strong>”算法,是不是前面说的,老年代出了杂种CMS不是“<strong>标记-整理</strong>”算法,其他都是。</p>
<p>另外,有了Parallel Old垃圾回收器后,就出现了以“<strong>吞吐量优先</strong>”著称的“男女朋友”收集器了,这就是:<strong>Parallel Old和Parallel Scavenge收集器的组合</strong>。</p>
<p>Parallel Old收集器的工作流程与Parallel Scavenge相同,这里给出Parallel Scavenge/Parallel Old收集器配合使用的流程图:</p>
<p><img src="http://image.ouyangsihai.cn/Fv96fnkSnbV85BS2E6mr_ZFBd9We" alt="" title=""></p>
<p>你是不是以为我还要讲CMS和G1,我任性,这几个面试重点还是得死磕它,下回分解哈。</p>
<h3>3 总结</h3>
<p>这里把上面的这些垃圾回收器做个总结,看完这个,面试给面试官讲的时候思路就非常清晰了。</p>
<table>
<thead><tr>
<th>收集器</th>
<th>串行、并行or并发</th>
<th>新生代/老年代</th>
<th>算法</th>
<th>目标</th>
<th>适用场景</th>
</tr></thead>
<tbody>
<tr>
<td><strong>Serial</strong></td>
<td>串行</td>
<td>新生代</td>
<td>复制算法</td>
<td>响应速度优先</td>
<td>单CPU环境下的Client模式</td>
</tr>
<tr>
<td><strong>Serial Old</strong></td>
<td>串行</td>
<td>老年代</td>
<td>标记-整理</td>
<td>响应速度优先</td>
<td>单CPU环境下的Client模式、CMS的后备预案</td>
</tr>
<tr>
<td><strong>ParNew</strong></td>
<td>并行</td>
<td>新生代</td>
<td>复制算法</td>
<td>响应速度优先</td>
<td>多CPU环境时在Server模式下与CMS配合</td>
</tr>
<tr>
<td><strong>Parallel Scavenge</strong></td>
<td>并行</td>
<td>新生代</td>
<td>复制算法</td>
<td>吞吐量优先</td>
<td>在后台运算而不需要太多交互的任务</td>
</tr>
<tr>
<td><strong>Parallel Old</strong></td>
<td>并行</td>
<td>老年代</td>
<td>标记-整理</td>
<td>吞吐量优先</td>
<td>在后台运算而不需要太多交互的任务</td>
</tr>
</tbody>
</table>
<p>好了,这回就到这里了,开头的几个问题,你会了吗?</p>
<p>另外,我花了3个月时间把Java学习和面试的总结整理成了一本电子书!目录如下</p>
<p><img src="http://image.ouyangsihai.cn/FsgkJDzTT6xA7Ik84h6zYRg4oW6L" alt="" title=""></p>
<p>现在免费分享大家,在我的公众号<strong>好好学java</strong>回复<strong>Java面试</strong>即可获取。</p>
<h4>有收获?希望老铁们来个三连击,给更多的人看到这篇文章</h4>
<p>1、老铁们,关注我的原创微信公众号「<strong>好好学java</strong>」,专注于Java、数据结构和算法、微服务、中间件等技术分享,保证你看完有所收获。</p>
<p>2、给俺点个赞呗,可以让更多的人看到这篇文章,顺便激励下我继续写作,嘻嘻。</p>
<p><strong>点赞是对我最大的鼓励</strong><br>↓↓↓↓↓↓</p>
2019,我的这一年,在校研究生做到年入20W,另送我的读者2000元现金红包
https://segmentfault.com/a/1190000021462663
2020-01-02T10:39:06+08:00
2020-01-02T10:39:06+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
6
<p>2019年过去了,到了该总结这一年的时候了,回顾这一年,自己做出了很多的努力,也得到了一些收获,当然,每一次回顾的时候,总是会发现有很多的地方是不足的,这也是不可避免的,每一次回顾的时候,只希望自己最希望做到的事情可以如愿,那就心满意足了。</p>
<h3>公众号</h3>
<p>第一件事情,我还是想谈一下公众号:<code>好好学java</code>,这也是我这一年多的时间的重心所在,这一年的收获还是挺多的,不管是公众号涨粉,还是收入,我还是挺满意的,这一年,公众号涨粉达到<code>4W</code>,在此基础上,还新开了一下公众号:<code>Coding这件小事</code>,粉丝也达到了<code>2W</code>,这一点还是非常的自豪的,在这一路上,其实很不容易,在这个圈子可以看到很多的变化,很多人在半路上就放弃了,但是,我很为自己感到自豪,一直坚持,做到现在的成绩。<br><img src="/img/remote/1460000021462666" alt="image" title="image"></p>
<p>2020年,关于公众号,我将继续努力,坚持,持续的为粉丝提供优质的内容,2020年一起继续努力,继续成长。</p>
<h3>技术成长</h3>
<p>关于技术成长方面,我也一直在努力,丰富自己的知识面,不管是从知识的广度还是知识的深度,都有在提升,这一年的时间,陆陆续续也一直在学习新的技术,提升自己的能力,但是,因为把挺多的时间都花在运营公众号上了,进度还是不够快,这一点在2020还是需要继续努力的方面。</p>
<p>另外,其实,在2019年的总结中,我就提到过,在2019年的时候,我提出了自己的一个不足,就是对于技术文章的输出还是不够的,我自己也认识到了这一点,在2019这一年的时间里,为了能够弥补这个不足,我一直都在努力的输出技术博客,其实,在这个过程当中还是提高了自己的写作的能力的,也能够分享更多的技术文章,让更多的同行看到,为技术社区贡献自己的一份力量。</p>
<p>在前几天,在写了第五个年头的CSDN平台,我申请的<code>博客专家</code>头衔通过了,对于这个头衔的获得,我内心还是有一点窃喜的,因为,这不仅仅只是一个头衔而已,他代表着我这5年来分享的技术博客的认可,对于我个人的博客输出的质量的认可,这也给了我继续更加努力的输出技术博客的动力。<br><img src="/img/remote/1460000021462667" alt="image" title="image"></p>
<h3>交际圈子</h3>
<p>关于交际圈子我还是非常想谈谈的,在以前其实我是没有这个概念的,当然,我也知道也听过很多的人说过人脉的重要性,但是,确实只是停留在这个认知上,在这一年的时间里,由于运营公众号,认识了很多的技术的大佬,大部分都是在互联网一线大厂的大佬,他们也是在运营着自己的公众号,很高兴自己能够慢慢的提高自己的能力的同时,也能扩大自己的圈子,能够走进技术这个大的圈子里,扩大自己的眼界。</p>
<p>在暑假的时候,在北京就线下见了两位做公众号的朋友(老邓和小詹),交谈的时候就学到了很多,给了自己很多的启发与思考。</p>
<p>在12月的时候,参加了一次线下的公众号号主的聚会,通过这次聚会,认识了很多的大佬,他们都非常的优秀,在他们自己的领域都取得了挺大的突破和成绩,通过跟他们的沟通,也扩大了自己的眼界,我总是觉得,跟更加优秀的人沟通,总是能看到每个人不同的闪光点,总是能够学到更多的知识,以后有机会,希望能够多参加这样的聚会。</p>
<p>我是哪一个??</p>
<h3>生活</h3>
<p>2019已经到了研究生的第二年的关键时期了,只能说时间过的真的非常的快,回想刚刚进入研究生的时候,想着能够先休息一下,但是,现在,研究生第二年的时间也已经过去一半了,2020年也要开始找实习了,任务也更加的重的,希望自己能够好好的把握好这宝贵的时间。</p>
<p>这一年,第一次出去实习了2个月,这次实习是实验室安排去的,很感谢实验室能够给我这次去北京实习的机会,这是第二次去北京,第一次去北京是大三的时候,去北京比赛;确实,这一次在实习中感受到了跟在学校很不一样的氛围跟环境,不管从压力,强度,还是要求,都是没有办法比的,虽然有压力,但是,也让自己学到了很多关于工作上的为人处事的道理。另外,对于还没有进入到社会的我来说,是一次挺不错的锻炼。</p>
<p>这一年,我渐渐的增加了锻炼身体的时间,特别是这半年来的时间,每个星期都是去打一两次篮球,每天早上起来就是在keep上做一次锻炼,这种习惯渐渐的养成了,希望以后能够一直的坚持,继续保持锻炼身体,啥时候有八块腹肌的时候,就算是有成效了,哈哈。</p>
<p>这一年,关于旅行,这一年只去了一次长沙,不得不说长沙吃的是真的好吃,而且也挺多,就那个文和友小龙虾,真不错,茶颜悦色也是非常推荐的,女朋友很喜欢喝,用了个小技巧,免费喝了几次,哈哈,非常爽,我感觉我跟我女朋友去长沙不是去旅游的,而是去吃东西的,就是两个吃货。</p>
<p>这一年,是我们在一起的第五年,五年如一日,从赣师大,到武汉,4年过去了,从喻家山,到南望山,一年过去了,现在变成了,从喻家山,到未来城,半年又过去了,不管地点怎么变化,时间怎么变化,身边的朋友怎么变少,至少,不变的,还有你,还有我们。<br><img src="/img/remote/1460000021462669" alt="image" title="image"></p>
<h3>2020</h3>
<p>关于2020也有一些自己的计划,我这个人总是会给自己一些计划,虽然有时候完成的不是很满意,但是,我总是这么做,做一个有计划的人。</p>
<p>第一,找到一份满意的工作,到了研究生第二年了,通过自己的努力,能够达到这个目标。</p>
<p>第二,更多的技术文章输出,2020,希望能够输出更多的技术博客。</p>
<p>第三,公众号,对于这一点还是我的重心所在,2020,能够在2019的基础上更进一步,达到目标,输出高质量的内容,打造自己的技术圈子。</p>
<p>第四,开创更多的方向,主要是想在小视频方向能够有所突破,2020,这需要加油。</p>
<p>第五,关于生活,跟女朋友一起去更多的地方旅行,体验不一样的生活。</p>
<p>第六,不止2020,未来几年,给我们一个答案。</p>
<p>最后,希望2020年,公众号的朋友们都能达到自己的目标,在工作上,学习上都能有所收获,2020,我们继续一起努力。</p>
<p>最后的最后,新年的第一天,当然是给大家发红包啦,这一年来感谢大家的支持,这些成绩跟大家的支持是分不开的。每个金额大小不定,数量巨多,这次是支付宝口令红包,100%中奖。</p>
<h3>参与方式</h3>
<p>大家可以在我的小号[<code>程序员的技术圈子</code>]回复:<code>2020</code>,即可获取参与方式,数量巨多,肯定都有,<code>扫一扫</code>下面二维码即可获取参与方式。</p>
<p><img src="/img/remote/1460000021462668" alt="image" title="image"></p>
<blockquote>这个公众号暂时还没有改名,也还没有文章,不过,后面主要会分享一些关于个人的思考,行业动态,分享自己的一些经验,而且,不一定会是以文章的形式出现,可能是以视频的形式出现,尽请期待。</blockquote>
<blockquote>本文参与了 SegmentFault思否征文「2019 总结」,欢迎正在阅读的你也加入。</blockquote>
深入理解Java虚拟机-如何利用VisualVM对高并发项目进行性能分析
https://segmentfault.com/a/1190000021442637
2019-12-30T15:49:31+08:00
2019-12-30T15:49:31+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
30
<p>前面在学习JVM的知识的时候,一般都需要利用相关参数进行分析,而分析一般都需要用到一些分析的工具,因为一般使用IDEA,而VisualVM对于IDEA也不错,所以就选择VisualVM来分析JVM性能,这篇文章就介绍一下<strong>如何利用VisualVM进行性能分析</strong>,以及在分析之前需要知道一些<strong>GC优化的原则</strong>,<strong>GC优化的目的</strong>,以及<strong>遇到问题时怎么去解决问题的方法</strong>。</p>
<h3>1 为什么需要</h3>
<p>开发大型 Java 应用程序的过程中难免遇到内存泄露、性能瓶颈等问题,比如文件、网络、数据库的连接未释放,未优化的算法等。随着应用程序的持续运行,可能会造成整个系统运行效率下降,严重的则会造成系统崩溃。为了找出程序中隐藏的这些问题,在项目开发后期往往会使用性能分析工具来对应用程序的性能进行分析和优化。</p>
<p>VisualVM 是一款免费的性能分析工具。它通过 jvmstat、JMX、SA(Serviceability Agent)以及 Attach API 等多种方式从程序运行时获得实时数据,从而进行动态的性能分析。同时,它能自动选择更快更轻量级的技术尽量减少性能分析对应用程序造成的影响,提高性能分析的精度。</p>
<h3>2 如何安装</h3>
<p>这里有两种方式:</p>
<ul><li>没有按照IDEA插件</li></ul>
<p>如果没有按照IDEA插件的话,我们需要找到JDK的按照目录bin下找到如下执行程序。</p>
<p><img src="/img/remote/1460000021442640" alt="" title=""></p>
<p>然后双击执行,就会出现界面,如下;</p>
<p><img src="/img/remote/1460000021442641" alt="" title=""></p>
<p>但是,我们一般使用IDEA,所以会使用插件,就是下面这种方式。</p>
<ul><li>按照IDEA插件</li></ul>
<p>先在插件中找到VisualVM安装;</p>
<p><img src="/img/remote/1460000021442643" alt="" title=""></p>
<p>安装了之后,在运行的地方就会多出现两个VisualVM的运行按钮;<br><img src="/img/remote/1460000021442642" alt="" title=""></p>
<p>这样运行程序之后,就可以自动打开VisualVM程序了。</p>
<h3>3 基本介绍</h3>
<p>这一部分先对这个工具做一个简要的介绍,看看基本有哪些我们会用到的功能。</p>
<p>在没有添加其他插件的时候,是只有下面几个功能的。</p>
<p><img src="/img/remote/1460000021442644" alt="" title=""></p>
<h4>3.1 概述</h4>
<p><img src="/img/remote/1460000021442645" alt="" title=""></p>
<p>如上图所示,概述基本上都是我们的<strong>系统属性、运行程序时设置的JVM参数</strong>等信息的展示,所以,这一部分可以让我们查看这些信息。</p>
<h4>3.2 监视</h4>
<p><img src="/img/remote/1460000021442646" alt="" title=""></p>
<p>监视这个界面的功能还是很有作用的,可以看到<strong>cup运行情况、堆的使用情况、类的情况以及线程的动态情况</strong>。</p>
<p>因此,我们可以利用这个界面查看cpu情况好不好,更重要的是,我们可以查看堆的使用情况,这对于我们分析JVM还是非常重要的。</p>
<h4>3.3 线程</h4>
<p><img src="/img/remote/1460000021442647" alt="" title=""></p>
<p>如上图所以,可以看到所有的线程的情况,是<strong>运行、休眠、等待、驻留、监视</strong>等情况。</p>
<p><strong>注意,</strong> 以上这些都不是关键,关键是VisualVM中还有一个很重要的功能,可以添加插件获取更多的功能。</p>
<h4>3.4 插件添加</h4>
<p>正是因为有了插件的扩展功能,所以这个工具才如此强大,VisualVM可以做到以下:</p>
<ul>
<li>显示虚拟机进程以及进程的配置、环境信息、jps、jinfo。</li>
<li>监视应用程序的cpu、GC、堆、方法区以及线程的信息(jstat、jstack)。</li>
<li>dump以及分析堆转存储快照(jmap、jhat)。</li>
<li>还有很多其他的功能。</li>
</ul>
<p>在工具->找到可用插件,安装即可。</p>
<p><img src="/img/remote/1460000021442649" alt="" title=""></p>
<p>下一部分我们就利用已经安装的插件<code>Visual GC</code>进行分析。</p>
<h3>4 利用<code>Visual GC</code>分析虚拟机内存区域</h3>
<p>这部分会用到一些Java虚拟机的一些基础知识,所以,查看这部分之前,请先查看这篇文章:。</p>
<p><img src="/img/remote/1460000021442648" alt="" title=""></p>
<p>在这个界面分为以下几个部分。</p>
<ul>
<li>space(Metaspace(元数据)、Old老年代、新生代(Eden、S0、S1))</li>
<li>Graphs(Compile Time(编译时间)、Class Loader Time(类加载时间)、GC Time(垃圾收集时间)、Eden Space、Survivor 0、Survivor 1、Old Gen、Metaspace)</li>
<li>Histogram(Parameters参数设置)</li>
</ul>
<p><strong>那么知道这些参数之后,怎么去分析虚拟机到底运行是好是坏呢</strong>,这个时候,我们需要了解一些Java虚拟机基础的优化知识。</p>
<p>首先,需要了解一些<strong>GC优化的原则</strong>。</p>
<ul>
<li>多数的Java应用不需要在服务器上进行GC优化;</li>
<li>多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题;</li>
<li>在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合);</li>
<li>减少创建对象的数量;</li>
<li>减少使用全局变量和大对象;</li>
<li>GC优化是到最后不得已才采用的手段;</li>
<li>在实际使用中,分析GC情况优化代码比优化GC参数要多得多;</li>
</ul>
<p>另外,我们需要知道我们<strong>GC优化的目的</strong>。</p>
<ul>
<li>将转移到老年代的对象数量降低到最小;</li>
<li>减少full GC的执行时间;</li>
</ul>
<p>一般,我们需要执行的有以下几点;</p>
<ul>
<li>减少使用全局变量和大对象;</li>
<li>调整新生代的大小到最合适;</li>
<li>设置老年代的大小为最合适;</li>
<li>选择合适的GC收集器;</li>
</ul>
<p>至于怎么算合适,后面我会通过一个实例讲解。</p>
<p>其实,如果想要知道更多<strong>JVM内存分配和回收策略的原理</strong>,可以查看这篇文章:<a href="https://link.segmentfault.com/?enc=1eTzHcZvMWFmtLswEASnRQ%3D%3D.mVvcaIR8VNv1GFVqJiCpWe6D8VwfPoShhUFMtxUtD7Guyte6IntSLirzI%2Bx5eXyGUqxdhmv4mLsclmQcpFu0lo256BMoYMxUjO7Mp0HcPrSIOkiE3ReR%2FKk9oLTtSl7ClB2K7CwC3g7%2B0QtLn6POL7TnRlRva%2FNKQ0gQ0P%2BoyKN9tLFw3Z42%2BO8kOesMp0SGZStbnRC0KWMk6x14pghleg%3D%3D" rel="nofollow">JVM内存分配和回收策略的原理</a>。</p>
<p>一般我们执行了我们的程序之后,接下来就是需要<strong>查看GC的状态</strong>了,接着<strong>分析结果</strong>,判断<strong>是否需要进行优化</strong>。</p>
<p>一般如果达到以下的<strong>指标</strong>,就不需要进行GC了。</p>
<ul>
<li>
<code>Minor GC</code>执行时间不到<code>50ms</code>,<code>Minor GC</code>执行不频繁,约<code>10</code>秒一次;</li>
<li>
<code>Full GC</code>执行时间不到<code>1s</code>,<code>Full GC</code>执行频率不算频繁,不低于<code>10</code>分钟1次;</li>
</ul>
<h4>实例 1</h4>
<p>我们先看一个GC状态需要优化的例子,在这个实例中,我们给堆分配的最大最小的值都是<code>64M</code>(很小的堆大小)。</p>
<h5>GC状态差情况分析</h5>
<pre><code>/**
* VM Args:-Xms64m -Xmx64m -XX:+HeapDumpOnOutOfMemoryError
* @author 欧阳思海
*/
public class HeapTest {
static class StaticObject {
}
public static void main(String[] args) {
List<StaticObject> list = new ArrayList<StaticObject>();
int i = 1;
//不断的向堆中添加对象
while (true) {
list.add(new StaticObject());
i++;
System.out.println(i);
System.out.println(list.size());
}
}
}</code></pre>
<p><img src="/img/remote/1460000021442651" alt="" title=""></p>
<p>由于分配的堆内存太小,所以导致,堆溢出。</p>
<p>接着我们查看一下<code>Visual GC</code>的监视情况。</p>
<ul><li>监视界面情况</li></ul>
<p><img src="/img/remote/1460000021442650" alt="" title=""></p>
<p>我们可以从堆的使用情况看出,基本已经使用完。</p>
<ul><li>Visual GC监视情况</li></ul>
<p><img src="/img/remote/1460000021442652" alt="" title=""></p>
<p>从上图可知,在短短的运行时间中,<strong>Eden进行了49次GC,虽然时间短,但是能说明一个问题,新生代堆内存分配的空间太小,导致频繁GC</strong>。</p>
<p>同时,<strong>Old老年代也进行了33次GC,虽然运行时间也在不需要优化的范围内,而且从Survivor可以看出,基本没有GC,说明这些都是大对象,直接进入到了Old老年代,导致GC频繁</strong>。</p>
<p>所以,我们需要进行的优化就是<strong>加大新生代和老年代堆内存的大小,同时减少大对象的产生</strong>。</p>
<h5>参数优化分析</h5>
<p>我们将VM参数改为:<code>-Xms512m -Xmx512m -Xmn128m -XX:+HeapDumpOnOutOfMemoryError</code>,运行大概<strong>5分钟</strong>再次查看结果。</p>
<p><img src="/img/remote/1460000021442653" alt="" title=""></p>
<p>首先没有出现堆内存溢出。</p>
<ul><li>监视情况</li></ul>
<p><img src="/img/remote/1460000021442654" alt="" title=""></p>
<p>加大了堆内存,所以堆内存没有出现问题。</p>
<ul><li>Visual GC监视情况</li></ul>
<p><img src="/img/remote/1460000021442655" alt="" title=""></p>
<p>加大了堆内存之后,<strong>Eden新生代进行了66次GC,使用时间3.381s基本满足要求(执行时间不到50ms,Minor GC执行不频繁,约10秒一次),同时老年代old进行了2次GC,使用时间4.127s,这里还是有待优化的,不太满足优化要求。</strong></p>
<ul><li>dump文件分析</li></ul>
<p><img src="/img/remote/1460000021442657" alt="" title=""></p>
<p>点击<strong>堆 dump</strong>这个按钮就会生成 dump文件,我们可以分析类及对象的一些情况。</p>
<p><img src="/img/remote/1460000021442656" alt="" title=""></p>
<p>分析之后发现,<strong>StaticObject对象大多,没有进行GC</strong>,问题主要在这里,所以,下一步需要解决这个问题。</p>
<p>通过以上分析可以说明一个问题,加大了堆内存之后,新生代和老年代的GC情况大大的改善了,但是还有<strong>大对象的问题</strong>,所以还有待优化。</p>
<h5>修改大对象,进行GC</h5>
<p>修改程序,如下:</p>
<pre><code class="java">/**
* VM Args:-Xms512m -Xmx512m -Xmn128m -XX:+HeapDumpOnOutOfMemoryError
*
* @author 欧阳思海
*/
public class HeapTest {
/* static class StaticObject {
}*/
public static void main(String[] args) {
int i = 1;
while (true) {
i++;
System.out.println(i);
}
}
}
</code></pre>
<ul><li>监视情况</li></ul>
<p><img src="/img/remote/1460000021442658" alt="" title=""></p>
<p>堆一直运行良好。</p>
<ul><li>Visual GC监视情况</li></ul>
<p><img src="/img/remote/1460000021442659" alt="" title=""></p>
<p>这次相对于上次相比,老年代的情况已经改善了,没有GC,说明大对象不存在了。</p>
<p>通过上面的分析跟优化,就满足GC的需求了,不需要再优化了。</p>
<p><strong>Java虚拟机深入理解</strong>系列全部文章更新中...</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=f%2FZK4pqXvV9kGcz8g7lBfQ%3D%3D.IpBR130MoMhy%2BkM7uqRcKeFSWgAg9S6jUvdovk7DtH3W20v%2B9NVhJ7CvxDXXZAzvZkqS5v%2BkSb8T0tYLzIG4YKiBFfbnRO16g0GGcvXp8KxcGK9bmV8dhL%2BHaEy23sAI" rel="nofollow">深入理解Java虚拟机-Java内存区域透彻分析 </a></li>
<li><a href="https://link.segmentfault.com/?enc=Of0Awp61Mq27VhpasjNg1A%3D%3D.K%2BJfMVZmHcM9luPLz%2BwUqIazFmyXMLT33gNefqp5nKprl5rx5ZsC1sNJkuYVcXK%2FbKCEWD%2FPkwGZ3OGTVH6u1EsP19wBsJZoKQR4267dI2Kgt5mErgrlh0JYw%2F7rUfZh" rel="nofollow">深入理解Java虚拟机-常用vm参数分析 </a></li>
<li><a href="https://link.segmentfault.com/?enc=%2B5ow%2F0TjVht4yF3W9Y%2FqpQ%3D%3D.EquXwVwWGwcE0Foz%2BYjuAQNpzp0ppmJdqfWwGJli%2F1sgIZGsUcpoa6P4nw%2BN2wnTD%2BSx5gZH5Csj27BDP%2FP0vAYucv52mhJwJRUbIikUpCPwnEQEthymQypbfRih5PPN4el%2F3Ijs6c1gNTu8QGBVB2OHGdCHzxIRaV5QyU26%2Fy1d%2F%2BucXr9pyky0ql5emAZQ%2FWDNsLbLAHvMMZ4ec1Pmgg%3D%3D" rel="nofollow">深入理解Java虚拟机-JVM内存分配与回收策略原理,从此告别JVM内存分配文盲</a></li>
<li><a href="https://link.segmentfault.com/?enc=cxaoRP%2FJ9uLIC2qodP21Bg%3D%3D.OFhhD4V2p8P13H0T3%2BdhnajKU2MrQbjPluBsHXHXX04rp58irygB8d8URsIW8tmUvVGst6mLEdR0UtVfuw2Rbr0N23MvRtW%2F%2BPBeISlXBvUtIsHxnZz6dpMN6m6lupxBQ9tb0pkeBpSKZLweEaNiVBXyyZ8zwAvKSgSEz4f7DK9mat9i7mGYDIWH4Gy4I2ZUIx9RgrIWAls02ey8VPihwqIMw9lLHtOWuCPUSQEmcMA%3D" rel="nofollow">深入理解Java虚拟机-如何利用JDK自带的命令行工具监控上百万的高并发的虚拟机性能 </a></li>
<li><a href="https://link.segmentfault.com/?enc=WkMrqqP4BSkQ5oWCo%2BqknQ%3D%3D.Uuuur4g%2FcSZuLTJaLKUn2aoNd%2FTDHB%2B4mchGobe9F9kB3nxUTiiV7ZYpdMtbtdRsGQRV1SOUObuub2RNHqakxna7TWWjV5H6ffrFQAWYHRmDQYT1V%2B4dRpYUlJoVCMtn04%2BeU7jp7WjOgpYhr7xxpaGFTaK9Ff%2FOKkR0CsmktV57CIlubFhEtcIEB2SNrq8O" rel="nofollow">深入理解Java虚拟机-如何利用VisualVM对高并发项目进行性能分析</a></li>
<li><a href="https://link.segmentfault.com/?enc=GMa9KkP2x0Uh2IHigQFb9A%3D%3D.3ZywggjES5F3%2FZyxTxG6BJLaestc%2BvFM2cLnTGP4q8tQ2t1WiXFOuInKX9VnzK6UI%2F1d5w4rqPh1UyHgG4O3cBAwnucg9OBjxdGBy5xuiSR%2BHVgo%2F6ikyr%2FSoixW5oe8" rel="nofollow">深入理解Java虚拟机-你了解GC算法原理吗 </a></li>
</ul>
<h3>5 总结</h3>
<p>通过上面的分析及使用,VisualVM基本的使用以及如何利用VisualVM进行Java虚拟机优化相信你已经掌握了,如果还想了解更过关于Java虚拟机的知识及优化文章,请看本系列的其他文章。</p>
<blockquote>1、<strong>原创不易</strong>,老铁,文章需要你的 <strong>点赞</strong> 让更多的人看到,希望能够帮助到大家!<p>2、文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的<strong>微信公众号</strong>:<strong><code>好好学java</code></strong>,公众号已有 <strong>6W</strong> 粉丝。</p>
</blockquote>
深入理解Java虚拟机-利用常用vm参数分析上线项目问题
https://segmentfault.com/a/1190000021260002
2019-12-11T17:06:29+08:00
2019-12-11T17:06:29+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
5
<p>话不多说,今天就分析一下一些常用的Java虚拟机的参数设置,以及如何更好的使用!</p>
<h3>1 JVM参数简介</h3>
<p>首先想说的是其实这些参数我们并不是陌生的,在平时的开发和使用中经常都会遇到,只是在平时缺少一个比较系统的总结,所以,对这些参数感觉是很陌生的,所以,通过这篇文章的总结,我相信你一定都会对这些参数熟稔于心,做做心中有数。</p>
<p>在Java虚拟机的参数中,其实可以把这些参数分为三类,当然,这是针对JDK1.6来说的,如果对于JDK1.8,那么就不是这么分类的了,但是,由于这两个版本很多常用的参数的差别是不大的,所以这篇文章就先介绍JDK1.6的VM参数。</p>
<p>主要可以分为以下三类:</p>
<ul>
<li>
<code>标准参数(-)</code>,所有的JVM实现都必须实现这些参数的功能,而且向后兼容。</li>
<li>
<code>非标准参数(-X)</code>,默认JVM实现这些参数的功能,但是并不保证所有JVM实现都满足,且不保证向后兼容。</li>
<li>
<code>非Stable参数(-XX)</code>,此类参数各个JVM实现会有所不同,将来可能会随时取消,需要慎重使用。</li>
</ul>
<p>虽然是这么分类的,实际上呢,非标准参数和非稳定的参数实际的使用中还是用的非常的多的,在后面的文章的介绍中你就会发现。</p>
<h3>2 标准参数</h3>
<p>这一类参数可以说是我们刚刚开始Java是就用的非常多的参数了,比如<code>java -version</code>、<code>java -jar</code>等等,我们在CMD中输入<code>java -help</code>就可以获得Java当前版本的所有标准参数了。</p>
<p><img src="/img/remote/1460000021260006" alt="" title=""></p>
<p>如上图就是JDK1.8的所有标准参数了,下面我们将介绍一些我们会用的比较多的参数。</p>
<ul><li>-client</li></ul>
<p>以client模式启动JVM,这种方式启动速度快,但运行时性能和内存管理效率不高,适合客户端程序或者开发调试。</p>
<ul><li>-server</li></ul>
<p>以server模式启动JVM,与client情况恰好相反。适合生产环境,适用于服务器。64位的JVM自动以server模式启动。</p>
<ul><li>-classpath或者-cp</li></ul>
<p>通知JVM类搜索路径。如果指定了<code>-classpath</code>,则JVM就忽略<code>CLASSPATH</code>中指定的路径。各路径之间以分号隔开。如果<code>-classpath</code>和<code>CLASSPATH</code>都没有指定,则JVM从当前路径寻找class。</p>
<p>JVM搜索路径的顺序:</p>
<p><strong>1.先搜索JVM自带的jar或zip包。</strong></p>
<p>Bootstrap,搜索路径可以用<code>System.getProperty("sun.boot.class.path")</code>获得;</p>
<p><strong>2.搜索<code>JRE_HOME/lib/ext</code>下的jar包。</strong></p>
<p>Extension,搜索路径可以用<code>System.getProperty("java.ext.dirs")</code>获得;</p>
<p><strong>3.搜索用户自定义目录,顺序为:当前目录(.),CLASSPATH,-cp。</strong></p>
<p>搜索路径用<code>System.getProperty("java.class.path")</code>获得。</p>
<pre><code class="java">System.out.println(System.getProperty("sun.boot.class.path"));
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println(System.getProperty("java.class.path"));</code></pre>
<p><img src="/img/remote/1460000021260005" alt="" title=""></p>
<p>如上就是我电脑的JVM的路径。</p>
<ul><li>-DpropertyName=value</li></ul>
<p>定义系统的全局属性值,如配置文件地址等,如果value有空格,则需要使用双引号。</p>
<p>另外用<code>System.getProperty("hello")</code>可以获得这些定义的属性值,在代码中也可以用<code>System.setProperty("hello","world")</code>的形式来定义属性。</p>
<p>如键值对设置为hello=world。<br><img src="/img/remote/1460000021260008" alt="" title=""></p>
<pre><code class="java">System.out.println(System.getProperty("hello"));</code></pre>
<p>运行结果就是:<br><img src="/img/remote/1460000021260007" alt="" title=""></p>
<ul><li>-verbose</li></ul>
<p>查询GC问题最常用的命令之一,参数如下:<br><strong>-verbose:class</strong><br>输出JVM载入类的相关信息,当JVM报告说找不到类或者类冲突时可此进行诊断。<br><strong>-verbose:gc</strong><br>输出每次GC的相关情况。<br><strong>-verbose:jni</strong><br>输出native方法调用的相关情况,一般用于诊断jni调用错误信息。</p>
<p>另外,控制台<strong>输出GC信息</strong>还可以使用如下命令:</p>
<p>在JVM的启动参数中加入<code>-XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime</code>,按照参数的顺序分别输出GC的简要信息,GC的详细信息、GC的时间信息及GC造成的应用暂停的时间。</p>
<h3>3 非标准参数</h3>
<p>非标注的参数主要是关于Java内存区域的设置参数,所以在看这些参数之前,应该先查看Java内存区域的基础知识,可以查看这篇文章:<a href="https://link.segmentfault.com/?enc=cM4u9h4%2Btpmfslrhndh%2BdQ%3D%3D.4spSWtSBKrOQ0SjHvQi9C2hZJqSM6tCW6nEbG54%2BxkOAf5fXBrM9G0AdSTHfxETuyInDp6mmd7ww%2F5Czt8IXQGfoNiQy5kYe27%2F44Eq6mWk23wAYS3hzReHiNpnLN03v" rel="nofollow">深入理解Java虚拟机-Java内存区域透彻分析</a>。</p>
<p>非标准参数实在标准参数的基础上的一些扩充参数,可以输入<code>java -X</code>,获得当前JVM支持的非标准参数。</p>
<p><img src="/img/remote/1460000021260010" alt="" title=""></p>
<p>从图片中可以看出来,这些非标准的参数其实不多的,下面我们再 讲解一些比较常用的参数。</p>
<ul><li>-Xmn</li></ul>
<p>新生代内存大小的最大值,包括E区和两个S区的总和。设置方法:<code>-Xmn512m、-Xmn2g</code>。</p>
<ul><li>-Xms</li></ul>
<p>初始堆的大小,也是堆大小的最小值,默认值是总共的物理内存/64(且小于1G)。默认情况下,当堆中可用内存小于40%,堆内存会开始增加,一直增加到-Xmx的大小。</p>
<ul><li>-Xmx</li></ul>
<p>堆的最大值,默认值是总共的物理内存/64(且小于1G),默认情况下,当堆中可用内存大于70%,堆内存会开始减少,一直减小到-Xms的大小。</p>
<p>因此,为了避免这种浮动,所以在设置<code>-Xms</code>和<code>-Xmx</code>参数时,一般会设置成一样的,能够提高性能。</p>
<p>另外,官方默认的配置为<strong>年老代大小:年轻代大小=2:1</strong>左右,使用<code>-XX:NewRatio</code>可以设置年老代和年轻代之比,例如,<code>-XX:NewRatio=4</code>,表示<strong>年老代:年轻代=4:1</strong></p>
<p><strong>参数实例</strong></p>
<p>设置<code>-Xms</code>、<code>-Xmn</code>和<code>-Xmx</code>参数分别为<code>-Xms512m -Xmx512m -Xmn128m</code>。同时设置新生代和老生代之比为1:4,E:S0:S1=8:1:1。</p>
<pre><code>**
* @ClassName MethodTest
* @Description vm参数设置:-Xms512m -Xmx512m -Xmn128m -XX:NewRatio=4 -XX:SurvivorRatio=8
* @Author 欧阳思海
* @Date 2019/11/25 20:06
* @Version 1.0
**/
public class MethodTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
long i = 0;
while (i < 1000000000) {
System.out.println(i);
list.add(String.valueOf(i++).intern());
}
}
}</code></pre>
<p>运行之后,用VisualVM查看相关信息是否正确。</p>
<p>当我们<strong>没有设置</strong><code>-XX:NewRatio=4 -XX:SurvivorRatio=8</code>时,使用官方默认的情况如下: </p>
<p><img src="/img/remote/1460000021260009" alt="" title=""></p>
<p>上图可以看出,<strong>新生代(Eden Space + Survivor 0 + Survivor 1):老年代(Old Gen)≈ 1:2</strong>。</p>
<p>当我们<strong>设置了</strong><code>-XX:NewRatio=4 -XX:SurvivorRatio=8</code>时,情况如下:</p>
<p><img src="/img/remote/1460000021260011" alt="" title=""></p>
<p>变成了<strong>新生代(Eden Space + Survivor 0 + Survivor 1):老年代(Old Gen)≈ 1:4</strong>,Eden Space:Survivor 0: Survivor 1 = 8:1:1。</p>
<p><img src="/img/remote/1460000021260012" alt="" title=""></p>
<p>从上图可知,堆的信息是正确的。</p>
<ul><li>-Xss</li></ul>
<p>设置每个线程的栈内存,默认1M,一般来说是不需要改的。</p>
<ul><li>-Xprof</li></ul>
<p>跟踪正运行的程序,并将跟踪数据在标准输出输出;适合于开发环境调试。</p>
<ul><li>-Xnoclassgc</li></ul>
<p>禁用类垃圾收集,关闭针对class的gc功能;因为其阻止内存回收,所以可能会导致OutOfMemoryError错误,慎用。</p>
<ul><li>-Xincgc</li></ul>
<p>开启增量gc(默认为关闭);这有助于减少长时间GC时应用程序出现的停顿;但由于可能和应用程序并发执行,所以会降低CPU对应用的处理能力。</p>
<ul><li>-Xloggc:file</li></ul>
<p>与<code>-verbose:gc</code>功能类似,只是将每次GC事件的相关情况记录到一个文件中,文件的位置最好在本地,以避免网络的潜在问题。<br> 若与verbose命令同时出现在命令行中,则以-Xloggc为准。</p>
<ul><li>-Xint</li></ul>
<p>在解释模式(interpreted mode)下,<code>-Xint</code>标记会强制JVM执行所有的字节码,这会降低运行速度,通常低10倍或更多。</p>
<ul><li>-Xcomp</li></ul>
<p>-Xcomp参数与它(-Xint)正好相反,JVM在第一次使用时会把所有的字节码编译成本地代码,从而带来最大程度的优化。</p>
<p>然而,很多应用在使用<code>-Xcomp</code>也会有一些性能损失,当然这比使用-Xint损失的少,原因是<code>-xcomp</code>没有让JVM启用<code>JIT</code>编译器的全部功能。JIT编译器可以对是否需要编译做判断,如果所有代码都进行编译的话,对于一些只执行一次的代码就没有意义了。</p>
<ul><li>-Xmixed</li></ul>
<p><code>-Xmixed</code>是混合模式,这是JVM默认的模式,也是推荐使用的模式。将解释模式与编译模式进行混合使用,由JVM自己决定。</p>
<h3>4 非Stable参数</h3>
<p>这类参数你一看官网以为不能使用呢,官网给你的建议就是这些参数不稳定,慎用,其实这主要的原因还是因为每个公司的实现都是不一样的,所以就是导致不稳定。但是呢,在实际的使用中却是非常的多的,而且这部分的参数很重要。</p>
<p>这些参数大致可以分为三类:</p>
<ul>
<li>性能参数(Performance Options):用于JVM的性能调优和内存分配控制,如初始化内存大小的设置;</li>
<li>行为参数(Behavioral Options):用于改变JVM的基础行为,如GC的方式和算法的选择;</li>
<li>调试参数(Debugging Options):用于监控、打印、输出等jvm参数,用于显示jvm更加详细的信息;</li>
</ul>
<p>下面还是先罗列一些比较常用的参数,其实,这些文章很多了,这里主要还是做一个总结,以后自己看文章的时候比较方便,如果有同行看到了文章,你可以参考参考,还是很有帮助的。</p>
<p>另外,选取其中的一些参数做一些例子来解释,这样也能够更加的形象。</p>
<p><strong>注意:以下参数都是JDK1.7及以下可以使用。</strong></p>
<ul><li>性能参数</li></ul>
<table>
<thead><tr>
<th>参数及其默认值</th>
<th>描述</th>
</tr></thead>
<tbody>
<tr>
<td>-XX:LargePageSizeInBytes=4m</td>
<td>设置用于Java堆的大页面尺寸</td>
</tr>
<tr>
<td>-XX:MaxHeapFreeRatio=70</td>
<td>GC后java堆中空闲量占的最大比例</td>
</tr>
<tr>
<td>-XX:MinHeapFreeRatio=40</td>
<td>GC后java堆中空闲量占的最小比例</td>
</tr>
<tr>
<td><strong>-XX:MaxNewSize=size</strong></td>
<td>新生成对象能占用内存的最大值</td>
</tr>
<tr>
<td><strong>-XX:MaxPermSize=64m</strong></td>
<td>老生代对象能占用内存的最大值</td>
</tr>
<tr>
<td><strong>-XX:NewRatio=2</strong></td>
<td>新生代内存容量与老生代内存容量的比例</td>
</tr>
<tr>
<td><strong>-XX:NewSize=2.125m</strong></td>
<td>新生代对象生成时占用内存的默认值</td>
</tr>
<tr>
<td>-XX:ReservedCodeCacheSize=32m</td>
<td>保留代码占用的内存容量</td>
</tr>
<tr>
<td>-XX:ThreadStackSize=512</td>
<td>设置线程栈大小,若为0则使用系统默认值</td>
</tr>
<tr>
<td>-XX:+UseLargePages</td>
<td>使用大页面内存</td>
</tr>
</tbody>
</table>
<ul><li>行为参数</li></ul>
<table>
<thead><tr>
<th>参数及其默认值</th>
<th>描述</th>
</tr></thead>
<tbody>
<tr>
<td>-XX:+ScavengeBeforeFullGC</td>
<td>新生代GC优先于Full GC执行</td>
</tr>
<tr>
<td>-XX:+UseGCOverheadLimit</td>
<td>在抛出OOM之前限制jvm耗费在GC上的时间比例</td>
</tr>
<tr>
<td><strong>-XX:-UseParNewGC</strong></td>
<td>打开此开关,使用<code>ParNew+Serial Old</code>收集器</td>
</tr>
<tr>
<td><strong>-XX:-UseConcMarkSweepGC</strong></td>
<td>使用<code>ParNew+CMS+Serial Old</code>收集器对老生代采用并发标记交换算法进行GC</td>
</tr>
<tr>
<td><strong>-XX:-UseParallelGC</strong></td>
<td>启用并行GC,使用<code>ParallelScavenge+Serial Old</code>收集器</td>
</tr>
<tr>
<td><strong>-XX:-UseParallelOldGC</strong></td>
<td>对Full GC启用并行,当<code>-XX:-UseParallelGC</code>启用时该项自动启用,<code>ParallelScavenge+Parallel Old</code>收集器</td>
</tr>
<tr>
<td><strong>-XX:-UseSerialGC</strong></td>
<td>启用串行GC</td>
</tr>
<tr>
<td><strong>-XX:+UseG1GC</strong></td>
<td>使用垃圾优先(G1)收集器</td>
</tr>
<tr>
<td>-XX:SurvivorRatio=n</td>
<td>Eden区域与Survivor区域大小之比。预设值为8</td>
</tr>
<tr>
<td>-XX:PretenureSizeThreshold=n</td>
<td>直接晋升到老年代的<strong>对象大小</strong>,设置这个参数之后,大于这个参数的对象直接进入到老年代分配</td>
</tr>
<tr>
<td>-XX:MaxTenuringThreshold=n</td>
<td>晋升到老年代的<strong>对象年龄</strong>,每个对象在坚持过一次Minor GC之后,年龄加1,当超过这个值之后就进入老年代。预设值为15</td>
</tr>
<tr>
<td>-XX:+UseAdaptiveSizePolicy</td>
<td>动态调整Java堆中各个区域的大小以及进入老年代的年龄</td>
</tr>
<tr>
<td>-XX:ParallelGCThreads=n</td>
<td>设置并行收集器收集时使用的CPU数。并行收集线程数</td>
</tr>
<tr>
<td>-XX:MaxGCPauseMillis=n</td>
<td>设置并行收集最大暂停时间</td>
</tr>
<tr>
<td>-XX:GCTimeRatio=n</td>
<td>设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+N)</td>
</tr>
<tr>
<td>-XX:+UseThreadPriorities</td>
<td>启用本地线程优先级</td>
</tr>
<tr>
<td>-XX:-DisableExplicitGC</td>
<td>禁止调用<code>System.gc()</code>;但jvm的gc仍然有效</td>
</tr>
<tr>
<td>-XX:+MaxFDLimit</td>
<td>最大化文件描述符的数量限制</td>
</tr>
</tbody>
</table>
<p>前面6个参数都是关于<strong>垃圾收集器</strong>的行为参数,也是经常会用到的参数。</p>
<ul><li>调试参数</li></ul>
<table>
<thead><tr>
<th>参数及其默认值</th>
<th>描述</th>
</tr></thead>
<tbody>
<tr>
<td>-XX:-CITime</td>
<td>打印消耗在JIT编译的时间</td>
</tr>
<tr>
<td>-XX:ErrorFile=./hs_err_pid<pid>.log</td>
<td>保存错误日志或者数据到文件中</td>
</tr>
<tr>
<td>-XX:HeapDumpPath=./java_pid<pid>.hprof</td>
<td>指定导出堆信息时的路径或文件名</td>
</tr>
<tr>
<td>-XX:-HeapDumpOnOutOfMemoryError</td>
<td>当首次遭遇OOM时导出此时堆中相关信息</td>
</tr>
<tr>
<td>-XX:OnError="<cmd args>;<cmd args>"</td>
<td>出现致命ERROR之后运行自定义命令</td>
</tr>
<tr>
<td>-XX:OnOutOfMemoryError="<cmd args>;<cmd args>"</td>
<td>当首次遭遇OOM时执行自定义命令</td>
</tr>
<tr>
<td>-XX:-PrintClassHistogram</td>
<td>遇到Ctrl-Break后打印类实例的柱状信息,与<code>jmap -histo</code>功能相同</td>
</tr>
<tr>
<td>-XX:-PrintConcurrentLocks</td>
<td>遇到Ctrl-Break后打印并发锁的相关信息,与<code>jstack -l</code>功能相同</td>
</tr>
<tr>
<td>-XX:-PrintCommandLineFlags</td>
<td>打印在命令行中出现过的标记</td>
</tr>
<tr>
<td>-XX:-PrintCompilation</td>
<td>当一个方法被编译时打印相关信息</td>
</tr>
<tr>
<td>-XX:-PrintGC</td>
<td>每次GC时打印相关信息</td>
</tr>
<tr>
<td>-XX:-PrintGCDetails</td>
<td>每次GC时打印详细信息</td>
</tr>
<tr>
<td>-XX:-PrintGCTimeStamps</td>
<td>打印每次GC的时间戳</td>
</tr>
<tr>
<td>-XX:-TraceClassLoading</td>
<td>跟踪类的加载信息</td>
</tr>
<tr>
<td>-XX:-TraceClassLoadingPreorder</td>
<td>跟踪被引用到的所有类的加载信息</td>
</tr>
<tr>
<td>-XX:-TraceClassResolution</td>
<td>跟踪常量池</td>
</tr>
<tr>
<td>-XX:-TraceClassUnloading</td>
<td>跟踪类的卸载信息</td>
</tr>
<tr>
<td>-XX:-TraceLoaderConstraints</td>
<td>跟踪类加载器约束的相关信息</td>
</tr>
</tbody>
</table>
<h3>5 JDK8的VM参数</h3>
<p>由于考虑到现在JDK8用的非常的广泛,所以,接下来总结一些JDK8会使用到的参数。</p>
<p>查看这篇<a href="https://link.segmentfault.com/?enc=tmk%2FQzEXYTNl6GZmMhAj8g%3D%3D.V%2BgvnPSq4hC9xfp44nrxREipgmiy7cQt2GkwJnpcQo7JIxDZnsB2gkWcqg31KBysf0aexYO1ssD%2FT%2B1OQ4Cnqi3ugQEPzjFWiF1oh5BLKWo%3D" rel="nofollow">JDK8的参数文章</a>你会发现,标准参数和非标准参数JDK8的差别并不是很大,如果有其他的参数需要使用,可以查看上面这篇文章。</p>
<p><strong>Java虚拟机深入理解</strong>系列全部文章更新中...</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=zZrN82uM6jTG4xhCSHSZ0Q%3D%3D.1df4l9BIkonn3Oo6Qp3sQONT0xdbPD1L94sAXjMfXAlDKfw5zwjRerVcU%2BF50eKBEku1AfyIO9qQjR38Ji1nsLPgVC35hswHn97rwt7ZIs4TSicyHGfFrH6ndotRwvY5" rel="nofollow">深入理解Java虚拟机-Java内存区域透彻分析 </a></li>
<li><a href="https://link.segmentfault.com/?enc=CXUIIWUdwLmvbbS%2F7wqTlw%3D%3D.LdgDHWimz6kGB6IIcc4kASWwvHQnS%2F2Tt0ZPwvKq9AS3T3kGDT1UddtAP5OgV9cHiDNZGZHmB%2B7bWIrz710v0xP1ialJaXDosZuepcYTPEHLNW9XRFgP%2F7LhY3jvpn73" rel="nofollow">深入理解Java虚拟机-常用vm参数分析 </a></li>
<li><a href="https://link.segmentfault.com/?enc=Zn0UG9Lz7t6G546mtW2XhA%3D%3D.MN9Ebcfs2ayW5ZMPGs%2FomF6TfUvxmiM%2BcIxtKVpf9JT%2BP2XQJKr3IHYexlwn1Iq%2Bh4S2rgYcX9MJ6RHWTiMaRc%2B0ChOXHJN3b7tBZWURLLkKcJ9MsWzCkl2FeZ8vgmUlESqxeZrrLkUQrCgQmd6LkL9lv3T1gmHE05oqN%2FRGCz8VEvo6rPFXDPzbSWm5zaJcYlTegkHeOKrrsF1eZOXZxA%3D%3D" rel="nofollow">深入理解Java虚拟机-JVM内存分配与回收策略原理,从此告别JVM内存分配文盲</a></li>
<li><a href="https://link.segmentfault.com/?enc=EDEHEFvAMze%2F%2BhfBVIXAvg%3D%3D.HKZY0hZRhVlNHqKNTZPmkCG4sCTfrYBlEufdIBJZpnphrlC%2BN8r7IV5wW7idD%2Fd%2FpEIGRcgOTFf0eHU0uyfuQXz98OuplU7Ozc36dFbK4Xcnql3fc1cIcPr7H8sVAeMfC773kPXKL%2BYbhQ7gVyPVE1bE1L%2Ft3HbrE9hvPGo9x0DDRRaRPUwWECXigIBrOEEkNUH872DjBajltd248veHhaOc0hUMMlHeH%2FeZVSXsHhs%3D" rel="nofollow">深入理解Java虚拟机-如何利用JDK自带的命令行工具监控上百万的高并发的虚拟机性能 </a></li>
<li><a href="https://link.segmentfault.com/?enc=6wjd7zuWL7eXI5ml6mG6Cw%3D%3D.uyNQ1IPRQ6EjfEUgNQm%2BjG2rKW8HIoeSmPYCL6h%2BcojKm%2FK0BRNzWf8vrukIitiGLs0m6yJZzwrddvDE4HIn5yJwAUxEMn0h9eJ542CJStfctRiUh%2FODE7q%2B3Zn79DlLGlpEMNTjssLjn5Md3MwIB6vFsoo9qVrmzI0I5ZAfefenRjTEOWMnMZBfrMEV15Fd" rel="nofollow">深入理解Java虚拟机-如何利用VisualVM对高并发项目进行性能分析</a></li>
<li><a href="https://link.segmentfault.com/?enc=XSQSeK%2BDVF4h4sxH06lUJA%3D%3D.WuGtLvlUYekuU6QaQWr5OCQ23%2Ba3em8KiAJbn9Q4aLQChfJJy6huW4TfAfP5AeUiOKLSkxVz7XqZVHUC6%2FJR59VIPecXr7sg6gGlSh1jEeGrAOmLw9osmAu6w20k6HUa" rel="nofollow">深入理解Java虚拟机-你了解GC算法原理吗 </a></li>
</ul>
<blockquote>1、<strong>原创不易</strong>,老铁,文章需要你的 <strong>点赞</strong> 让更多的人看到,希望能够帮助到大家!<p>2、文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的<strong>微信公众号</strong>:<strong><code>好好学java</code></strong>,公众号已有 <strong>6W</strong> 粉丝,回复:<strong>1024</strong>,获取公众号的大礼包,公众号长期发布 <strong>Java 优质系列文章</strong>,关注我们一定会让你收获很多!</p>
<p><img src="/img/remote/1460000021254987" alt="" title=""></p>
</blockquote>
Java面试题大全,看完这些面试文章足够了
https://segmentfault.com/a/1190000021254273
2019-12-11T10:31:05+08:00
2019-12-11T10:31:05+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
114
<p>Java最新面试题汇总,希望这些文字对于面试的你们有帮助。</p>
<h3>基础面试题</h3>
<ul>
<li><a href="https://link.segmentfault.com/?enc=z3i7EsgIdePyWRiGJbilPg%3D%3D.iua6eKbU%2Bnf%2FrQ6Sa0d7LJOCvjsZm6OjD8klxPFCIVWQfcWkOVKpaicMZTrQTuRH" rel="nofollow">Java面试题-基础篇一</a></li>
<li><a href="https://link.segmentfault.com/?enc=yJI1UaHQgYuqvSwntQj35A%3D%3D.Y0DV%2BMJyC8EPBQzXgHXtUTWgWUoIoXa8LewPf0giDS6PyvUcKJe74JsffqTB38wR" rel="nofollow">Java面试题-基础篇二</a></li>
<li><a href="https://link.segmentfault.com/?enc=TnSUjroXLklYJvRoKLs0MA%3D%3D.yoemPsyuWdSeYpZ9sxbaXHVurDiqeSJQVXoAXx7xqT7bO%2F5ump9WE8T%2FtQzBLEmm" rel="nofollow">Java面试题-集合框架篇三</a></li>
<li><a href="https://link.segmentfault.com/?enc=HlnXrhsbHUGyKlK6eaPbNw%3D%3D.xY%2FdAKkEGvOFFyi30Tk%2B8i6lczGhRJ77I%2FfoOCYgQHE3osWCyLIkqABkBTvDSNHH" rel="nofollow">Java基础面试题(4)</a></li>
<li><a href="https://link.segmentfault.com/?enc=dLVsPc45xHB4cLNpQKv4lw%3D%3D.t9udkaUOxSzWuVLdNhWwT7hEpmjH0w8d2aqE4Lr7mjjT4Crcb1oUJalF0FTj5dDc" rel="nofollow">Java基础面试题(5)</a></li>
<li><a href="https://link.segmentfault.com/?enc=cYTt4bfsKS5v0vM%2B6Gp50Q%3D%3D.ofQmFnD2xHopMYp9aO%2FhANnI4U2YupViGeyUesQCuGf8U8Zs2JuHX34PjVqCFplF" rel="nofollow">Java多线程与并发面试题</a></li>
<li><a href="https://link.segmentfault.com/?enc=oxQ7kadose1U10OSCmCdNw%3D%3D.HzBssksGQmON70Tu%2BO1vxzpHK6bj%2FYiGFpg9RO7Vl%2FqVCF32I4KZUuZKWErRCm0j" rel="nofollow">Java中高级面试题【第一部分】</a></li>
<li><a href="https://link.segmentfault.com/?enc=UWZxqlXDH60sDpnZ9%2FuLmg%3D%3D.OqhIPKMEG68H6dGQdfoOMIjjbdErwfu6349jEqGxhjFfq%2FhEWG4h3qePa4dBokCu" rel="nofollow">Java中高级面试题【第二部分】</a></li>
<li><a href="https://link.segmentfault.com/?enc=LXNIhy%2B529Ush8T%2BSb71wg%3D%3D.uRyKoF9zTlo4IfNUAOhFF0byXl6VadgKBig%2Futm%2FbhuZKrE%2F%2FZBHkzbdHUhVJyB1" rel="nofollow">JavaEE第二阶段面试题总结(1)</a></li>
<li><a href="https://link.segmentfault.com/?enc=fHeVfKXj3c1Mc2fiDDgvNg%3D%3D.GRw%2F9ml2WIHTsLv8PzTzxRfe%2BpeFBFq6U6MXJmAK%2FtdCK%2FTQrpYDtsbMcvddK%2FlP" rel="nofollow">JavaEE第二阶段面试题总结(2)</a></li>
<li><a href="https://link.segmentfault.com/?enc=meSP9gj3npvJ1SAEAtDDSg%3D%3D.6rCo5S2ZJ%2B66IicV66cGAVbbw9bn88lizHtXPaCbx2jXXHiYCjk1WWxRnPCwsYiz" rel="nofollow">JavaEE第二阶段面试题总结(3)</a></li>
<li><a href="https://link.segmentfault.com/?enc=B0fhCDkvrjbLnvGTblyRnw%3D%3D.wuR86zamoskxcRTWSfv52O2VzWozhteVcBYwEEGEu3EqiVmtSgBKa0QaPWCx9vAp" rel="nofollow">Java岗 面试考点精讲(基础篇01期)</a></li>
<li><a href="https://link.segmentfault.com/?enc=zHntYH%2BokeBZb4YEm4fEaQ%3D%3D.xGjwg0QsFyS9PFkWuKm0xsi0rqXc5MK1XuDd80IwgLaptyvxPjvcfIYvocfgOSfH" rel="nofollow">Java岗 面试考点精讲(基础篇02期)</a></li>
<li><a href="https://link.segmentfault.com/?enc=PDPa8Bhcq41OyVhaoDDOJw%3D%3D.scE14fMh7ZtLRGVnLt3oWHb2ZwJp8daRp7S0d8guQovLUTQPknvgwz33WBBIs2UM" rel="nofollow">Java岗 面试考点精讲(网络篇03期)</a></li>
<li><a href="https://link.segmentfault.com/?enc=srWWqMTmMcurETVUJvy%2FvQ%3D%3D.%2BDOgABH79glbhb0uirOouDMyHJffCODgUGpnSJvzFwTF%2FJcxKKGwdZwvi6aw6QAk" rel="nofollow">2019最新整理JAVA面试题附答案</a></li>
</ul>
<h3>进阶面试题</h3>
<ul>
<li><a href="https://link.segmentfault.com/?enc=%2Bz4zMpvWgq4%2FUGmQ72QCrQ%3D%3D.rh2IQlANMBLRbw6VhAs7qKTdPYbVpvVnRGCQCz7uofji7trqcnPsYCnY%2BcmvUY3%2B" rel="nofollow">Redis面试总结</a></li>
<li><a href="https://link.segmentfault.com/?enc=s%2BAB%2FYolDAlT9Bcu3W%2F2yg%3D%3D.WRTvn%2FBdMCzkzcGBp0DYqf3Os65HEA3g30J6%2BAc9a8jFuTOQZCOzzbRrkeHj3LLr" rel="nofollow">27 道经典 MyBatis 面试题</a></li>
<li><a href="https://link.segmentfault.com/?enc=Ax%2F4%2BVT251zwKzg3sV9ztg%3D%3D.KPDXAWX%2FYqmi5c7JNNcugRhgAp3WnDCiZFrD33FETKp4vOSyVqGQ5dhVxH5iDNpd" rel="nofollow">数据库面试常问的一些基本概念</a></li>
<li><a href="https://link.segmentfault.com/?enc=tAiyoGjmoxbOVfRRdQeazg%3D%3D.brH%2BWFwgf4oAeQphE4xqxyb7oW8jHKtcCc3D%2B7ZK95LtSnWdbgUVkgfI3LDxzvfB" rel="nofollow">经典Java面试题汇总及答案解析</a></li>
<li><a href="https://link.segmentfault.com/?enc=ywMdkHwQLZkGpx0aTkoUog%3D%3D.3DPgIB7PP1rY09%2F%2FbFdnoHtFk0KyLTq5rSXPiWFgc%2BizihmsbpNBfFiCS9mAQCBP" rel="nofollow">Java 面试题:百度前200页都在这里了</a></li>
<li><a href="https://link.segmentfault.com/?enc=LEZ7j1dSCHpvjZGvG%2BMFdA%3D%3D.I5RvMhtimUaPTlpawZNqPmBg4SE0wEvOBr%2FcMuWqrWKeKd90VXDX7F9K8KDkPVBT" rel="nofollow">springboot面试题</a></li>
<li><a href="https://link.segmentfault.com/?enc=%2FHBGZeAo9T04gFPMLwrGEg%3D%3D.nj4yVtYHLyBUOlrD2xANLCcfJN9PhCMbMS1y411i8b1uDqRTN55lwbYwNzne30B7" rel="nofollow">Java面试中常问的计算机网络方面问题</a></li>
<li><a href="https://link.segmentfault.com/?enc=H6p3XxMbVQl76u6%2FTlzigA%3D%3D.fCDg4ORad2HVGwlcuzwRuPSmADAHcuIRrK4M5k0qunTUmTrsto%2FtG2Gz7dlScj9n" rel="nofollow">Java面试中常问的Spring方面问题</a></li>
<li><a href="https://link.segmentfault.com/?enc=xNrXl2mXztorHGz91ummiw%3D%3D.byJ6aDkG%2B063oQTk1GTWjPrAgW2wzSADCDzv1YtLkFl14hcCLIZkTmbO5UecBcNh" rel="nofollow">Java面试中常问的数据库方面问题</a></li>
<li>[[灵魂拷问]MySQL面试高频100问(工程师方向)](<a href="https://link.segmentfault.com/?enc=E9JnE6Wr7up9O0XNjHsE2g%3D%3D.WQwQU3ERRKSMfb28B1xsFH1bweFCSeVscBklHC7xIBryYhQi0sxzwgIUoNKIUGgg" rel="nofollow">http://www.java1000.com/3278....</a>
</li>
<li><a href="https://link.segmentfault.com/?enc=7JepJhqMzCT7yXvh9mnjwQ%3D%3D.jjHWDYHH%2B8RlUS34G%2BIc4Kmipqf8a5h6AxKWF%2F6dqTPrrTt5KIHTZccWIpW6i18m" rel="nofollow">挑战10个最难回答的Java面试题(附答案)</a></li>
<li><a href="https://link.segmentfault.com/?enc=PhvuWiXmsEjB0arkdKyD2A%3D%3D.ausvis96G1Ogql9aD7UdpEfE7Lp1y%2B7%2BA%2BcmxObeoh%2BC91NQXfLXtp7aCXNidNN6" rel="nofollow">Java并发编程73道面试题及答案 —— 面试稳了</a></li>
<li><a href="https://link.segmentfault.com/?enc=hzTBpLxhF0zcpP7SeYQEFg%3D%3D.8zzPrin%2BA9mKnnrXIbv28BI8MNrTHcMMY852bf32Z1ss5TQJpqMwXxSfNY%2FBEWg%2F" rel="nofollow">Java面试手册:答题技巧</a></li>
<li><a href="https://link.segmentfault.com/?enc=gCNoDaAvukpuzkom0Boekw%3D%3D.OzX%2FDQMkTSwfkQUI%2FueJypXJdphWBn0SU2zot%2BG95LbCNQzLFYgO%2FrAZl2p7KFRh" rel="nofollow">Java面试手册:J2EE</a></li>
<li>[]()</li>
<li>[]()</li>
</ul>
<h3>面试经验总结</h3>
<ul>
<li><a href="https://link.segmentfault.com/?enc=6XvK9NaBlS8aFJwAmk2ieA%3D%3D.tvsSUl5800sLuHwbTClAn5e%2FJFiOeTT%2FOfSQMz%2BE3mHNK5CtPlX%2BTCTLfuYIPDQO" rel="nofollow">拿下阿里、头条、滴滴的offer后谈谈面试经验(上)</a></li>
<li><a href="https://link.segmentfault.com/?enc=Hs%2BIq7v8wN7GwI3JcGbGLA%3D%3D.0nucd0myfByv3X317zGB4ivB%2Bwx8JAcAXacr3CBowbNThcWaixUkTCaL%2BytQyKMH" rel="nofollow">Java面试求职经验分享</a></li>
<li><a href="https://link.segmentfault.com/?enc=1nKRf3Mdh4OHkrYB%2FWZtQw%3D%3D.bVIhDB%2FWhnUDcHw6F98uHgKw3iI3iFuPyw3xIXQ1E1aIU7j%2B85dB%2FctjNQ0hqRy1" rel="nofollow">在做技术面试官时,我是这样甄别大忽悠的——如果面试时你有这样的表现,估计悬</a></li>
<li><a href="https://link.segmentfault.com/?enc=oF5DgYoQE3JfO5N%2FWhj1UA%3D%3D.fMIc2fKjrcfRNIACcO7smSNt9RUok%2F0NBkxbD9VC%2BuOahB%2B6l5jvM%2BRmOohHjNAD" rel="nofollow">面试官想看到的以及面试者需要准备的!</a></li>
<li><a href="https://link.segmentfault.com/?enc=KfKSirZC%2FAg9%2FKz4K1HcWQ%3D%3D.CuIFeuicC8HYtJ3Dg%2BTqpxoIDBYwWd4XBVdxpFdtgvbUgzed%2FBn0D29RpEUmYXu4" rel="nofollow">最近面了不少java开发的,给我的感受是。。。</a></li>
<li><a href="https://link.segmentfault.com/?enc=vblctwxeg0KyNEgTEIPRPQ%3D%3D.r5o1xRbHLaCPtm9%2BpF42BxVTDJmzOBFWDuX0qcrkNCW12aZn8TFkxkpLL5ADjjBF" rel="nofollow">如何在面试中介绍自己的项目经验</a></li>
<li><a href="https://link.segmentfault.com/?enc=Uu70lU%2Bzhl%2FzNmYH3i%2Blyg%3D%3D.dm51u1wpuh57M072UHS%2Fm6kt%2B9HUbvQ1iSWDGij7CIWp7m%2BfiQ6tl%2BJZmfQLx7js" rel="nofollow">两年JAVA程序员的面试总结</a></li>
<li><a href="https://link.segmentfault.com/?enc=m%2BDUc2OSxg8AHjA9DPnLCQ%3D%3D.xtUh717LX4jW%2FPASsgGKXthDt70OkRMDTsRVELKiJo5yLCPzSVDWnPrnAYFivuft" rel="nofollow">头条、滴滴、百度面试试题及面试总结</a></li>
</ul>
<h3>更多Java面试题</h3>
<ul><li><a href="https://link.segmentfault.com/?enc=iL2XyvQi7LDy4eq7ll8xyg%3D%3D.EQEIgSKcIvqDkHkILPieJ6le%2F%2B3dgQOKVw5Oce8xG84il8JlixoIYpAdcT%2FFbhFNKDpNiDEmVDQfbnKIHGiVOu2d5ShemS5G7tA7AAlXP58%3D" rel="nofollow">Java面试题大全,请查看!</a></li></ul>
深入理解Java虚拟机-Java内存区域透彻分析
https://segmentfault.com/a/1190000021247652
2019-12-10T15:44:08+08:00
2019-12-10T15:44:08+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
14
<p>这篇文章主要介绍Java内存区域,也是作为Java虚拟机的一些最基本的知识,理解了这些知识之后,才能更好的进行Jvm调优或者更加深入的学习,本来这些知识是晦涩难懂的,所以希望能够讲解的透彻且形象。</p>
<h3>0 运行时数据区域</h3>
<p>JVM载执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。</p>
<p>Java 虚拟机所管理的内存一共分为Method Area(方法区)、VM Stack(虚拟机栈)、Native Method Stack(本地方法栈)、Heap(堆)、Program Counter Register(程序计数器)五个区域。</p>
<p>这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。具体如下图所示:</p>
<p><img src="/img/remote/1460000021247658" alt="" title=""></p>
<p>上图介绍的是JDK1.8 JVM运行时内存数据区域划分。1.8同1.7比,最大的差别就是:<strong>元数据区取代了永久代</strong>。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:<strong>元数据空间并不在虚拟机中,而是使用本地内存</strong>。</p>
<h3>1 程序计数器(Program Counter Register)</h3>
<p><strong>程序计数器(Program Counter Register)</strong>是一块较小的内存空间,可以看作是当前线程所执行的字节码的<strong>行号指示器</strong>。在虚拟机概念模型中,<strong>字节码解释器</strong>工作时就是通过改变计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。</p>
<p>程序计数器是一块 <strong>“线程私有”</strong> 的内存,每条线程都有一个独立的程序计数器,能够将切换后的线程恢复到正确的执行位置。</p>
<ul><li>执行的是一个<strong>Java方法</strong>
</li></ul>
<p>计数器记录的是正在执行的<strong>虚拟机字节码指令的地址</strong>。</p>
<ul><li>执行的是<strong>Native方法</strong>
</li></ul>
<p><strong>计数器为空(Undefined)</strong>,因为native方法是java通过JNI直接调用本地C/C++库,可以近似的认为native方法相当于C/C++暴露给java的一个接口,java通过调用这个接口从而调用到C/C++方法。由于该方法是通过C/C++而不是java进行实现。那么自然无法产生相应的字节码,并且C/C++执行时的内存分配是由自己语言决定的,而不是由JVM决定的。</p>
<ul><li>程序计数器也是唯一一个在Java虚拟机规范中没有规定任何<strong>OutOfMemoryError</strong>情况的内存区域。</li></ul>
<p>其实,我感觉这块区域,作为我们开发人员来说是不能过多的干预的,我们只需要了解有这个区域的存在就可以,并且也没有虚拟机相应的参数可以进行设置及控制。</p>
<h3>2 Java虚拟机栈(Java Virtual Machine Stacks)</h3>
<p><img src="/img/remote/1460000021247655" alt="" title=""></p>
<p><strong>Java虚拟机栈(Java Virtual Machine Stacks)</strong>描述的是<strong>Java方法执行的内存模型</strong>:每个方法在执行的同时都会创建一个<strong>栈帧(Stack Frame)</strong>,从上图中可以看出,栈帧中存储着<strong>局部变量表</strong>、<strong>操作数栈</strong>、<strong>动态链接</strong>、<strong>方法出口</strong>等信息。每一个方法从调用直至执行完成的过程,会对应一个栈帧在虚拟机栈中入栈到出栈的过程。</p>
<p>与程序计数器一样,Java虚拟机栈也是<strong>线程私有</strong>的。</p>
<p>而<strong>局部变量表</strong>中存放了编译期可知的各种:</p>
<ul>
<li>
<strong>基本数据类型</strong>(boolen、byte、char、short、int、 float、 long、double)</li>
<li>
<strong>对象引用</strong>(reference类型,它不等于对象本身,可能是一个指向对象起始地址的指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)</li>
<li>
<strong>returnAddress类型</strong>(指向了一条字节码指令的地址)</li>
</ul>
<p>其中64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其余数据类型只占用1个。<strong>局部变量表所需的内存空间在编译期间完成分配</strong>,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。</p>
<p>Java虚拟机规范中对这个区域规定了两种异常状况:</p>
<ul>
<li>
<strong>StackOverflowError</strong>:线程请求的栈深度大于虚拟机所允许的深度,将会抛出此异常。</li>
<li>
<strong>OutOfMemoryError</strong>:当可动态扩展的虚拟机栈在扩展时无法申请到足够的内存,就会抛出该异常。</li>
</ul>
<p>一直觉得上面的概念性的知识还是比较抽象的,下面我们通过JVM参数的方式来控制栈的内存容量,模拟StackOverflowError异常现象。</p>
<h3>3 本地方法栈(Native Method Stack)</h3>
<p><strong>本地方法栈(Native Method Stack)</strong> 与Java虚拟机栈作用很相似,它们的区别在于虚拟机栈为虚拟机执行Java方法(即字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。</p>
<p>在虚拟机规范中对本地方法栈中使用的语言、方式和数据结构并无强制规定,因此具体的虚拟机可实现它。甚至<strong>有的虚拟机(Sun HotSpot虚拟机)直接把本地方法栈和虚拟机栈合二为一</strong>。与虚拟机一样,本地方法栈会抛出<strong>StackOverflowError</strong>和<strong>OutOfMemoryError</strong>异常。</p>
<ul><li>使用-Xss参数减少栈内存容量(更多的JVM参数可以参考这篇文章:<a href="https://link.segmentfault.com/?enc=Q1qnlXLyJt1vg0M9m%2Fb7Wg%3D%3D.zWq1css3mKAWs3LxFNoK7Wi2Dt8afaQla11KWxSqwSo3hJaetKNS4o4xMkVBiNyZ%2B9647U6AI8o3j%2FSxI%2B0FQ9%2B%2Fox48WwN44nBTqpVT1xR5EL25OERMbzj247amEu4G" rel="nofollow">深入理解Java虚拟机-常用vm参数分析</a>)</li></ul>
<p>这个例子中,我们将栈内存的容量设置为<code>256K</code>(默认1M),并且再定义一个变量查看栈递归的深度。</p>
<pre><code class="java">/**
* @ClassName Test_02
* @Description 设置Jvm参数:-Xss256k
* @Author 欧阳思海
* @Date 2019/9/30 11:05
* @Version 1.0
**/
public class Test_02 {
private int len = 1;
public void stackTest() {
len++;
System.out.println("stack len:" + len);
stackTest();
}
public static void main(String[] args) {
Test_02 test = new Test_02();
try {
test.stackTest();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
</code></pre>
<p>运行时设置JVM参数</p>
<p><img src="/img/remote/1460000021247657" alt="" title=""></p>
<p>输出结果:</p>
<p><img src="/img/remote/1460000021247656" alt="" title=""></p>
<h3>4 Java堆(Heap)</h3>
<p>对于大多数应用而言,<strong>Java堆(Heap)</strong>是Java虚拟机所管理的内存中最大的一块,它<strong>被所有线程共享的</strong>,在虚拟机启动时创建。此内存区域<strong>唯一的目的</strong>是<strong>存放对象实例</strong>,几乎所有的对象实例都在这里分配内存,且每次分配的空间是<strong>不定长</strong>的。在Heap 中分配一定的内存来保存对象实例,实际上只是保存<strong>对象实例的属性值</strong>,<strong>属性的类型</strong>和<strong>对象本身的类型标记</strong>等,<strong>并不保存对象的方法(方法是指令,保存在Stack中)</strong>,在Heap 中分配一定的内存保存对象实例和对象的序列化比较类似。</p>
<p>Java堆是垃圾收集器管理的主要区域,因此也被称为 <strong>“GC堆(Garbage Collected Heap)”</strong> 。从内存回收的角度看内存空间可如下划分:</p>
<p><img src="/img/remote/1460000021247659" alt="图片摘自https://blog.csdn.net/bruce128/article/details/79357870" title="图片摘自https://blog.csdn.net/bruce128/article/details/79357870"></p>
<ul><li>
<strong>新生代(Young)</strong>: 新生成的对象优先存放在新生代中,新生代对象朝生夕死,存活率很低。在新生代中,常规应用进行一次垃圾收集一般可以回收70% ~ 95% 的空间,回收效率很高。</li></ul>
<p>如果把新生代再分的细致一点,新生代又可细分为<strong>Eden空间</strong>、<strong>From Survivor空间</strong>、<strong>To Survivor空间</strong>,默认比例为8:1:1。</p>
<ul>
<li>
<strong>老年代(Tenured/Old)</strong>:在新生代中经历了多次(具体看虚拟机配置的阀值)GC后仍然存活下来的对象会进入老年代中。老年代中的对象生命周期较长,存活率比较高,在老年代中进行GC的频率相对而言较低,而且回收的速度也比较慢。</li>
<li>
<strong>永久代(Perm)</strong>:永久代存储类信息、常量、静态变量、即时编译器编译后的代码等数据,对这一区域而言,Java虚拟机规范指出可以不进行垃圾收集,一般而言不会进行垃圾回收。</li>
</ul>
<p>其中<strong>新生代和老年代组成了Java堆的全部内存区域</strong>,而<strong>永久代不属于堆空间,它在JDK 1.8以前被Sun HotSpot虚拟机用作方法区的实现</strong></p>
<p>另外,再强调一下堆空间内存分配的大体情况,这对于后面一些Jvm优化的技巧还是有帮助的。</p>
<ul>
<li>老年代 : 三分之二的堆空间</li>
<li>年轻代 : 三分之一的堆空间<br>eden区: 8/10 的年轻代空间<br>survivor0 : 1/10 的年轻代空间<br>survivor1 : 1/10 的年轻代空间</li>
</ul>
<p>最后,我们再通过一个简单的例子更加形象化的展示一下<strong>堆溢出</strong>的情况。</p>
<ul><li>JVM参数设置:-Xms10m -Xmx10m</li></ul>
<p>这里将堆的最小值和最大值都设置为10m,如果不了解这些参数的含义,可以参考这篇文章:<a href="https://link.segmentfault.com/?enc=U9F4aqXCtD9h%2BKUB1hTyJA%3D%3D.l%2FshPMHxiwyeymd0ylBPXM4kfqodzn0uT7skUkbJsBAXlgunTetjyP9SO8%2FjaFgTf2qc6WRKyXTv8trmytM76dSrkWen%2FbxjXRbjfZOUSeob23l1L5iPMU1JNcFRKSnu" rel="nofollow">深入理解Java虚拟机-常用vm参数分析</a></p>
<pre><code class="java">/**
* VM Args:-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
* @author zzm
*/
public class HeapTest {
static class HeapObject {
}
public static void main(String[] args) {
List<HeapObject> list = new ArrayList<HeapObject>();
//不断的向堆中添加对象
while (true) {
list.add(new HeapObject());
}
}
}
</code></pre>
<p>输出结果:<br><img src="/img/remote/1460000021247660" alt="" title=""></p>
<p>图中出现了<code>java.lang.OutOfMemoryError</code>,并且提示了<code>Java heap space</code>,这就说明是Java堆内存溢出的情况。</p>
<p><strong>堆的Dump文件分析</strong></p>
<p>我的使用的是VisualVM工具进行分析,关于如何使用这个工具查看这篇文章(<a href="https://link.segmentfault.com/?enc=RSA2wUGCwoHZkkJQ4WcfzA%3D%3D.lucEqnYX905YHQL26QByoLqIATccvdV%2FQnyLJWhQkvu19Tn7nkCVnUD3fUxtpSJyWy6dJPk7Mb4qONS19GPaTMJflCIj7Xz7OVfU0daBXe0lcevgkQyCZH4c7ktb3dasAUGUAhcSixkuPxnoasFVYnEEqK03RetkANKI%2BpDCjrbX5yWfm7TTDGKOrECE8qZy" rel="nofollow">深入理解Java虚拟机-如何利用VisualVM对高并发项目进行性能分析 </a>)。在运行程序之后,会同时打开VisualVM工具,查看堆内存的变化情况。</p>
<p><img src="/img/remote/1460000021247662" alt="" title=""></p>
<p>在上图中,可以看到,堆的最大值是30m,但是使用的堆的容量也快接近30m了,所以很容易发生堆内存溢出的情况。</p>
<p>接着查看dump文件。</p>
<p><img src="/img/remote/1460000021247661" alt="" title=""></p>
<p>如上图,堆中的大部分的对象都是HeapObject,所以,就是因为这个对象的一直产生,所以导致堆内存不够分配,所以出现内存溢出。</p>
<p>我们再看GC情况。</p>
<p><img src="/img/remote/1460000021247663" alt="" title=""></p>
<p>如上图,Eden新生代总共48次minor gc,耗时1.168s,基本满足要求,但是survivor却没有,这不正常,同时Old Gen老年代总共27次full gc,耗时4.266s,耗时长,gc多,这正是因为大量的大对象进入到老年代导致的,所以,导致full gc频繁。</p>
<h3>5 方法区(Method Area)</h3>
<p><strong>方法区(Method Area)</strong> 与Java堆一样,是各个线程共享的内存区域。它用于存储一杯<code>虚拟机加载</code>的<strong>类信息、常量、静态变量、及时编译器编译后的代码</strong>等数据。正因为方法区所存储的数据与堆有一种类比关系,所以它还被称为 <strong>Non-Heap</strong>。</p>
<p><big><strong>运行时常量池(Runtime Constant Pool)</strong></big></p>
<p><strong>运行时常量池(Runtime Constant Pool)</strong>是方法区的一部分。<strong>Class文件</strong>中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是<strong>常量池(Constant Pool Table)</strong>,用于存放编译期生成的各种字面量和符号引用,<strong>这部分内容将在类加载后进入方法区的运行时常量池存放</strong>。</p>
<p>Java虚拟机对Class文件每一部分(自然包括常量池)的格式有严格规定,每一个字节用于存储那种数据都必须符合规范上的要求才会被虚拟机认可、装载和执行。但<strong>对于运行时常量池,Java虚拟机规范没有做任何有关细节的要求</strong>,不同的提供商实现的虚拟机可以按照自己的需求来实现此内存区域。不过一般而言,除了保存<strong>Class文件中的描述符号引用</strong>外,还会把<strong>翻译出的直接引用</strong>也存储在运行时常量池中。</p>
<p>运行时常量池相对于Class文件常量池的另外一个重要特征是具备<strong>动态性</strong>,Java语言并不要求常量一定只有编译器才能产生,也就是<strong>并非置入Class文件中的常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中</strong>。</p>
<h4>运行时常量池举例</h4>
<p>上面的<strong>动态性</strong>在开发中用的比较多的便是String类的<code>intern()</code> 方法。所以,我们以<code>intern()</code> 方法举例,讲解一下<strong>运行时常量池</strong>。</p>
<p><code>String.intern()</code>是一个<code>native</code>方法,作用是:如果字符串常量池中已经包含有一个等于此String对象的字符串,则直接返回池中的字符串;否则,加入到池中,并返回。</p>
<pre><code class="java">/**
* @ClassName MethodTest
* @Description vm参数设置:-Xms512m -Xmx512m -Xmn128m -XX:PermSize=10M -XX:MaxPermSize=10M -XX:NewRatio=4 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:-HeapDumpOnOutOfMemoryError -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
* @Author 欧阳思海
* @Date 2019/11/25 20:06
* @Version 1.0
**/
public class MethodTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
long i = 0;
while (i < 1000000000) {
System.out.println(i);
list.add(String.valueOf(i++).intern());
}
}
}</code></pre>
<p>vm参数介绍:</p>
<blockquote>-Xms512m -Xmx512m -Xmn128m -XX:PermSize=10M -XX:MaxPermSize=10M -XX:NewRatio=4 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:-HeapDumpOnOutOfMemoryError -XX:+UseParNewGC -XX:+UseConcMarkSweepGC<br>开始堆内存和最大堆内存都是512m,永久代大小10m,新生代和老年代1:4,E:S1:S2=8:1:1,最大经过15次survivor进入老年代,使用的,垃圾收集器是新生代ParNew,老年代CMS。</blockquote>
<p>通过这样的设置之后,查看运行结果:<br><img src="/img/remote/1460000021247664" alt="" title=""></p>
<p>首先堆内存耗完,然后看看GC情况,设置这些参数之后,GC情况应该会不错,拭目以待。</p>
<p><img src="/img/remote/1460000021247665" alt="" title=""></p>
<p>上图是GC情况,我们可以看到<strong>新生代</strong> 21 次minor gc,用了1.179秒,平均不到50ms一次,性能不错,<strong>老年代</strong> 117 次full gc,用了45.308s,平均一次不到1s,性能也不错,说明jvm运行是不错的。</p>
<blockquote>
<strong>注意:</strong> 在JDK1.6及以前的版本中运行以上代码,因为我们通过<code>-XX:PermSize=10M -XX:MaxPermSize=10M</code>设置了方法区的大小,所以也就是设置了常量池的容量,所以运行之后,会报错:<code>java.lang.OutOfMemoryError:PermGen space</code>,这说明常量池溢出;在JDK1.7及以后的版本中,将会一直运行下去,不会报错,在前面也说到,JDK1.7及以后,去掉了永久代。</blockquote>
<h3>6 直接内存</h3>
<p><strong>直接内存(Direct Memory)</strong>并不是虚拟机<strong>运行时数据区</strong>的一部分,也不是Java虚拟机规范中定义的内存区域。但这部分内存也被频繁运用,而却可能导致<strong>OutOfMemoryError</strong>异常出现。</p>
<p>这个我们实际中主要接触到的就是NIO,在NIO中,我们为了能够加快IO操作,采用了一种直接内存的方式,使得相比于传统的IO快了很多。</p>
<p>在NIO引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配<strong>堆外内存</strong>,然后通过一个存储在Java堆中的<code>DirectByteBuffer</code>对象作为这块内存的引用进行操作。这样能避免在Java堆和Native堆中来回复制数据,在一些场景里显著提高性能。</p>
<p>在配置虚拟机参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统的限制),从而导致动态扩展时出现<strong>OutOfMemoryError</strong>异常。</p>
<p><strong>Java虚拟机深入理解</strong>系列全部文章更新中...</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=JDv4oPwVoKEtp6NEFPCOCQ%3D%3D.0VtYDiQBBa9pYOwbq0mcBgWTJ4dlbGnij%2FfD9KVfCHYjuSoLxp5VYBNR%2F0Qh44ob%2FffiiFOp8xgSU5dgJHl7IC8I4uJt0HzHIk6ObczFyOrHP4Jmmr4HfkpTE4LjlP6k" rel="nofollow">深入理解Java虚拟机-Java内存区域透彻分析 </a></li>
<li><a href="https://link.segmentfault.com/?enc=MLY5IJ1th8rD4tEc9GYy0g%3D%3D.z%2FEOtbhdfo3mCdhVWOOMshgPbVZsvATzhWSXwGH26mJyj%2BmH8GujzaX2ImsCxGl%2FzIMYP9VVMYp4Ossw0JtSyo0gQ2g3GGp1BHEGacdM6YyW%2Fsly6U8NlsIkLEv2KUzj" rel="nofollow">深入理解Java虚拟机-常用vm参数分析 </a></li>
<li><a href="https://link.segmentfault.com/?enc=M8xl%2BB%2Bx%2FQ9YzKT1fTqCjA%3D%3D.BhfNjFP6c9%2BYMBELR0hz%2BQpfstOE282KUXGWdobq3E6V8lSEd2ydaJhGSgGeW7BVe6k4eiTyYz8CnAjbi6yOVNqjLn5ws0fdR4ydE1XqMb23S%2F4gLdh7Ar1HlwdXy1QoxN%2FWhov8ynai5M7Jk817WVYdk9WfcSKtm5RxTV5qmdriagNIQtVAD9JccwuSAXy5%2B53t7N2bx4D7YIQvDDAm2w%3D%3D" rel="nofollow">深入理解Java虚拟机-JVM内存分配与回收策略原理,从此告别JVM内存分配文盲</a></li>
<li><a href="https://link.segmentfault.com/?enc=ZnxOWcrFT%2F4TcIMJIs35ZA%3D%3D.GXAkwyj3KUNtZMq77Idb%2FhHQSxG9tGBZOIEZAlOcaNzEma8bw9zIhlnpPWYFkVXry7eGholrfcVzYAb7EbG0ISQVwH7RJ3Od%2FhFiULIgYOG1mhlueAuqRNls3lBmJE2zRTbmdQjvoJ77HeIm2bEMlyejLiOGEvEGChS%2F7Xe1hX3cIXJ4CJre3L5S4RAmZ084T%2FOSyfLTxg6k%2FFqDdyn8KCOEeDM5V8CCwdOhBqSSDjo%3D" rel="nofollow">深入理解Java虚拟机-如何利用JDK自带的命令行工具监控上百万的高并发的虚拟机性能 </a></li>
<li><a href="https://link.segmentfault.com/?enc=a53cD0Z8%2FC9CjpT0vWB0MQ%3D%3D.nxLry%2FYFkMZUaAiDEJ7xihaYZy6Nou%2FVkPGkx249zq1tgm8tLndDVz3rcuqCYbQi3rdYXJVpAhEQ6P7PfG%2BQXu%2B8qodonaQp6jjvmyvbaQvaSR0203Y75ZUJiq37jAOkF4E8oGZL6ZmrixWc%2BsuXBxBbs9LjT2mJAPKWcBogWiD02d9yNPvCGvS9CELypige" rel="nofollow">深入理解Java虚拟机-如何利用VisualVM对高并发项目进行性能分析</a></li>
<li><a href="https://link.segmentfault.com/?enc=WQWoH0vnS0kg0HXAKOtbGw%3D%3D.5QlvPuwcQC2IOGQt5%2B8AEBrn7i8%2F0neghdn3GKjn%2BW0yKsXH%2Bc6f741tjr4X9jhL5aI15gqylLhBSk6gZMBnePXXGFmnSajrBUBcG1%2Fw%2FVPrlVNHgTCUYgyxYDCcl9D7" rel="nofollow">深入理解Java虚拟机-你了解GC算法原理吗 </a></li>
</ul>
<blockquote>1、<strong>原创不易</strong>,老铁,文章需要你的 <strong>点赞</strong> 让更多的人看到,希望能够帮助到大家!<p>2、文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的<strong>微信公众号</strong>:<strong><code>好好学java</code></strong>,公众号已有 <strong>6W</strong> 粉丝,回复:<strong>1024</strong>,获取公众号的大礼包,公众号长期发布 <strong>Java 优质系列文章</strong>,关注我们一定会让你收获很多!</p>
<p><img src="/img/remote/1460000021254987" alt="" title=""></p>
</blockquote>
MySQL事务,这篇文章就够了
https://segmentfault.com/a/1190000020857341
2019-10-30T12:07:12+08:00
2019-10-30T12:07:12+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
35
<blockquote>原文链接:<a href="https://link.segmentfault.com/?enc=F6x5qAcv3c8voRQNxN1Dfg%3D%3D.xOGOJ2fqUmdVlRAzK5eBw6rLBbrmMOZ3Djysie91cjQ%3D" rel="nofollow">https://blog.ouyangsihai.cn/</a> >> <a href="https://link.segmentfault.com/?enc=AeUXI9tqsal9yXPhDxsMFg%3D%3D.r4apuP5xNhdFfJY7Twtg9cSJKxieCPNI%2BE%2F255HZLPG7OLVQlZQqDjRltCivrbI6ouLxoeZDkWU7H%2Bc6bqFZJlEzUef8f%2Fwxn3B5R0hjI8E%3D" rel="nofollow">MySQL事务,这篇文章就够了</a>
</blockquote>
<p>在看这篇文章之前,我们回顾一下前面的几篇关于MySQL的系列文章,应该对你读下面的文章有所帮助。</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=GwQLscJEo8oimSkuASlN1g%3D%3D.oLl81wriKGODxBsz%2FqtVThw5iVAwKKmLlUWnHDYrCPUfgVuXPaNZnqmPUbJMVZn1iL%2BGLPBsReP6SHtqhWjNoWvCpRD98DkYNupGgQiPYhY%3D" rel="nofollow">InnoDB与MyISAM等存储引擎对比 </a></li>
<li><a href="https://link.segmentfault.com/?enc=HJ9WO2x0cNYES004Xj3OLA%3D%3D.NhgdC65IJpEb3Vpr0yHT6h%2BR%2BmkYaqz3HFWL5qbiJOmBfbXkOyouCqG%2FRXRzdrnDtDOyoAdlXuWFK7WYroPtCDaeNAD07c5Khe1zPLhDXibjDb0AmtrMx8SA88DaiwXddANoGJ2Nygc9er5trXXQlw%3D%3D" rel="nofollow">面试官问你B树和B+树,就把这篇文章丢给他 </a></li>
<li><a href="https://link.segmentfault.com/?enc=PM8hsDkHG6yyur007HzmYg%3D%3D.yj5Wavt2JjCHRYKAl00lwL5fHwu2Ia1eZXYm5eeO3l1uDX%2B%2By3ZMaVe1shO0zxQH6kipuH5KkqwqbrOuGdQPsA%3D%3D" rel="nofollow">MySQL的B+树索引的概念、使用、优化及使用场景 </a></li>
<li><a href="https://link.segmentfault.com/?enc=hyh9XDuukTZc%2FSue4wvdAA%3D%3D.9Z3CiLv8OlxdpUrPYIL2hW%2FuJAflSvnVaLwZ3zzLeIfL6D0pEjktSmQs1BGMd5t%2FLbg8d835wH22Q2lymlsJ0Q%3D%3D" rel="nofollow">MySQL全文索引最强教程 </a></li>
<li><a href="https://link.segmentfault.com/?enc=LfsjADElQixSxJjT6eb4jw%3D%3D.iiULsu9Jdx4BumPYWWpkHF4RDoEIHsv5XOqMoY9jBfuFZzZqbNc2btZb5aXW1Hlcgv0psMxNp7adGBK3byCSxw%3D%3D" rel="nofollow">MySQL的又一神器-锁,MySQL面试必备</a></li>
</ul>
<h3>0 什么是事务</h3>
<p><strong>事务(Transaction)</strong> 是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都 执行,要么都不执行,它是一个不可分割的工作单位。事务是数据库维护数据一致性的单位,在每 个事务结束时,都能保持数据一致性。</p>
<p>同时,事务有着严格的地定义,必须满足四个特性,也就是我们一直说的ACID,但是,并不是说各种数据库就一定会满足四个特性,对于不同的数据库的实现来说,在不同程度上是不一定完全满足要求的,比如,Oracle数据库来说,默认的事务隔离级别是<code>READ COMMITTED</code>,是不满足隔离性的要求的。</p>
<p>下面我们趁热打铁,介绍一下事务的必知必会的四大特性,这几个特性也是在面试中,面试官面试MySQL的相关知识的时候,问的比较多的问题,所以,这几个特性务必需要理解并且透彻的记在心里,开个玩笑,被火车撞了,也不应该忘记这四个特性!</p>
<h3>1 事务的四大特性</h3>
<p>事务的四大特性简称为:<code>ACID</code>,分别是<strong>原子性、一致性、隔离性和持久性</strong>。</p>
<p>下面我们一一来介绍一下。</p>
<ul><li>原子性(Atomicity)</li></ul>
<p>原子性指的是整个数据库的事务是一个不可分割的工作单位,每一个都应该是一个原子操作。</p>
<p>当我们执行一个事务的时候,如果一系列的操作中,有一个操作失败了,那么,需要将这一个事务中的所有操作恢复到执行事务之前的状态,这就是事务的原子性。</p>
<p>下面举个简单的例子。</p>
<pre><code class="java">i++;</code></pre>
<p>上面这个最简单不过的代码经常也会被问到,这是一个原子操作吗?那肯定不是,如果我们把这个代码放到一个事务中来说,当<code>i+1</code>出现问题的时候,回滚的就是整个代码i++(i = i + 1)了,所以回滚之后,i的值也是不会改变的。</p>
<p>以上就是原子性的概念。</p>
<ul><li>一致性(consistency)</li></ul>
<p><strong>一致性</strong>是指事务将数据库从一种状态转变为下一种一致性的状态,也就是说在事务执行前后,这两种状态应该是一样的,也就是数据库的完整性约束不会被破坏。</p>
<p>另外,需要注意的是一致性是不关注中间状态的,比如银行转账的过程,你转账给别人,至于中间的状态,你少了500 ,他多了500,这些中间状态不关注,如果分多次转账中间状态也是不可见的,只有最后的成功或者失败的状态是可见的。</p>
<p>如果到分布式的一致性问题,又可以分为强一致性、弱一致性和最终一致性,关于这些概念,可以自己查查,还是很有意思的。</p>
<ul><li>隔离性(isolation)</li></ul>
<p>事务我们是可以开启很多的,MySQL数据库中可以同时启动很多的事务,但是,事务和事务之间他们是相互分离的,也就是互不影响的,这就是事务的<strong>隔离性</strong>。</p>
<ul><li>持久性(durability)</li></ul>
<p>事务的<strong>持久性</strong>是指事务一旦提交,就是永久的了,就是发生问题,数据库也是可以恢复的。因此,持久性保证事务的高可靠性。</p>
<h3>2 事务的分类</h3>
<p>事务可以分为很多中类型,一般分为:<strong>扁平事务、带有保存点的扁平事务、链事务、嵌套事务、分布式事务</strong>。</p>
<h4>扁平事务</h4>
<p>扁平事务是最简单的一种,在实际开发中也是使用的最多的一种事务。在这种事务中,所有操作都处于同一层次,最常见的方式如下:</p>
<pre><code class="java">BEGIN WORK
Operation 1
Operation 2
Operation 3
...
Operation N
COMMIT WORK</code></pre>
<p><strong>举个例子</strong></p>
<pre><code>begin work;
select * from user;
update user set name = 'sihai' where id = 1;
commit work;</code></pre>
<p>扁平事务的主要<strong>缺点</strong>是不能提交或回滚事务的某一部分,或者分几个独立的步骤去提交。</p>
<h4>带有保存点的扁平事务</h4>
<p>这种事务除了支持扁平事务支持的操作外,这种事务跟扁平事务最大的区别就是<strong>允许在事务执行过程中回滚到同一事务中较早的一个状态</strong>,这是因为可能某些事务在执行过程中出现的错误并不会对所有的操作都无效,放弃整个事务不合乎要求,开销也太大。<strong>保存点</strong>用来通知系统应该记住事务当前的状态,以便以后发生错误时,事务能回到该状态。</p>
<p><strong>举个例子</strong></p>
<pre><code>begin work;
select * from user;
savepoint t1;
update user set name = 'sihai' where id = 1;
savepoint t2;
commit work;</code></pre>
<p>通过上面的方式我们就建立了两个保存点t1、t2,通过<code>ROLLBACK TO SAVEPOINT t1</code>,我们就可以返回到<code>保存点t1</code>。</p>
<h4>链事务</h4>
<p>链事务:在提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式的传给下一个要开始的事务。需要注意,提交事务操作和下一个事务操作将合并为一个<strong>原子操作</strong>,就是下一个事务可以看到上一个事务的结果。</p>
<p>链事务,就是指回滚时,只能恢复到最近一个保存点;而带有保存点的扁平事务则可以回滚到任意正确的保存点。</p>
<p><img src="/img/remote/1460000020857344" alt="" title=""></p>
<p><strong>举个例子</strong></p>
<pre><code>begin work;
select * from user;
savepoint t1;
update user set name = 'sihai' where id = 1;
savepoint t2;
commit work;</code></pre>
<p>还是这个例子,但是对于链事务来说,是不能直接rollback到保存点t1的,最能恢复到最近的一个保存点t2;另外我们<strong>需要注意</strong>,链事务在执行commit后就会释放当前事务所持有的所有锁,而带有保存点的扁平事务不会影响所持有的锁。</p>
<h4>嵌套事务</h4>
<p>在事务中再嵌套事务,这种结构有点像一颗横着的树的结构,位于根节点的事务称为顶层事务。事务的前驱称为父事务,其它事务称为子事务。事务的前驱称为父事务,事务的下一层称为子事务。</p>
<p>子事务既可以提交也可以回滚,但是它的提交操作并不马上生效,除非由其父事务提交。因此就可以确定,任何子事务都在顶层事务提交后才真正的被提交了。同理,任意一个事务的回滚都会引起它的所有子事务一同回滚。</p>
<p><img src="/img/remote/1460000020857345" alt="" title=""></p>
<pre><code class="java">BEGIN WORK
SubTransaction1:
BEGIN WORK
SubOperationX
COMMIT WORK
SubTransaction2:
BEGIN WORK
SubOperationY
COMMIT WORK
...
SubTransactionN:
BEGIN WORK
SubOperationN
COMMIT WORK
COMMIT WORK</code></pre>
<h4>分布式事务</h4>
<p>分布式事务通常是指在一个分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点。</p>
<p>在不同的物理地址,通过网络访问,执行不同的事务,这就是分布式事务。</p>
<h3>3 事务的使用</h3>
<p>首先这一部分我们还是先介绍一下这些事务的语句,也不是很多,使用也不复杂,下面用一个表格做一个整理。</p>
<p><img src="/img/remote/1460000020857346" alt="" title=""></p>
<blockquote>
<strong>注意</strong>:<code>COMMIT</code>和<code>COMMIT WORK</code>语句不同之处在于COMMIT WORK用来控制事务结束后的行为是<code>CHAIN</code>还是<code>RELEASE</code>,如果是CHAIN,那么事务就是<strong>链事务</strong>。</blockquote>
<p>用户可以通过参数<code>completion_type</code>控制,如下:</p>
<p><img src="/img/remote/1460000020857347" alt="" title=""></p>
<ul><li>completion_type = 1 实例</li></ul>
<p>执行下面的操作;</p>
<pre><code class="java">SET @@completion_type = 1;
BEGIN WORK;
INSERT INTO lock_test SELECT 10;
COMMIT WORK;</code></pre>
<p>接着我们再执行下面的操作;</p>
<pre><code>INSERT INTO lock_test SELECT 115;
ROLLBACK;
SELECT * FROM lock_test;</code></pre>
<p>我们先插入一条数据115,然后再回滚,我们知道如果不是在一个事务的时候,115应该是会插入成功的,就算我们回滚了,但是,这里我们回滚之后,查询结果如下:</p>
<p><img src="/img/remote/1460000020857348" alt="" title=""></p>
<p>这个时候并没有115这条记录,也就是回滚生效了,说明在<code>COMMIT WORK</code>之后,又是一个新的事务,所以才会出现这样的结果。</p>
<ul><li>completion_type = 2 实例</li></ul>
<p>我们先进行下面的操作;</p>
<pre><code class="java">SET @@completion_type = 2;
BEGIN WORK;
INSERT INTO lock_test SELECT 5;
COMMIT WORK;</code></pre>
<p>上面我们已经提交事务了,当我们使用下面的语句查询lock_test的数据的时候,就会出现<strong>断开连接</strong>。</p>
<pre><code class="java">SELECT * FROM lock_test;</code></pre>
<p><img src="/img/remote/1460000020857349" alt="" title=""></p>
<h3>4 事务的隔离级别</h3>
<p>事务的隔离级别有四种分别是:</p>
<ul>
<li>READ UNCOMMITTED</li>
<li>READ COMMITTED</li>
<li>REPEATABLE READ</li>
<li>SERIALIZABLE</li>
</ul>
<p>对于这几种隔离级别会带来的问题及总结,可以查看这篇文章:<a href="https://link.segmentfault.com/?enc=0gOfIWuGN%2FuWd4TF2menTQ%3D%3D.VzX23DRFx%2BxnCJlzfPTaSRleQVG3NicYfYuTrWN4lWH6sAn39iYE7F4Gs2hX3JT0c4sBQh5nEUhoMwr6niROEg%3D%3D" rel="nofollow">MySQL的又一神器-锁,MySQL面试必备 </a></p>
<h3>5 总结</h3>
<p>这篇文章从下面几个内容介绍了一下MySQL数据库事务的内容,更详细的其他内容在后面的文章中再讲解。</p>
<ul>
<li>概念</li>
<li>事务类型</li>
<li>事务使用</li>
<li>事务的隔离级别</li>
</ul>
<blockquote>1、<strong>原创不易</strong>,老铁,文章需要你的 <strong>点赞</strong> 让更多的人看到,希望能够帮助到大家!<p>2、文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的<strong>微信公众号</strong>:<strong><code>好好学java</code></strong>,公众号已有 <strong>6W</strong> 粉丝,回复:<strong>1024</strong>,获取公众号的大礼包,公众号长期发布 <strong>Java 优质系列文章</strong>,关注我们一定会让你收获很多!</p>
<p><img src="/img/remote/1460000021254987" alt="" title=""></p>
</blockquote>
MySQL的又一神器-锁,MySQL面试必备
https://segmentfault.com/a/1190000020762791
2019-10-22T12:33:13+08:00
2019-10-22T12:33:13+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
105
<blockquote>原文链接:blog.ouyangsihai.cn >> <a href="https://link.segmentfault.com/?enc=PB%2Bt3QzisKR1GNKbGeC%2FUQ%3D%3D.r9G%2FAPYIkeJasqcoOw7kwtZwO%2FI7f3gHk0RPiebPqfpfwSJP5tH%2BzOEwAdylTzVgcpjT7LfaElyRoMplB76gQA%3D%3D" rel="nofollow">MySQL的又一神器-锁,MySQL面试必备</a>
</blockquote>
<p>在看这篇文章之前,我们回顾一下前面的几篇关于MySQL的文章,应该对你读下面的文章有所帮助。</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=NqlyGMAlIKCT3rVzb1ZUUg%3D%3D.%2Fi6TLc65kD7H%2FsoNcqbomRAQKZCzv1IBnxehnulBGhNT9mUuFf5THszc0N8ywHnIWxC6tmeVRBv6ayp%2FCepYkThG402%2BQ%2Fr1tKlX%2B0TOHb4%3D" rel="nofollow">InnoDB与MyISAM等存储引擎对比 </a></li>
<li><a href="https://link.segmentfault.com/?enc=gpYTaQ%2FytU4gHx2iVOHfmw%3D%3D.1QBXhVx1ZC8do2oOG%2FsPeZKBdSLhsVwVobtjDAF90sud5fGZyLzcx%2F%2FB%2FkKoJUclqRiXpfqOJvvqBafBDcU48H5uhfATQXkesn%2BYdM5xmiAtm42RYCCAZ1O3FXSTbC494iVoxNgcreTq%2BCrjApagAg%3D%3D" rel="nofollow">面试官问你B树和B+树,就把这篇文章丢给他 </a></li>
<li><a href="https://link.segmentfault.com/?enc=cdicfEWZd%2BDyYo4kiFKmmw%3D%3D.slnmQVsTAqSuiGAwROksnlQPpHL%2Fss2MDT%2BVLirYKRcUYS12nG7FpKjJ6NZqxGSEYOTIudvbk%2BVMxwBR1nGV8A%3D%3D" rel="nofollow">MySQL的B+树索引的概念、使用、优化及使用场景 </a></li>
<li><a href="https://link.segmentfault.com/?enc=UVvYtaF%2FUu2UtC8EFSAGLw%3D%3D.o88PxH2k79knLN0Xj8j%2FBZttlln96il9U3%2FwH1aNxYBurTUvYBrl0j46hPNyTMwO0i463Drd6eqafldU3F4d7Q%3D%3D" rel="nofollow">MySQL全文索引最强教程 </a></li>
<li><a href="https://link.segmentfault.com/?enc=3JdeUlexqLmqYTGO6Si3yg%3D%3D.SLpV2guyXWZg%2FWSPBv6OVVdRQlW7FlWR%2FoxJUB%2BXirOZ9cLV0eM6OdeRbseWh%2FcpBhMfU4qUG7bA8eTn7Den%2Fg%3D%3D" rel="nofollow">MySQL的又一神器-锁,MySQL面试必备</a></li>
</ul>
<h3>1 什么是锁</h3>
<h4>1.1 锁的概述</h4>
<p>在生活中锁的例子多的不能再多了,从古老的简单的门锁,到密码锁,再到现在的指纹解锁,人脸识别锁,这都是锁的鲜明的例子,所以,我们理解锁应该是非常简单的。</p>
<p>再到MySQL中的锁,对于MySQL来说,锁是一个很重要的特性,数据库的锁是为了支持对共享资源进行并发访问,提供数据的完整性和一致性,这样才能保证在高并发的情况下,访问数据库的时候,数据不会出现问题。</p>
<h4>1.2 锁的两个概念</h4>
<p>在数据库中,lock和latch都可以称为锁,但是意义却不同。</p>
<p><strong>Latch</strong>一般称为<code>闩锁</code>(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差,在InnoDB引擎中,Latch又可以分为<code>mutex</code>(互斥量)和<code>rwlock</code>(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。</p>
<p><strong>Lock</strong>的对象是<code>事务</code>,用来锁定的是数据库中的对象,如表、页、行。并且一般lock的对象仅在事务commit或rollback后进行释放(不同事务隔离级别释放的时间可能不同)。</p>
<h3>2 InnoDB存储引擎中的锁</h3>
<h4>2.1 锁的粒度</h4>
<p>在数据库中,锁的粒度的不同可以分为表锁、页锁、行锁,这些锁的粒度之间也是会发生升级的,<strong>锁升级</strong>的意思就是讲当前锁的粒度降低,数据库可以把一个表的1000个行锁升级为一个页锁,或者将页锁升级为表锁,下面分别介绍一下这三种锁的粒度(参考自博客:<a href="https://link.segmentfault.com/?enc=e8IT40KbS%2Beu3UzdgUmZ%2FQ%3D%3D.iU%2BhNiBqsstm%2Fo9h5tOGsLY8SlFOr0L0k6HCZjbdLf1i%2BKHldkoDofM%2BrnpiM6eNz%2BaOIbhSMjA1skRq6%2F4ztQ%3D%3D" rel="nofollow">https://blog.csdn.net/baoling...</a>)。</p>
<h5>表锁</h5>
<p>表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。</p>
<p>当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并大度大打折扣。</p>
<p>使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。</p>
<p><strong>特点:</strong> 开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。</p>
<h5>页锁</h5>
<p>页级锁定是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,会发生死锁。<br>在数据库实现资源锁定的过程中,随着锁定资源颗粒度的减小,锁定相同数据量的数据所需要消耗的内存数量是越来越多的,实现算法也会越来越复杂。不过,随着锁定资源 颗粒度的减小,应用程序的访问请求遇到锁等待的可能性也会随之降低,系统整体并发度也随之提升。<br>使用页级锁定的主要是BerkeleyDB存储引擎。</p>
<p><strong>特点:</strong> 开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。</p>
<h5>行锁</h5>
<p>行级锁定最大的特点就是锁定对象的粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。</p>
<p>虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁。</p>
<p><strong>特点:</strong> 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。</p>
<p>比较表锁我们可以发现,这两种锁的特点基本都是相反的,而从锁的角度来说,<strong>表级锁</strong>更适合于以查询为主,只有少量按索引条件更新数据的应用,如Web应用;而<strong>行级锁</strong>则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。</p>
<h5>MySQL 不同引擎支持的锁的粒度</h5>
<p><img src="/img/remote/1460000020762795?w=627&h=180" alt="" title=""></p>
<h4>2.2 锁的类型</h4>
<p>InnoDB存储引擎中存在着不同类型的锁,下面一一介绍一下。</p>
<p><strong>S or X (共享锁、排他锁)</strong></p>
<p>数据的操作其实只有两种,也就是读和写,而数据库在实现锁时,也会对这两种操作使用不同的锁;InnoDB 实现了标准的<strong>行级锁</strong>,也就是<strong>共享锁(Shared Lock)和互斥锁(Exclusive Lock)</strong>。</p>
<ul>
<li>共享锁(读锁)(S Lock),允许事务读一行数据。</li>
<li>排他锁(写锁)(X Lock),允许事务删除或更新一行数据。</li>
</ul>
<p><strong>IS or IX (共享、排他)意向锁</strong></p>
<p>为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB存储引擎支持一种额外的锁方式,就称为<strong>意向锁</strong>,意向锁在 InnoDB 中是<strong>表级锁</strong>,意向锁分为:</p>
<ul>
<li>意向共享锁:表达一个事务想要获取一张表中某几行的共享锁。</li>
<li>意向排他锁:表达一个事务想要获取一张表中某几行的排他锁。</li>
</ul>
<p>另外,这些锁之间的并不是一定可以共存的,有些锁之间是不兼容的,所谓<strong>兼容性</strong>就是指事务 A 获得一个某行某种锁之后,事务 B 同样的在这个行上尝试获取某种锁,如果能立即获取,则称锁兼容,反之叫冲突。</p>
<p>下面我们再看一下这两种锁的兼容性。</p>
<ul><li>S or X (共享锁、排他锁)的兼容性</li></ul>
<p><img src="/img/remote/1460000020762796?w=626&h=142" alt="" title=""></p>
<ul><li>IS or IX (共享、排他)意向锁的兼容性</li></ul>
<p><img src="/img/remote/1460000020762797?w=677&h=232" alt="" title=""></p>
<h3>3 前面小结</h3>
<p>这里用一个思维导图把前面的概念做一个小结。</p>
<p><img src="/img/remote/1460000020762798" alt="" title=""></p>
<h3>4 一致性非锁定读和一致性锁定读</h3>
<h4>一致性锁定读(Locking Reads)</h4>
<p>在一个事务中查询数据时,普通的SELECT语句不会对查询的数据进行加锁,其他事务仍可以对查询的数据执行更新和删除操作。因此,InnoDB提供了两种类型的锁定读来保证额外的安全性:</p>
<ul>
<li><code>SELECT ... LOCK IN SHARE MODE</code></li>
<li><code>SELECT ... FOR UPDATE</code></li>
</ul>
<p><code>SELECT ... LOCK IN SHARE MODE</code>: 对读取的行添加S锁,其他事物可以对这些行添加S锁,若添加X锁,则会被阻塞。 </p>
<p><code>SELECT ... FOR UPDATE</code>: 会对查询的行及相关联的索引记录加X锁,其他事务请求的S锁或X锁都会被阻塞。 当事务提交或回滚后,通过这两个语句添加的锁都会被释放。 注意:只有在自动提交被禁用时,SELECT FOR UPDATE才可以锁定行,若开启自动提交,则匹配的行不会被锁定。</p>
<p>#### 一致性非锁定读</p>
<p><strong>一致性非锁定读(consistent nonlocking read)</strong> 是指InnoDB存储引擎通过多版本控制(MVVC)读取当前数据库中行数据的方式。如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此去等待行上锁的释放。相反地,InnoDB会去读取行的一个快照。所以,非锁定读机制大大提高了数据库的并发性。</p>
<p><img src="/img/remote/1460000020762799" alt="来自网络:侵权删" title="来自网络:侵权删"></p>
<p>一致性非锁定读是InnoDB默认的读取方式,即读取不会占用和等待行上的锁。在事务隔离级别<code>READ COMMITTED</code>和<code>REPEATABLE READ</code>下,InnoDB使用一致性非锁定读。</p>
<p>然而,对于快照数据的定义却不同。在<code>READ COMMITTED</code>事务隔离级别下,一致性非锁定读总是<strong>读取被锁定行的最新一份快照数据</strong>。而在<code>REPEATABLE READ</code>事务隔离级别下,则<strong>读取事务开始时的行数据版本</strong>。</p>
<p>下面我们通过一个简单的例子来说明一下这两种方式的区别。</p>
<p>首先创建一张表;</p>
<p><img src="/img/remote/1460000020762800" alt="" title=""></p>
<p>插入一条数据;</p>
<pre><code>insert into lock_test values(1);</code></pre>
<p>查看隔离级别;</p>
<pre><code>select @@tx_isolation;</code></pre>
<p><img src="/img/remote/1460000020762801" alt="" title=""></p>
<p>下面分为两种事务进行操作。</p>
<p>在<code>REPEATABLE READ</code>事务隔离级别下;</p>
<p><img src="/img/remote/1460000020762802" alt="" title=""></p>
<p>在<code>REPEATABLE READ</code>事务隔离级别下,读取事务开始时的行数据,所以当会话B修改了数据之后,通过以前的查询,还是可以查询到数据的。</p>
<p>在<code>READ COMMITTED</code>事务隔离级别下;</p>
<p><img src="/img/remote/1460000020762803?w=625&h=584" alt="" title=""></p>
<p>在<code>READ COMMITTED</code>事务隔离级别下,读取该行版本最新的一个快照数据,所以,由于B会话修改了数据,并且提交了事务,所以,A读取不到数据了。</p>
<h3>5 行锁的算法</h3>
<p>InnoDB存储引擎有3种行锁的算法,其分别是:</p>
<ul>
<li>Record Lock:单个行记录上的锁。</li>
<li>Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。</li>
<li>Next-Key Lock:Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身。</li>
</ul>
<p><strong>Record Lock</strong>:总是会去锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时InnoDB存储引擎会使用隐式的主键来进行锁定。</p>
<p><strong>Next-Key Lock</strong>:结合了Gap Lock和Record Lock的一种锁定算法,在Next-Key Lock算法下,InnoDB对于行的查询都是采用这种锁定算法。举个例子10,20,30,那么该索引可能被Next-Key Locking的区间为:<br><img src="/img/remote/1460000020762804" alt="" title=""></p>
<p>除了Next-Key Locking,还有<strong>Previous-Key Locking</strong>技术,这种技术跟Next-Key Lock正好相反,锁定的区间是区间范围和前一个值。同样上述的值,使用Previous-Key Locking技术,那么可锁定的区间为:<br><img src="/img/remote/1460000020762805?w=672&h=347" alt="" title=""></p>
<p>不是所有索引都会加上Next-key Lock的,这里有一种<strong>特殊的情况</strong>,在查询的列是唯一索引(包含主键索引)的情况下,<code>Next-key Lock</code>会降级为<code>Record Lock</code>。</p>
<p>接下来,我们来通过一个例子解释一下。</p>
<pre><code class="java">CREATE TABLE test (
x INT,
y INT,
PRIMARY KEY(x), // x是主键索引
KEY(y) // y是普通索引
);
INSERT INTO test select 3, 2;
INSERT INTO test select 5, 3;
INSERT INTO test select 7, 6;
INSERT INTO test select 10, 8;</code></pre>
<p>我们现在会话A中执行如下语句;</p>
<pre><code class="java">SELECT * FROM test WHERE y = 3 FOR UPDATE</code></pre>
<p>我们分析一下这时候的加锁情况。</p>
<ul><li>对于主键x</li></ul>
<p><img src="/img/remote/1460000020762806" alt="" title=""></p>
<ul><li>辅助索引y</li></ul>
<p><img src="/img/remote/1460000020762807?w=634&h=345" alt="" title=""></p>
<p>用户可以通过以下两种方式来显示的关闭Gap Lock:</p>
<ul>
<li>将事务的隔离级别设为 READ COMMITED。</li>
<li>将参数innodb_locks_unsafe_for_binlog设置为1。</li>
</ul>
<p><strong>Gap Lock的作用</strong>:是为了阻止多个事务将记录插入到同一个范围内,设计它的目的是用来解决<strong>Phontom Problem(幻读问题)</strong>。在MySQL默认的隔离级别(Repeatable Read)下,InnoDB就是使用它来解决幻读问题。</p>
<blockquote>
<strong>幻读</strong>:是指在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次的SQL可能会返回之前不存在的行,也就是第一次执行和第二次执行期间有其他事务往里插入了新的行。</blockquote>
<h3>6 锁带来的问题</h3>
<h4>6.1 脏读</h4>
<p><strong>脏读:</strong> 在不同的事务下,当前事务可以读到另外事务未提交的数据。另外我们需要注意的是默认的MySQL隔离级别是<code>REPEATABLE READ</code>是不会发生脏读的,脏读发生的条件是需要事务的隔离级别为<code>READ UNCOMMITTED</code>,所以如果出现脏读,可能就是这种隔离级别导致的。</p>
<p>下面我们通过一个例子看一下。<br><img src="/img/remote/1460000020762808" alt="" title=""></p>
<p>从上面这个例子可以看出,当我们的事务的隔离级别为<code>READ UNCOMMITTED</code>的时候,在会话A还没有提交时,会话B就能够查询到会话A没有提交的数据。</p>
<h4>6.2 不可重复读</h4>
<p><strong>不可重复读:</strong> 是指在一个事务内多次读取同一集合的数据,但是多次读到的数据是不一样的,这就违反了数据库事务的一致性的原则。但是,这跟脏读还是有区别的,脏读的数据是没有提交的,但是不可重复读的数据是已经提交的数据。</p>
<p>我们通过下面的例子来看一下这种问题的发生。</p>
<p><img src="/img/remote/1460000020762809" alt="" title=""></p>
<p>从上面的例子可以看出,在A的一次会话中,由于会话B插入了数据,导致两次查询的结果不一致,所以就出现了不可重复读的问题。</p>
<p>我们需要注意的是不可重复读读取的数据是已经提交的数据,事务的隔离级别为<code>READ COMMITTED</code>,这种问题我们是可以接受的。</p>
<p>如果我们需要避免不可重复读的问题的发生,那么我们可以使用<strong>Next-Key Lock算法</strong>(设置事务的隔离级别为<code>READ REPEATABLE</code>)来避免,在MySQL中,不可重复读问题就是Phantom Problem,也就是<strong>幻像问题</strong>。</p>
<h4>6.3 丢失更新</h4>
<p><strong>丢失更新:</strong>指的是一个事务的更新操作会被另外一个事务的更新操作所覆盖,从而导致数据的不一致。在当前数据库的任何隔离级别下都不会导致丢失更新问题,要出现这个问题,在多用户计算机系统环境下有可能出现这种问题。</p>
<p>如何避免丢失更新的问题呢,我们只需要让事务的操作变成串行化,不要并行执行就可以。</p>
<p>我们一般使用<code>SELECT ... FOR UPDATE</code>语句,给操作加上一个排他X锁。</p>
<h4>6.4 小结</h4>
<p>这里我们做一个小结,主要是在不同的事务的隔离级别下出现的问题的对照,这样就更加清晰了。</p>
<p><img src="/img/remote/1460000020762810" alt="" title=""></p>
<blockquote>1、<strong>原创不易</strong>,老铁,文章需要你的 <strong>点赞</strong> 让更多的人看到,希望能够帮助到大家!<p>2、文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的<strong>微信公众号</strong>:<strong><code>好好学java</code></strong>,公众号已有 <strong>6W</strong> 粉丝,回复:<strong>1024</strong>,获取公众号的大礼包,公众号长期发布 <strong>Java 优质系列文章</strong>,关注我们一定会让你收获很多!</p>
<p><img src="/img/remote/1460000021254987" alt="" title=""></p>
</blockquote>
面试官出的MySQL索引问题,这篇文章全给你解决!
https://segmentfault.com/a/1190000020621056
2019-10-09T09:23:11+08:00
2019-10-09T09:23:11+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
50
<blockquote>原文链接:blog.ouyangsihai.cn >> <a href="https://link.segmentfault.com/?enc=gDgJYPNCzQUmm4l%2FWiasbA%3D%3D.C09gMhryvTFJ7dxDS37X7pxK4%2BTs7oQ%2Fqm%2FW%2BxGgX3fDf%2F0hjhFcANIOvsuIhBxDw8GwLyMdRbD1PktnbmRNog%3D%3D" rel="nofollow">MySQL的B+树索引的概念、使用、优化及使用场景 </a>
</blockquote>
<p>在看这篇文章之前,我们回顾一下前面的几篇关于MySQL的文章,应该对你读下面的文章有所帮助。</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=PUsNmpS0gNU5gk2sLZt%2FpQ%3D%3D.lHWMu1HPW9ZM29DxCt%2BEWi429f%2BsGozm26Nbp%2BzNsWre9RY%2F0Yy7ZiyPjh4%2BAqjphFEh5dLw6aSAk0yV7YzG9%2F6z%2B%2B5GqXaGm7c7sKSflNQ%3D" rel="nofollow">InnoDB与MyISAM等存储引擎对比 </a></li>
<li><a href="https://link.segmentfault.com/?enc=rZ%2FkET9hfT0qASmksq3m0A%3D%3D.rNymzjC%2BpcFWr%2Fu4F1Obg3d%2BR6AXmP24Whm0%2FtzTpGHBjO9bjUsBwdynz%2FjEYE0QxLtaNMJqD%2FH3pDm%2BFr3F%2FmOZdC121YhCfkxw4KM6JUnfv%2BRs2trz8gGRVawz8N6ucWWuWgBulAU6DLB7uGdFYw%3D%3D" rel="nofollow">面试官问你B树和B+树,就把这篇文章丢给他 </a></li>
<li><a href="https://link.segmentfault.com/?enc=qMIUeIPY0hefFsFsbEVb7A%3D%3D.oUHDiJiHTcftQ4uesYb%2BmUWyatS%2Bk5y4fZdxV1d%2F%2FpE9JCiUo6bDYVQE7LE1%2FMapU9i%2F6db8gQtAEb%2FHAUIQXQ%3D%3D" rel="nofollow">MySQL的B+树索引的概念、使用、优化及使用场景 </a></li>
<li><a href="https://link.segmentfault.com/?enc=DbFe%2BL6%2BJJrUFqyfrUo%2B8A%3D%3D.vpofETlSb0mBvurpoTK28y%2FbjzZHOR35FeseAK3tZQ3W86SACf7z0fSMVQYzMbDuhrK05rMBC8NWvwd4iSaCUw%3D%3D" rel="nofollow">MySQL全文索引最强教程 </a></li>
<li><a href="https://link.segmentfault.com/?enc=9pz5CUbBVQBu5OFhM24Ldg%3D%3D.Fvmg8MSeeEv4f1zv74VyZh0LkRCKCIxF0yq0wEykRdLqNaqQPE%2BTrQ96WolyNf%2FY8pPYimCaIzXugEeAT2oYwQ%3D%3D" rel="nofollow">MySQL的又一神器-锁,MySQL面试必备</a></li>
</ul>
<h3>0 前言</h3>
<p>这篇文章不会讲解索引的基础知识,主要是关于MySQL数据库的B+树索引的相关原理,里面的一些知识都参考了MySQL技术内幕这本书,也算对于这些知识的总结。对于B树和B+树相关的知识,可以参考我的这篇博客:<a href="https://link.segmentfault.com/?enc=k6rv5JaNlK630qi8fQjSwA%3D%3D.FzuHI1D%2BvlBTEcZFM%2BUkFuEmzsiTHuHxHv5obKPD1x26GWwUiR0N88bBohwxIy5pzLz33q7SADV1lBPk459Jf9BAzJ%2Fm4idWjwD0MENU2Ymhw2LD8OBW3d7FgljTyjwyLkr32bBmDgcAlKHD%2FEnPqw%3D%3D" rel="nofollow">面试官问你B树和B+树,就把这篇文章丢给他</a></p>
<h3>1 索引的管理</h3>
<p>索引有很多中类型:普通索引、唯一索引、主键索引、组合索引、全文索引,下面我们看看如何创建和删除下面这些类型的索引。</p>
<h4>1.1 索引的创建方式</h4>
<p>索引的创建是可以在很多种情况下进行的。</p>
<ul><li>直接创建索引</li></ul>
<pre><code>CREATE [UNIQUE|FULLLTEXT] INDEX index_name ON table_name(column_name(length))</code></pre>
<p><code>[UNIQUE|FULLLTEXT]</code>:表示可选择的索引类型,唯一索引还是全文索引,不加话就是普通索引。<br><code>table_name</code>:表的名称,表示为哪个表添加索引。<br><code>column_name(length)</code>:column_name是表的列名,length表示为这一列的前length行记录添加索引。</p>
<ul><li>修改表结构的方式添加索引</li></ul>
<pre><code>ALTER TABLE table_name ADD [UNIQUE|FULLLTEXT] INDEX index_name (column(length))</code></pre>
<ul><li>创建表的时候同时创建索引</li></ul>
<pre><code>CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER NOT NULL ,
PRIMARY KEY (`id`),
[UNIQUE|FULLLTEXT] INDEX index_name (title(length))
)</code></pre>
<h4>1.2 主键索引和组合索引创建的方式</h4>
<p>前面讲的都是<strong>普通索引、唯一索引和全文索引</strong>创建的方式,但是,<strong>主键索引和组合索引</strong>创建的方式却是有点不一样的,所以单独拿出来讲一下。</p>
<p><strong>组合索引创建方式</strong></p>
<ul><li>创建表的时候同时创建索引</li></ul>
<pre><code>CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER NOT NULL ,
PRIMARY KEY (`id`),
INDEX index_name(id,title)
)</code></pre>
<ul><li>修改表结构的方式添加索引</li></ul>
<pre><code>ALTER TABLE table_name ADD INDEX name_city_age (name,city,age); </code></pre>
<p><strong>主键索引创建方式</strong><br>主键索引是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引。</p>
<pre><code>CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER NOT NULL ,
PRIMARY KEY (`id`)
)</code></pre>
<h4>1.3 删除索引</h4>
<p>删除索引可利用<code>ALTER TABLE</code>或<code>DROP INDEX</code>语句来删除索引。类似于<code>CREATE INDEX</code>语句,<code>DROP INDEX</code>可以在<code>ALTER TABLE</code>内部作为一条语句处理,语法如下。</p>
<p>(1)<code>DROP INDEX index_name ON talbe_name</code><br>(2)<code>ALTER TABLE table_name DROP INDEX index_name</code><br>(3)<code>ALTER TABLE table_name DROP PRIMARY KEY</code></p>
<p>第3条语句只在删除<code>PRIMARY KEY</code>索引时使用,因为一个表只可能有一个<code>PRIMARY KEY</code>索引,因此不需要指定索引名。</p>
<h4>1.4 索引实例</h4>
<p>上面讲了一下基本的知识,接下来,还是通过一个具体的例子来体会一下。</p>
<ul><li><strong>step1:创建表</strong></li></ul>
<pre><code> create table table_index(
id int(11) not null auto_increment,
title char(255) not null,
primary key(id)
);</code></pre>
<ul><li><strong>step2:添加索引</strong></li></ul>
<p>首先,我们使用直接添加索引的方式添加一个普通索引。</p>
<pre><code>CREATE INDEX idx_a ON table_index(title);</code></pre>
<p>接着,我们用修改表结构的时候添加索引。</p>
<pre><code>ALTER TABLE table_index ADD UNIQUE INDEX idx_b (title(100));</code></pre>
<p>最后,我们再添加一个组合索引。</p>
<pre><code> ALTER TABLE table_index ADD INDEX idx_id_title (id,title);</code></pre>
<p>这样,我们就把前面索引的方式都用上一遍了,我相信你也熟悉这些操作了。</p>
<ul><li><strong>step3:使用<code>SHOW INDEX</code>命令查看索引信息</strong></li></ul>
<p>如果想要查看表中的索引信息,可以使用命令<code>SHOW INDEX</code>,下面的例子,我们查看表<code>table_index</code>的索引信息。</p>
<pre><code> SHOW INDEX FROM table_index\G;</code></pre>
<p><img src="/img/remote/1460000020621062" alt="" title=""></p>
<p>得到上面的信息,上面的信息什么意思呢?我们逐一介绍!</p>
<table>
<thead><tr>
<th>字段</th>
<th>解释</th>
</tr></thead>
<tbody>
<tr>
<td>Table</td>
<td>索引所在的表</td>
</tr>
<tr>
<td>Non_unique</td>
<td>非唯一索引,如果是0,代表唯一的,也就是说如果该列索引中不包括重复的值则为0 否则为1</td>
</tr>
<tr>
<td>Key_name</td>
<td>索引的名字,如果是主键的话 则为PRIMARY</td>
</tr>
<tr>
<td>Seq_in_index</td>
<td>索引中该列的位置,从1开始,如果是组合索引 那么按照字段在建立索引时的顺序排列</td>
</tr>
<tr>
<td>Collation</td>
<td>列是以什么方式存储在索引中的。可以是A或者NULL,B+树索引总是A,排序的,</td>
</tr>
<tr>
<td>Sub_part</td>
<td>是否列的部分被索引,如果只是前100行索引,就显示100,如果是整列,就显示NULL</td>
</tr>
<tr>
<td>Packed</td>
<td>关键字是否被压缩,如果没有,为NULL</td>
</tr>
<tr>
<td>Index_type</td>
<td>索引的类型,对于InnoDB只支持B+树索引,所以都是显示BTREE</td>
</tr>
</tbody>
</table>
<ul><li><strong>step4:删除索引</strong></li></ul>
<p>直接删除索引方式</p>
<pre><code>DROP INDEX idx_a ON table_index;</code></pre>
<p>修改表结构时删除索引</p>
<pre><code>ALTER TABLE table_index DROP INDEX idx_b;</code></pre>
<h4>1.5 Cardinality关键字解析</h4>
<p>在上面介绍了那么多个关键字的意思,但是<code>Cardinality</code>这个关键字非常的关键,优化器会根据这个值来判断是否使用这个索引。在B+树索引中,只有高选择性的字段才是有意义的,<strong>高选择性</strong>就是这个字段的取值范围很广,比如姓名字段,会有很多的名字,可选择性就高了。</p>
<p>一般来说,判断是否需要使用索引,就可以通过<code>Cardinality</code>关键字来判断,如果非常接近1,说明有必要使用,如果非常小,那么就要考虑是否使用索引了。</p>
<p>需要注意的一个问题时,这个关键字不是及时更新的,需要更新的话,需要使用<code>ANALYZE TABLE</code>,例如。</p>
<pre><code>analyze table table_index;</code></pre>
<p><img src="/img/remote/1460000020621063" alt="" title=""></p>
<p>因为目前没有数据,所以,你会发现,这个值一直都是0,没有变化。</p>
<p><img src="/img/remote/1460000020621064" alt="" title=""></p>
<h5>InoDB存储引擎Cardinality的策略</h5>
<p>在InnoDB存储引擎中,这个关键字的更新发生在两个操作中:insert和update。但是,并不是每次都会更新,这样会增加负荷,所以,对于这个关键字的更新有它的策略:</p>
<ul>
<li>表中<code>1/16</code>的数据发生变化</li>
<li>InnoDB存储引擎的计数器<code>stat_modified_conter</code>>2000000000</li>
</ul>
<p>默认InnoDB存储引擎会对8个叶子节点进行采样,采样过程如下:</p>
<ul>
<li>B+树索引中叶子节点数量,记做<code>A</code>
</li>
<li>
<strong>随机</strong>取得B+树索引中的<code>8</code>个叶子节点。统计每个页不同的记录个数,分别为p1-p8</li>
<li>根据采样信息得到Cardinality的预估值:<code>(p1+p2+p3+...+p8)*A/8</code>
</li>
</ul>
<p>因为随机采样,所以,每次的Cardinality值都是不一样的,只有一种情况会一样的,就是表中的叶子节点<strong>小于或者等于8</strong>,这时候,怎么随机采样都是这8个,所以也就一样的。</p>
<h4>1.6 Fast Index Creation</h4>
<p>在MySQL 5.5之前,<strong>对于索引的添加或者删除,每次都需要创建一张临时表,然后导入数据到临时表,接着删除原表</strong>,如果一张大表进行这样的操作,会非常的耗时,这是一个很大的缺陷。</p>
<p>InnoDB存储引擎从1.0.x版本开始加入了一种Fast Index Creation(快速索引创建)的索引创建方式。</p>
<p>这种方式的策略为:<strong>每次为创建索引的表加上一个S锁(共享锁),在创建的时候,不需要重新建表,删除辅助索引只需要更新内部视图,并将辅助索引空间标记为可用</strong>,所以,这种效率就大大提高了。</p>
<h4>1.7 在线数据定义</h4>
<p>MySQL5.6开始支持的在线数据定义操作就是:允许辅助索引创建的同时,还允许其他insert、update、delete这类DM操作,这就极大提高了数据库的可用性。</p>
<p>所以,我们可以使用新的语法进行创建索引:</p>
<pre><code>ALTER TABLE table_name ADD [UNIQUE|FULLLTEXT] INDEX index_name (column(length))
[ALGORITHM = {DEFAULT|INPLACE|COPY}]
[LOCK = {DEFAULT|NONE|SHARED|EXLUSIVE}]</code></pre>
<p><code>ALGORITHM</code>指定创建或者删除索引的算法</p>
<ul>
<li>COPY:创建临时表的方式</li>
<li>INPLACE:不需要创建临时表</li>
<li>DEFAULT:根据参数<code>old_alter_table</code>参数判断,如果是<code>OFF</code>,采用<code>INPLACE</code>的方式</li>
</ul>
<p>LOCK表示对表添加锁的情况</p>
<ul>
<li>NONE:不加任何锁</li>
<li>SHARE:加一个S锁,并发读可以进行,写操作需要等待</li>
<li>EXCLUSIVE:加一个X锁,读写都不能并发进行</li>
<li>DEFAULT:先判断是否可以使用<code>NONE</code>,如不能,判断是否可以使用<code>SHARE</code>,如不能,再判断是否可以使用<code>EXCLUSIVE</code>模式。</li>
</ul>
<h3>2 B+ 树索引的使用</h3>
<h4>2.1 联合索引</h4>
<p>联合索引是指对表上的多个列进行索引,这一部分我们将通过几个例子来讲解联合索引的相关知识点。</p>
<p>首先,我们先创建一张表以及为这张表创建联合索引。</p>
<pre><code>create table t_index(
a char(2) not null default '',
b char(2) not null default '',
c char(2) not null default '',
d char(2) not null default ''
)engine myisam charset utf8;</code></pre>
<p>创建联合索引</p>
<pre><code>alter table t_index add index abcd(a,b,c,d);</code></pre>
<p>插入几条测试数据</p>
<pre><code>insert into t_index values('a','b','c','d'),
('a2','b2','c2','d2'),
('a3','b3','c3','d3'),
('a4','b4','c4','d4'),
('a5','b5','c5','d5'),
('a6','b6','c6','d6');</code></pre>
<p>到这一步,我们已经基本准备好了需要的数据,我们可以进行更深一步的联合索引的探讨。</p>
<h5>我们什么时候需要创建联合索引呢</h5>
<p>索引建立的主要目的就是为了提高查询的效率,那么联合索引的目的也是类似的,联合索引的目的就是为了提高存在多个查询条件的情况下的效率,就如上面建立的表一样,有多个字段,当我们需要利用多个字段进行查询的时候,我们就需要利用到联合索引了。</p>
<h5>什么时候联合索引才会发挥作用呢</h5>
<p>有时候,我们会用联合索引,但是,我们并不清楚其原理,不知道什么时候联合索引会起到作用,什么时候又是会失效的?</p>
<p>带着这个问题,我们了解一下联合索引的<strong>最左匹配原则</strong>。</p>
<p><strong>最左匹配原则</strong>:这个原则的意思就是<strong>创建组合索引,以最左边的为准,只要查询条件中带有最左边的列,那么查询就会使用到索引。</strong></p>
<p>下面,我们用几个例子来看看这个原则。</p>
<pre><code>EXPLAIN SELECT * FROM t_index WHERE a = 'a' \G;</code></pre>
<p><img src="/img/remote/1460000020621065" alt="" title=""></p>
<p>我们看看这条语句的结果,首先,我们看到使用了索引,因为<strong>查询条件中带有最左边的列a</strong>,那么利用了几个索引呢?这个我们需要看<code>key_len</code>这个字段,我们知道utf8编码的一个字符3个字节,而我们使用的数据类型是<code>char(2)</code>,占两个字节,索引就是2*3等于6个字节,所以只有一个索引起到了作用。</p>
<pre><code>EXPLAIN SELECT * FROM t_index WHERE b = 'b2' \G;</code></pre>
<p><img src="/img/remote/1460000020621066" alt="" title=""></p>
<p>这个语句我们可以看出,这个没有使用索引,因为<code>possible_keys</code>为空,而且,从查询的行数<code>rows</code>可以看出为6(我们测试数据总共6条),说明进行了全盘扫描的,说明这种情况是不符合<strong>最左匹配原则</strong>,所以不会使用索引查询。</p>
<pre><code>EXPLAIN SELECT * FROM t_index WHERE a = 'a2' AND b = 'b2' ORDER BY d \G;</code></pre>
<p><img src="/img/remote/1460000020621067" alt="" title=""></p>
<p>这种情况又有点不一样了,我们使用了一个排序,可以看出使用了索引,通过<code>key_len</code>为12可以得到使用了2个索引<code>a、b</code>,另外在Extra选项中可以看到使用了<code>Using filesort</code>,也就是文件排序,这里使用文件排序的原因是这样的:上面的查询使用了a、b索引,但是当我们用d字段来排序时,(a,d)或者(b,d)这两个索引是没有排序的,<strong>联合索引的使用有一个好处,就是索引的下一个字段是会自动排序的</strong>,在这里的这种情况来说,c字段就是排序的,但是d是不会,如果我们用c来排序就会得到不一样的结果。</p>
<pre><code>EXPLAIN SELECT * FROM t_index WHERE a = 'a2' AND b = 'b2' ORDER BY c \G;</code></pre>
<p><img src="/img/remote/1460000020621068" alt="" title=""></p>
<p>是不是可以看到,当我们用c进行排序的时候,因为使用了a、b索引,所以c就自动排序了,所以也就不用filesort了。</p>
<p>讲到这里,我相信通过上面的几个例子,对于联合索引的相关知识已经非常的透彻清晰了,最后,我们再来聊几个常见的问题。</p>
<h5>Q1:为什么不对表中的每一个列创建一个索引呢</h5>
<p>第一,创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。<br>第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。<br>第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。</p>
<h5>Q2:为什么需要使用联合索引</h5>
<p><strong>减少开销</strong>。建一个联合索引(col1,col2,col3),实际相当于建了(col1),(col1,col2),(col1,col2,col3)三个索引。每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,使用联合索引会大大的减少开销!</p>
<p><strong>覆盖索引</strong>。对联合索引(col1,col2,col3),如果有如下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那么MySQL可以直接通过遍历索引取得数据,而无需回表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一。</p>
<p><strong>效率高</strong>。索引列越多,通过索引筛选出的数据越少。有1000W条数据的表,有如下sql:select from table where col1=1 and col2=2 and col3=3,假设假设每个条件可以筛选出10%的数据,如果只有单值索引,那么通过该索引能筛选出1000W10%=100w条数据,然后再回表从100w条数据中找到符合col2=2 and col3= 3的数据,然后再排序,再分页;如果是联合索引,通过索引筛选出1000w10% 10% *10%=1w,效率提升可想而知!</p>
<blockquote>
<strong>覆盖索引</strong><br>覆盖索引是一种从辅助索引中就可以得到查询的记录,而不需要查询聚集索引中的记录,使用覆盖索引的一个好处是辅助索引不包含整行记录的所有信息,所以大小远小于聚集索引,因此可以大大减少IO操作。覆盖索引的另外一个好处就是对于统计问题有优化,我们看下面的一个例子。</blockquote>
<pre><code>explain select count(*) from t_index \G;</code></pre>
<p><img src="/img/remote/1460000020621069" alt="" title=""></p>
<p>如果是myisam引擎,Extra列会输出<code>Select tables optimized away</code>语句,myisam引擎已经保存了记录的总数,直接返回结果,就不需要覆盖索引优化了。</p>
<p>如果是InnoDB引擎,Extra列会输出<code>Using index</code>语句,说明InnoDB引擎优化器使用了覆盖索引操作。</p>
<h4>2.2 索引提示</h4>
<p>MySQL数据库支持索引提示功能,索引提示功能就是我们可以显示的告诉优化器使用哪个索引,一般有下面两种情况可能使用到索引提示功能(INDEX HINT):</p>
<ul>
<li>MySQL数据库的优化器错误的选择了某个索引,导致SQL运行很慢</li>
<li>某SQL语句可以选择的索引非常的多,这时优化器选择执行计划时间的开销可能会大于SQL语句本身。</li>
</ul>
<p>这里我们接着上面的例子来讲解,首先,我们先为上面的<code>t_index</code>表添加几个索引;</p>
<pre><code>alter table t_index add index a (a);
alter table t_index add index b (b);
alter table t_index add index c (c);</code></pre>
<p>接着,我们执行下面的语句;</p>
<pre><code>EXPLAIN SELECT * FROM t_index WHERE a = 'a' AND b = 'b' AND c = 'c' \G;</code></pre>
<p><img src="/img/remote/1460000020621070" alt="" title=""></p>
<p>你会发现这条语句就可以使用三个索引,这个时候,我们可以显示的使用索引提示来使用a这个索引,如下:</p>
<pre><code>EXPLAIN SELECT * FROM t_index USE INDEX(a) WHERE a = 'a' AND b = 'b' AND c = 'c' \G;</code></pre>
<p><img src="/img/remote/1460000020621071" alt="" title=""></p>
<p>这样就显示的使用索引a了,如果这种方式有时候优化器还是没有选择你想要的索引,那么,我们可以另外一种方式<code>FORCE INDEX</code>。</p>
<pre><code>EXPLAIN SELECT * FROM t_index FORCE INDEX(a) WHERE a = 'a' AND b = 'b' AND c = 'c' \G;</code></pre>
<p><img src="/img/remote/1460000020621072" alt="" title=""></p>
<p>这种方式则一定会选择你想要的索引。</p>
<h4>2.3 索引优化</h4>
<h5>Multi-Range Read 优化</h5>
<p>MySQL5.6开始支持,这种优化的目的是为了减少磁盘的随机访问,并且将随机访问转化为较为顺序的数据访问,这种优化适用于range、ref、eq_ref类型的查询。</p>
<p>Multi-Range Read 优化的好处:</p>
<ul>
<li>让数据访问变得较为顺序。</li>
<li>减少缓冲区中页被替换的次数。</li>
<li>批量处理对键值的查询操作。</li>
</ul>
<p>我们可以使用参数<code>optimizer_switch</code>中的标记来控制是否开启Multi-Range Read 优化。下面的方式将设置为总是开启状态:</p>
<pre><code>SET @@optimizer_switch='mrr=on,mrr_cost_based=off';</code></pre>
<h5>Index Condition Pushdown(ICP) 优化</h5>
<p>这种优化方式也是从MySQL5.6开始支持的,不支持这种方式之前,当进行索引查询时,首先我们先根据索引查找记录,然后再根据where条件来过滤记录。然而,当支持ICP优化后,MySQL数据库会在取出索引的同时,判断是否可以进行where条件过滤,也就是将where过滤部分放在了存储引擎层,大大减少了上层SQL对记录的索取。</p>
<p>ICP支持range、ref、eq_ref、ref_or_null类型的查询,当前支持MyISAM和InnoDB存储引擎。</p>
<p>我们可以使用下面语句开启ICP:</p>
<pre><code>set @@optimizer_switch = "index_condition_pushdown=on"</code></pre>
<p>或者关闭:</p>
<pre><code>set @@optimizer_switch = "index_condition_pushdown=off"</code></pre>
<p>当开启了ICP之后,在执行计划Extra可以看到<code>Using index condition</code>提示。</p>
<h3>3 索引的特点、优点、缺点及适用场景</h3>
<h4>索引的特点</h4>
<ul>
<li>可以加快数据库的检索速度</li>
<li>降低数据库插入、修改、删除等维护的速度</li>
<li>只能创建在表上,不能创建在视图上</li>
<li>既可以直接创建也可以间接创建</li>
</ul>
<h4>索引的优点</h4>
<ul>
<li>创建唯一性索引,保证数据库表中的每一行数据的唯一性</li>
<li>大大加快数据的检索速度</li>
<li>加快数据库表之间的连接,特别是在实现数据的参考完整性方面特别有意义</li>
<li>在使用分组和排序字句进行数据检索时,同样可以显著减少查询的时间</li>
<li>通过使用索引,可以在查询中使用优化隐藏器,提高系统性能</li>
</ul>
<h4>索引的缺点</h4>
<ul>
<li>第一,创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。</li>
<li>第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。</li>
<li>第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。</li>
</ul>
<h4>索引的适用场景</h4>
<ul><li>匹配全值</li></ul>
<p>对索引中所有列都指定具体值,即是对索引中的所有列都有等值匹配的条件。</p>
<ul><li>匹配值的范围查询</li></ul>
<p>对索引的值能够进行范围查找。</p>
<ul><li>匹配最左前缀</li></ul>
<p>仅仅使用索引中的最左边列进行查询,比如在 col1 + col2 + col3 字段上的联合索引能够被包含 col1、(col1 + col2)、(col1 + col2 + col3)的等值查询利用到,可是不能够被 col2、(col2、col3)的等值查询利用到。<br>最左匹配原则可以算是 MySQL 中 B-Tree 索引使用的首要原则。</p>
<ul><li>仅仅对索引进行查询</li></ul>
<p>当查询的列都在索引的字段中时,查询的效率更高,所以应该尽量避免使用 select *,需要哪些字段,就只查哪些字段。</p>
<ul><li>匹配列前缀</li></ul>
<p>仅仅使用索引中的第一列,并且只包含索引第一列的开头一部分进行查找。</p>
<ul>
<li>能够实现索引匹配部分精确而其他部分进行范围匹配</li>
<li>如果列名是索引,那么使用 column_name is null 就会使用索引,例如下面的就会使用索引:</li>
</ul>
<pre><code>explain select * from t_index where a is null \G</code></pre>
<ul>
<li>经常出现在关键字order by、group by、distinct后面的字段</li>
<li>在union等集合操作的结果集字段</li>
<li>经常用作表连接的字段</li>
<li>考虑使用索引覆盖,对数据很少被更新,如果用户经常值查询其中你的几个字段,可以考虑在这几个字段上建立索引,从而将表的扫描变为索引的扫描</li>
</ul>
<h4>索引失效情况</h4>
<ul>
<li>以%开头的 like 查询不能利用 B-Tree 索引,执行计划中 key 的值为 null 表示没有使用索引</li>
<li>数据类型出现隐式转换的时候也不会使用索引,例如,<code>where 'age'+10=30</code>
</li>
<li>对索引列进行函数运算,原因同上</li>
<li>正则表达式不会使用索引</li>
<li>字符串和数据比较不会使用索引</li>
<li>复合索引的情况下,假如查询条件不包含索引列最左边部分,即不满足最左原则 leftmost,是不会使用复合索引的</li>
<li>如果 MySQL 估计使用索引比全表扫描更慢,则不使用索引</li>
<li>用 or 分割开的条件,如果 or 前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到</li>
<li>使用负向查询(not ,not in, not like ,<> ,!= ,!> ,!< ) 不会使用索引</li>
</ul>
<h3>参考文章</h3>
<ul>
<li><a href="https://link.segmentfault.com/?enc=xPt28gJ3SLhMNDs%2FlMdu%2FA%3D%3D.k%2BuQi%2B%2FCwb6TWgd6O73dN2qRulMHmJt8PpW17xoK3cV%2B8971M4cYuprAINt%2FbX8VEWfrlMOxvV8rIdoXdu48Nw%3D%3D" rel="nofollow">http://www.yuanrengu.com/inde...</a></li>
<li><a href="https://segmentfault.com/a/1190000009717352">https://segmentfault.com/a/11...</a></li>
<li><a href="https://link.segmentfault.com/?enc=40BXKXTp0o27Fm2fULwcaA%3D%3D.PMaG7mTQAOGUbv1tSwya3rwZ2WU%2BdWyY6WLEoJFmHaiJwK2Cgw4jQqAfKWHAfv8g" rel="nofollow">https://www.jb51.net/article/...</a></li>
<li><a href="https://link.segmentfault.com/?enc=F3P45nCTBl4JCwnjKxEmdA%3D%3D.gdzyXkaqj5rvNBrUY19ilXYSPhexSF4H6brQ%2Fec9gX657BkfsEsdDlAJhwYZO4Lh" rel="nofollow">https://www.cnblogs.com/xiaox...</a></li>
<li>《MySQL技术内幕》</li>
</ul>
<blockquote>1、<strong>原创不易</strong>,老铁,文章需要你的 <strong>点赞</strong> 让更多的人看到,希望能够帮助到大家!<p>2、文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的<strong>微信公众号</strong>:<strong><code>好好学java</code></strong>,公众号已有 <strong>6W</strong> 粉丝,回复:<strong>1024</strong>,获取公众号的大礼包,公众号长期发布 <strong>Java 优质系列文章</strong>,关注我们一定会让你收获很多!</p>
<p><img src="/img/remote/1460000021254987" alt="" title=""></p>
</blockquote>
面试官问你B树和B+树,就把这篇文章丢给他
https://segmentfault.com/a/1190000020416577
2019-09-18T10:36:50+08:00
2019-09-18T10:36:50+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
247
<blockquote>原文链接:<a href="https://link.segmentfault.com/?enc=80vsfO%2Fs34pzmhrl7uFjsg%3D%3D.KrALy2vpLW8a0PXyKmmtYuoT7Qex6TZttkwjA32ii9d%2BI7dImz0rfa1XM4Y7GsK%2FWZgbwSadE4m8RDJwq9m1dq28NFL7VNIyiJDab%2Fvr%2Fg5MjF%2FoF42woMmIkFPf%2F7aywsBxBa%2FWzneC2k9YV52zmw%3D%3D" rel="nofollow">面试官问你B树和B+树,就把这篇文章丢给他</a>
</blockquote>
<p>在看这篇文章之前,我们回顾一下前面的几篇关于MySQL的文章,应该对你读下面的文章有所帮助。</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=6lRIi3UM3c3kPMiiRNZBpA%3D%3D.gCetIbey20x8AhT051d0u2npc2yYQSxAsqkgwPUXBmHRaQub3Cu%2BaG5%2B6qguI1Q13xXUxMuV91mKSXov5FRVm0hLYkLtVZUenUFlJjoRTn4%3D" rel="nofollow">InnoDB与MyISAM等存储引擎对比 </a></li>
<li><a href="https://link.segmentfault.com/?enc=5fn0oF42UIAHoUqjRko40w%3D%3D.i5Cs81nYgL%2F34tIyBBJ9lHi709GC0IpGsWslCdRB%2B0CEPlbaUG18%2FN0Y1kk2Tta%2BYy7T5PMSs%2BRw0o6DXfdqfNbZaHq9%2BcFjrSrNHsMomPjvVBMxMJRgRq5TYxmywUw124U8uXnDLEceEk8xhDk1GQ%3D%3D" rel="nofollow">面试官问你B树和B+树,就把这篇文章丢给他 </a></li>
<li><a href="https://link.segmentfault.com/?enc=R6WZizlmCOIXJOaQpdvR7A%3D%3D.QCX787PYAgDMhzBqxJEeZNi8tvPh8BrGJbBP0dOpFedX1xVZX6RyNphDKTxCXXAn71d63mC9STKdrxsG7VWU%2Bw%3D%3D" rel="nofollow">MySQL的B+树索引的概念、使用、优化及使用场景 </a></li>
<li><a href="https://link.segmentfault.com/?enc=HtMvWXCJAKFwOjBxuhjtAA%3D%3D.NumC%2FQ2u5XbitassxAKfWiwQsBCSdYmn1EYq5CdsMc7vDvSYzpWdoG8X91megBzHcWw%2FxhqwfMT42Pt5VOJ2kQ%3D%3D" rel="nofollow">MySQL全文索引最强教程 </a></li>
<li><a href="https://link.segmentfault.com/?enc=Y6Nsm%2F%2BpbSogzxyHPASO5w%3D%3D.09f74SiSTayLlBvbh0ML5zS4%2BDovVgV4B6VnS8ViPB60V0A%2ByCCDi%2FpLFWuQ2cePPR%2FrJHbAYh0iKVF99q5Neg%3D%3D" rel="nofollow">MySQL的又一神器-锁,MySQL面试必备</a></li>
</ul>
<h3>1 B树</h3>
<p>在介绍B+树之前, 先简单的介绍一下B树,这两种数据结构既有相似之处,也有他们的区别,最后,我们也会对比一下这两种数据结构的区别。</p>
<h4>1.1 B树概念</h4>
<p>B树也称B-树,它是一颗多路平衡查找树。二叉树我想大家都不陌生,其实,B树和后面讲到的B+树也是从最简单的二叉树变换而来的,并没有什么神秘的地方,下面我们来看看B树的定义。</p>
<ul>
<li>每个节点最多有m-1个<strong>关键字</strong>(可以存有的键值对)。</li>
<li>根节点最少可以只有1个<strong>关键字</strong>。</li>
<li>非根节点至少有m/2个<strong>关键字</strong>。</li>
<li>每个节点中的关键字都按照从小到大的顺序排列,每个关键字的左子树中的所有关键字都小于它,而右子树中的所有关键字都大于它。</li>
<li>所有叶子节点都位于同一层,或者说根节点到每个叶子节点的长度都相同。</li>
<li>每个节点都存有索引和数据,也就是对应的key和value。</li>
</ul>
<p>所以,根节点的<strong>关键字</strong>数量范围:<code>1 <= k <= m-1</code>,非根节点的<strong>关键字</strong>数量范围:<code>m/2 <= k <= m-1</code>。</p>
<p>另外,我们需要注意一个概念,描述一颗B树时需要指定它的阶数,阶数表示了一个节点最多有多少个孩子节点,一般用字母m表示阶数。</p>
<p>我们再举个例子来说明一下上面的概念,比如这里有一个5阶的B树,根节点数量范围:1 <= k <= 4,非根节点数量范围:2 <= k <= 4。</p>
<p>下面,我们通过一个插入的例子,讲解一下B树的插入过程,接着,再讲解一下删除关键字的过程。</p>
<h4>1.2 B树插入</h4>
<p>插入的时候,我们需要记住一个规则:<strong>判断当前结点key的个数是否小于等于m-1,如果满足,直接插入即可,如果不满足,将节点的中间的key将这个节点分为左右两部分,中间的节点放到父节点中即可。</strong></p>
<p>例子:在5阶B树中,结点最多有4个key,最少有2个key(注意:下面的节点统一用一个节点表示key和value)。</p>
<ul><li>插入18,70,50,40</li></ul>
<p><img src="/img/remote/1460000020416580" alt="" title=""></p>
<ul><li>插入22</li></ul>
<p><img src="/img/remote/1460000020416581" alt="" title=""></p>
<p>插入22时,发现这个节点的关键字已经大于4了,所以需要进行分裂,分裂的规则在上面已经讲了,分裂之后,如下。</p>
<p><img src="/img/remote/1460000020416582" alt="" title=""></p>
<ul><li>接着插入23,25,39</li></ul>
<p><img src="/img/remote/1460000020416584" alt="" title=""></p>
<p>分裂,得到下面的。</p>
<p><img src="/img/remote/1460000020416585" alt="" title=""></p>
<p>更过的插入的过程就不多介绍了,相信有这个例子你已经知道怎么进行插入操作了。</p>
<h4>1.3 B树的删除操作</h4>
<p>B树的删除操作相对于插入操作是相对复杂一些的,但是,你知道记住几种情况,一样可以很轻松的掌握的。</p>
<ul><li>现在有一个初始状态是下面这样的B树,然后进行删除操作。</li></ul>
<p><img src="/img/remote/1460000020416586" alt="" title=""></p>
<ul><li>删除15,这种情况是删除叶子节点的元素,如果删除之后,节点数还是大于<code>m/2</code>,这种情况只要直接删除即可。</li></ul>
<p><img src="/img/remote/1460000020416587" alt="" title=""></p>
<p><img src="/img/remote/1460000020416588" alt="" title=""></p>
<ul><li>接着,我们把22删除,这种情况的规则:22是非叶子节点,<strong>对于非叶子节点的删除,我们需要用后继key(元素)覆盖要删除的key,然后在后继key所在的子支中删除该后继key</strong>。对于删除22,需要将后继元素24移到被删除的22所在的节点。</li></ul>
<p><img src="/img/remote/1460000020416589" alt="" title=""></p>
<p><img src="/img/remote/1460000020416590" alt="" title=""></p>
<p>此时发现26所在的节点只有一个元素,小于2个(m/2),这个节点不符合要求,这时候的规则(向兄弟节点借元素):<strong>如果删除叶子节点,如果删除元素后元素个数少于(m/2),并且它的兄弟节点的元素大于(m/2),也就是说兄弟节点的元素比最少值m/2还多,将先将父节点的元素移到该节点,然后将兄弟节点的元素再移动到父节点</strong>。这样就满足要求了。</p>
<p>我们看看操作过程就更加明白了。</p>
<p><img src="/img/remote/1460000020416591" alt="" title=""></p>
<p><img src="/img/remote/1460000020416592" alt="" title=""></p>
<ul><li>接着删除28,<strong>删除叶子节点</strong>,删除后不满足要求,所以,我们需要考虑向兄弟节点借元素,但是,兄弟节点也没有多的节点(2个),借不了,怎么办呢?如果遇到这种情况,<strong>首先,还是将先将父节点的元素移到该节点,然后,将当前节点及它的兄弟节点中的key合并,形成一个新的节点</strong>。</li></ul>
<p><img src="/img/remote/1460000020416593" alt="" title=""></p>
<p>移动之后,跟兄弟节点合并。</p>
<p><img src="/img/remote/1460000020416594" alt="" title=""></p>
<p>删除就只有上面的几种情况,根据不同的情况进行删除即可。</p>
<p>上面的这些介绍,相信对于B树已经有一定的了解了,接下来的一部分,我们接着讲解B+树,我相信加上B+树的对比,就更加清晰明了了。</p>
<h3>2 B+树</h3>
<h4>2.1 B+树概述</h4>
<p>B+树其实和B树是非常相似的,我们首先看看<strong>相同点</strong>。</p>
<ul>
<li>根节点至少一个元素</li>
<li>非根节点元素范围:m/2 <= k <= m-1</li>
</ul>
<p><strong>不同点</strong>。</p>
<ul>
<li>B+树有两种类型的节点:内部结点(也称索引结点)和叶子结点。内部节点就是非叶子节点,内部节点不存储数据,只存储索引,数据都存储在叶子节点。</li>
<li>内部结点中的key都按照从小到大的顺序排列,对于内部结点中的一个key,左树中的所有key都小于它,右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。</li>
<li>每个叶子结点都存有相邻叶子结点的指针,叶子结点本身依关键字的大小自小而大顺序链接。</li>
<li>父节点存有右孩子的第一个元素的索引。</li>
</ul>
<p>下面我们看一个B+树的例子,感受感受它吧!</p>
<p><img src="/img/remote/1460000020416595" alt="" title=""></p>
<h4>2.2 插入操作</h4>
<p>对于插入操作很简单,只需要记住一个技巧即可:<strong>当节点元素数量大于m-1的时候,按中间元素分裂成左右两部分,中间元素分裂到父节点当做索引存储,但是,本身中间元素还是分裂右边这一部分的</strong>。</p>
<p>下面以一颗5阶B+树的插入过程为例,5阶B+树的节点最少2个元素,最多4个元素。</p>
<ul><li>插入5,10,15,20</li></ul>
<p><img src="/img/remote/1460000020416596" alt="" title=""></p>
<ul><li>插入25,此时元素数量大于4个了,分裂</li></ul>
<p><img src="/img/remote/1460000020416597" alt="" title=""></p>
<ul><li>接着插入26,30,继续分裂</li></ul>
<p><img src="/img/remote/1460000020416598" alt="" title=""></p>
<p><img src="/img/remote/1460000020416599" alt="" title=""></p>
<p>有了这几个例子,相信插入操作没什么问题了,下面接着看看删除操作。</p>
<h4>2.3 删除操作</h4>
<p>对于删除操作是比B树简单一些的,因为<strong>叶子节点有指针的存在,向兄弟节点借元素时,不需要通过父节点了,而是可以直接通过兄弟节移动即可(前提是兄弟节点的元素大于m/2),然后更新父节点的索引;如果兄弟节点的元素不大于m/2(兄弟节点也没有多余的元素),则将当前节点和兄弟节点合并,并且删除父节点中的key</strong>,下面我们看看具体的实例。</p>
<ul><li>初始状态</li></ul>
<p><img src="/img/remote/1460000020416600" alt="" title=""></p>
<ul><li>删除10,删除后,不满足要求,发现左边兄弟节点有多余的元素,所以去借元素,最后,修改父节点索引</li></ul>
<p><img src="/img/remote/1460000020416601" alt="" title=""></p>
<ul><li>删除元素5,发现不满足要求,并且发现左右兄弟节点都没有多余的元素,所以,可以选择和兄弟节点合并,最后修改父节点索引</li></ul>
<p><img src="/img/remote/1460000020416602" alt="" title=""></p>
<ul><li>发现父节点索引也不满足条件,所以,需要做跟上面一步一样的操作</li></ul>
<p><img src="/img/remote/1460000020416603" alt="" title=""></p>
<p>这样,B+树的删除操作也就完成了,是不是看完之后,觉得非常简单!</p>
<h3>3 B树和B+树总结</h3>
<p>B+树相对于B树有一些自己的优势,可以归结为下面几点。</p>
<ul>
<li>单一节点存储的元素更多,使得查询的IO次数更少,所以也就使得它更适合做为数据库MySQL的底层数据结构了。</li>
<li>所有的查询都要查找到叶子节点,查询性能是稳定的,而B树,每个节点都可以查找到数据,所以不稳定。</li>
<li>所有的叶子节点形成了一个有序链表,更加便于查找。</li>
</ul>
<blockquote>1、<strong>原创不易</strong>,老铁,文章需要你的 <strong>点赞</strong> 让更多的人看到,希望能够帮助到大家!<p>2、文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的<strong>微信公众号</strong>:<strong><code>好好学java</code></strong>,公众号已有 <strong>6W</strong> 粉丝,回复:<strong>1024</strong>,获取公众号的大礼包,公众号长期发布 <strong>Java 优质系列文章</strong>,关注我们一定会让你收获很多!</p>
<p><img src="/img/remote/1460000021254987" alt="" title=""></p>
</blockquote>
Java后台开发Tomcat添加https支持小程序开发过程
https://segmentfault.com/a/1190000020383037
2019-09-14T18:23:20+08:00
2019-09-14T18:23:20+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
3
<blockquote>文章原文:<a href="#">blog.ouyangsihai.cn >> Java后台开发Tomcat添加https支持小程序开发过程</a><br>)</blockquote>
<h3>1 给自己的域名申请证书</h3>
<p><img src="/img/remote/1460000020383040" alt="" title=""></p>
<p><strong>注意</strong>:申请好了如果不是在腾讯注册的域名,不会自动通过,需要手动验证的,验证方法如下:<a href="https://link.segmentfault.com/?enc=xigi07HD5rfJS9TIV9vxQw%3D%3D.dfnBXCQ5PB7CuqdsjNikM9s2lSnllyPSXY2HGa0E5qKdRZzvq%2FuiElYjSMCmYPCN9qTwZjVV%2Bgn0%2Bts%2F7vpoVg%3D%3D" rel="nofollow">https://cloud.tencent.com/doc...</a></p>
<p>验证成功之后,才会给你下发证书。</p>
<h3>2 给Tomcat安装https证书</h3>
<h4>操作步骤</h4>
<p>参考文档:<a href="https://link.segmentfault.com/?enc=5GQtgVNKNkn8CK7dkF7o%2Bg%3D%3D.BT9iLESb3E0kSYPBZZs3AXsSaAa%2FeDucfwq3hXdPe%2FHipK1I6YgucP2IHcj1adJWXSH0FOR2Pwhgjftg5x%2FrPQ%3D%3D" rel="nofollow">https://cloud.tencent.com/doc...</a></p>
<h5>证书安装</h5>
<ol>
<li>
<p>已在 SSL 证书管理控制台 中下载并解压缩 <code>www.domain.com</code> 证书文件包到本地目录。<br>解压缩后,可获得相关类型的证书文件。其中包含 Tomcat 文件夹和 CSR 文件:</p>
<ul>
<li>
<strong>文件夹名称</strong>:Tomcat</li>
<li>
<p><strong>文件夹内容</strong>:</p>
<ul>
<li>
<code>www.domain.com.jks</code> 密钥库</li>
<li>
<code>keystorePass.txt</code> 密码文件(若已设置私钥密码,则无 <code>keystorePass.txt</code> 密码文件)</li>
</ul>
</li>
<li>
<p><strong>CSR 文件内容</strong>: <code>www.domain.com.csr</code> 文件</p>
<blockquote>说明:<p>CSR 文件是申请证书时由您上传或系统在线生成的,提供给 CA 机构。安装时可忽略该文件。</p>
</blockquote>
</li>
</ul>
</li>
<li>使用 “WinSCP” (即本地与远程计算机间的复制文件工具)登录 Tomcat 服务器。</li>
<li>将已获取到的 <code>www.domain.com.jks</code> 密钥库文件从本地目录拷贝至 <code>/usr/*/conf</code> 目录下。</li>
<li>远程登录 Tomcat 服务器。例如,使用 <a href="https://link.segmentfault.com/?enc=wWnWuctY61xUZ1%2BW2MHx5g%3D%3D.W8Ft7nzVnrqnAp0b7%2FV1SbA9ODd1F4NAcKNgCws4UpVrlzlAAYPAKm6yKnXY%2FsXUZM3KN2kBG4SJEZBgojGa%2B2gI1hZ%2Fls24HCUNQ3lA2vFKf29NZ6DpI%2BBXZjM2Niox" rel="nofollow">“PuTTY” 工具</a> 登录。</li>
<li>
<p>编辑在 <code>/usr/*/conf</code> 目录下的 <code>server.xml</code> 文件。添加如下内容:</p>
<pre><code><Connector port="443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="/usr/*/conf/www.domain.com.jks" #证书保存的路径
keystorePass="******"#密钥库密码
clientAuth="false"/></code></pre>
<p>详细 <code>server.xml</code> 文件请参考如下内容:</p>
<pre><code><?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Connector port="443" protocol="HTTP/1.1"
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false"
keystoreFile="/usr/*/conf/www.domain.com.jks"
keystorePass="******" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost=“www.domain.com">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name=“www.domain.com" appBase="webapps"
unpackWARs="true" autoDeploy="true" >
<Context path="" docBase ="Knews" />
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server></code></pre>
<p>配置文件的主要参数说明如下:</p>
<ul>
<li>
<strong>keystoreFile</strong>:密钥库文件的存放位置,可以指定绝对路径,也可以指定相对于 <CATALINA_HOME> (Tomcat安装目录)环境变量的相对路径。如果此项没有设定,默认情况下,Tomcat 将从当前操作系统用户的用户目录下读取名为 “.keystore” 的文件。</li>
<li>
<strong>keystorePass</strong>:密钥库密码,指定 keystore 的密码。申请证书时若设置了私钥密码,请填写私钥密码;若申请证书时未设置私钥密码,请填写 Tomcat 文件夹中 keystorePass.txt 文件的密码。</li>
<li>
<strong>clientAuth</strong>:如果设为 true,表示 Tomcat 要求所有的 SSL 客户出示安全证书,对 SSL 客户进行身份验证。</li>
</ul>
</li>
</ol>
<p>之后,重新启动Tomat,即可完成!</p>
<blockquote>文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的<strong>微信公众号</strong>:<code>好好学java</code>,获取优质学习资源。</blockquote>
InnoDB与MyISAM等存储引擎对比
https://segmentfault.com/a/1190000020383024
2019-09-14T18:17:04+08:00
2019-09-14T18:17:04+08:00
好好学java
https://segmentfault.com/u/sihai_5ae5ba9170388
9
<blockquote>文章原文:<a href="https://link.segmentfault.com/?enc=S9%2FGro1035x9aV40weckFw%3D%3D.uKFxfUfX0d%2BgDL3%2BuwpxY9UfHC2RZZRy81FEHkgMVhkSMWxKkMT2Nkzs6jGmNSbWQMA4da%2FDZCPrT6NKauGqIX5MiDmGl%2FJ3tTIhQ8m78R4%3D" rel="nofollow">blog.ouyangsihai.cn >> InnoDB与MyISAM等存储引擎对比</a>
</blockquote>
<h3>InnoDB存储引擎介绍</h3>
<p>InnoDB引擎是Mysql的默认的存储引擎,他有很多自己的特性,下面一一列举。</p>
<ul>
<li>
<strong>支持事务</strong>,InnoDB存储引擎主要就是为了在线事务处理(OLTP)的应用而设计的。</li>
<li>
<strong>行锁设计</strong>,支持外键,非锁定读。</li>
<li>支持多版本的并发控制(MVCC)来获得高并发性。</li>
<li>提供了插入缓冲、二次写、自适应哈希索引、预读等高性能和高可用的功能。</li>
</ul>
<p>上面这些算是 InnoDB 存储引擎的一些特点了,也是它的优势所在,为什么 InnoDB 引擎会使用如此广泛,就是因为它能有很好的性能。</p>
<h3>MyISAM储存引擎介绍</h3>
<ul>
<li>
<strong>不支持事务</strong>,它的设计目标是面向在线分析的应用(OLAP)。</li>
<li>支持全文索引。</li>
<li>
<strong>表锁设计</strong>。</li>
<li>它的缓冲池只缓冲<strong>索引文件</strong>,<strong>不缓冲数据文件</strong>,所以 MyISAM 存储引擎表由 <code>MYD</code> 和 <code>MYI</code> 组成,前者存储数据文件,后者存储索引文件。</li>
</ul>
<h3>存储引擎之间的对比</h3>
<p>这一部分,主要简要的介绍一下各个存储引擎之间的差别,及主要的作用及特点。</p>
<h4>特性对比</h4>
<table>
<thead><tr>
<th>特性</th>
<th>MyISAM</th>
<th>InnoDB</th>
<th>BDB</th>
<th>Memory</th>
<th>Archive</th>
<th>NDB</th>
</tr></thead>
<tbody>
<tr>
<td>存储限制</td>
<td>无</td>
<td>64TB</td>
<td>无</td>
<td>有</td>
<td>无</td>
<td>有</td>
</tr>
<tr>
<td>事务</td>
<td> </td>
<td>支持</td>
<td>支持</td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>锁级别</td>
<td>表锁</td>
<td>行锁</td>
<td>page</td>
<td>表</td>
<td>行</td>
<td>行</td>
</tr>
<tr>
<td>MVCC(并发控制)</td>
<td> </td>
<td>支持</td>
<td> </td>
<td> </td>
<td>支持</td>
<td>支持</td>
</tr>
<tr>
<td>全文索引</td>
<td>支持</td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>集群索引</td>
<td> </td>
<td>支持</td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>数据缓存和索引缓存</td>
<td> </td>
<td>支持</td>
<td> </td>
<td>支持</td>
<td> </td>
<td>支持</td>
</tr>
<tr>
<td>数据压缩</td>
<td>支持</td>
<td> </td>
<td> </td>
<td> </td>
<td>支持</td>
<td> </td>
</tr>
<tr>
<td>批量插入速度</td>
<td>高</td>
<td>低</td>
<td>高</td>
<td>高</td>
<td>很高</td>
<td>高</td>
</tr>
<tr>
<td>集群数据库支持</td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td>支持</td>
</tr>
<tr>
<td>外键支持</td>
<td> </td>
<td>支持</td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>适用场景</td>
<td>不需要事务的操作;插入、更新少,读取频繁;频繁的统计计算。</td>
<td>需要事务的操作;更新数据需要使用行级锁;大数据量读写;大型互联网应用。</td>
<td>类似 InnoDB</td>
<td>数据量不大,需要被频繁的访问,而且数据丢失不会对业务产生比较严重的影响。</td>
<td>存储引擎基本上用于数据归档,作为日志表</td>
<td>集群</td>
</tr>
</tbody>
</table>
<h4>存储引擎特性介绍</h4>
<table>
<thead><tr>
<th>存储引擎</th>
<th>主要特点</th>
</tr></thead>
<tbody>
<tr>
<td>BDB</td>
<td>可替代InnoDB的事务引擎,支持COMMIT、ROLLBACK和其他事务特性</td>
</tr>
<tr>
<td>Memory</td>
<td>数据存储在内存中,重启或崩溃,数据消失,使用哈希索引</td>
</tr>
<tr>
<td>Archive</td>
<td>只支持Insert和Select操作,支持索引,非常适合存储归档数据, 目标:高速插入和压缩功能</td>
</tr>
<tr>
<td>NDB</td>
<td>集群存储引擎,数据全部放在内存中,高可用、高性能的集群系统</td>
</tr>
<tr>
<td>Federated</td>
<td>不存放数据,只是指向一台远程MySQL数据库服务器上的表</td>
</tr>
<tr>
<td>Maria</td>
<td>新开发引擎,用于取代MyISAM存储引擎。 支持事务和非事务、缓存、索引文件、行锁、MVCC功能</td>
</tr>
</tbody>
</table>
<blockquote>文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的<strong>微信公众号</strong>:<code>好好学java</code>,获取优质学习资源。</blockquote>