面试官不讲武德,竟然问了我18个JVM问题!

前言

GC 对于Java 来说重要性不言而喻,不论是平日里对 JVM 的调优还是面试中的无情轰炸。

这篇文章我会以一问一答的方式来展开有关 GC 的内容。

本文章所说的 GC 实现没有特殊说明的话,默认指的是 HotSpot 的。

我先将十八个问题都列出来,大家可以先思考下能答出几道。

好了,开始表演。

young gc、old gc、full gc、mixed gc 傻傻分不清?

这个问题的前置条件是你得知道 GC 分代,为什么分代。这个在之前文章提了,不清楚的可以去看看。

现在我们来回答一下这个问题。

其实 GC 分为两大类,分别是 Partial GC 和 Full GC。

Partial GC 即部分收集,分为 young gc、old gc、mixed gc。

  • young gc:指的是单单收集年轻代的 GC。

  • old gc:指的是单单收集老年代的 GC。

  • mixed gc:这个是 G1 收集器特有的,指的是收集整个年轻代和部分老年代的 GC。

Full GC 即整堆回收,指的是收取整个堆,包括年轻代、老年代,如果有永久代的话还包括永久代。

其实还有 Major GC 这个名词,在《深入理解Java虚拟机》中这个名词指代的是单单老年代的 GC,也就是和 old gc 等价的,不过也有很多资料认为其是和 full gc 等价的。

还有 Minor GC,其指的就是年轻代的 gc。

young gc 触发条件是什么?

大致上可以认为在年轻代的 eden 快要被占满的时候会触发 young gc。

为什么要说大致上呢?因为有一些收集器的回收实现是在 full gc 前会让先执行以下 young gc。

比如 Parallel Scavenge,不过有参数可以调整让其不进行 young gc。

可能还有别的实现也有这种操作,不过正常情况下就当做 eden 区快满了即可。

eden 快满的触发因素有两个,一个是为对象分配内存不够,一个是为 TLAB 分配内存不够。

full gc 触发条件有哪些?

这个触发条件稍微有点多,我们来看下。

  • 在要进行 young gc 的时候,根据之前统计数据发现年轻代平均晋升大小比现在老年代剩余空间要大,那就会触发 full gc。

  • 有永久代的话如果永久代满了也会触发 full gc。

  • 老年代空间不足,大对象直接在老年代申请分配,如果此时老年代空间不足则会触发 full gc。

  • 担保失败即 promotion failure,新生代的 to 区放不下从 eden 和 from 拷贝过来对象,或者新生代对象 gc 年龄到达阈值需要晋升这两种情况,老年代如果放不下的话都会触发 full gc。

  • 执行 System.gc()、jmap -dump 等命令会触发 full gc。

知道 TLAB 吗?来说说看

这个得从内存申请说起。

一般而言生成对象需要向堆中的新生代申请内存空间,而堆又是全局共享的,像新生代内存又是规整的,是通过一个指针来划分的。

内存是紧凑的,新对象创建指针就右移对象大小 size 即可,这叫指针加法(bump [up] the pointer)。

可想而知如果多个线程都在分配对象,那么这个指针就会成为热点资源,需要互斥那分配的效率就低了。

于是搞了个 TLAB(Thread Local Allocation Buffer),为一个线程分配的内存申请区域。

这个区域只允许这一个线程申请分配对象,允许所有线程访问这块内存区域。

TLAB 的思想其实很简单,就是划一块区域给一个线程,这样每个线程只需要在自己的那亩地申请对象内存,不需要争抢热点指针。

当这块内存用完了之后再去申请即可。

这种思想其实很常见,比如分布式发号器,每次不会一个一个号的取,会取一批号,用完之后再去申请一批。

可以看到每个线程有自己的一块内存分配区域,短一点的箭头代表 TLAB 内部的分配指针。

如果这块区域用完了再去申请即可。

不过每次申请的大小不固定,会根据该线程启动到现在的历史信息来调整,比如这个线程一直在分配内存那么 TLAB 就大一些,如果这个线程基本上不会申请分配内存那 TLAB 就小一些。

还有 TLAB 会浪费空间,我们来看下这个图。

可以看到 TLAB 内部只剩一格大小,申请的对象需要两格,这时候需要再申请一块 TLAB ,之前的那一格就浪费了。

在 HotSpot 中会生成一个填充对象来填满这一块,因为堆需要线性遍历,遍历的流程是通过对象头得知对象的大小,然后跳过这个大小就能找到下一个对象,所以不能有空洞。

当然也可以通过空闲链表等外部记录方式来实现遍历。

还有 TLAB 只能分配小对象,大的对象还是需要在共享的 eden 区分配。

所以总的来说 TLAB 是为了避免对象分配时的竞争而设计的。

那 PLAB 知道吗?

可以看到和 TLAB 很像,PLAB 即 Promotion Local Allocation Buffers。

用在年轻代对象晋升到老年代时。

在多线程并行执行 YGC 时,可能有很多对象需要晋升到老年代,此时老年代的指针就“热”起来了,于是搞了个 PLAB。

先从老年代 freelist(空闲链表) 申请一块空间,然后在这一块空间中就可以通过指针加法(bump the pointer)来分配内存,这样对 freelist 竞争也少了,分配空间也快了。

大致就是上图这么个思想,每个线程先申请一块作为 PLAB ,然后在这一块内存里面分配晋升的对象。

这和 TLAB 的思想相似。

产生 concurrent mode failure 真正的原因

《深入理解Java虚拟机》:由于CMS收集器无法处理“浮动垃圾”(FloatingGarbage),有可能出现“Con-current Mode Failure”失败进而导致另一次完全“Stop The World”的Full GC的产生。

这段话的意思是因为抛这个错而导致一次 Full GC。

而实际上是 Full GC 导致抛这个错,我们来看一下源码,版本是 openjdk-8。

首先搜一下这个错。

再找找看  report_concurrent_mode_interruption 被谁调用。

查到是在 void CMSCollector::acquire_control_and_collect(...) 这个方法中被调用的。

再来看看 first_state :CollectorState first_state = _collectorState;

看枚举已经很清楚了,就是在 cms gc 还没结束的时候。

acquire_control_and_collect 这个方法是 cms 执行 foreground gc 的。

cms 分为  foreground gc 和 background gc。

foreground 其实就是 Full gc。

因此是 full gc 的时候 cms gc 还在进行中导致抛这个错。

究其原因是因为分配速率太快导致堆不够用,回收不过来因此产生 full gc。

也有可能是发起 cms gc 设置的堆的阈值太高。

CMS GC 发生 concurrent mode failure 时的 full GC 为什么是单线程的?

以下的回答来自 R 大。

因为没足够开发资源,偷懒了。就这么简单。没有任何技术上的问题。大公司都自己内部做了优化。

所以最初怎么会偷这个懒的呢?多灾多难的CMS GC经历了多次动荡。它最初是作为Sun Labs的Exact VM的低延迟GC而设计实现的。

但 Exact VM在与 HotSpot VM争抢 Sun 的正牌 JVM 的内部斗争中失利,CMS GC 后来就作为 Exact VM 的技术遗产被移植到了 HotSpot VM上。

就在这个移植还在进行中的时候,Sun 已经开始略显疲态;到 CMS GC 完全移植到 HotSpot VM 的时候,Sun 已经处于快要不行的阶段了。

开发资源减少,开发人员流失,当时的 HotSpot VM 开发组能够做的事情并不多,只能挑重要的来做。而这个时候 Sun Labs 的另一个 GC 实现,Garbage-First GC(G1 GC)已经面世。

相比可能在长时间运行后受碎片化影响的 CMS,G1 会增量式的整理/压缩堆里的数据,避免受碎片化影响,因而被认为更具潜力。

于是当时本来就不多的开发资源,一部分还投给了把G1 GC产品化的项目上——结果也是进展缓慢。

毕竟只有一两个人在做。所以当时就没能有足够开发资源去打磨 CMS GC 的各种配套设施的细节,配套的备份 full GC 的并行化也就耽搁了下来。

但肯定会有同学抱有疑问:HotSpot VM不是已经有并行GC了么?而且还有好几个?

让我们来看看:

  • ParNew:并行的young gen GC,不负责收集old gen。

  • Parallel GC(ParallelScavenge):并行的young gen GC,与ParNew相似但不兼容;同样不负责收集old gen。

  • ParallelOld GC(PSCompact):并行的full GC,但与ParNew / CMS不兼容。

所以…就是这么一回事。

HotSpot VM 确实是已经有并行 GC 了,但两个是只负责在 young GC 时收集 young gen 的,这俩之中还只有 ParNew 能跟 CMS 搭配使用;

而并行 full GC 虽然有一个 ParallelOld,但却与 CMS GC 不兼容所以无法作为它的备份 full GC使用。

为什么有些新老年代的收集器不能组合使用比如 ParNew 和 Parallel Old?

这张图是 2008 年 HostSpot 一位 GC 组成员画的,那时候 G1 还没问世,在研发中,所以画了个问号在上面。

里面的回答是 :

"ParNew" is written in a style... "Parallel Old" is not written in the "ParNew" style

HotSpot VM 自身的分代收集器实现有一套框架,只有在框架内的实现才能互相搭配使用。

而有个开发他不想按照这个框架实现,自己写了个,测试的成绩还不错后来被  HotSpot VM 给吸收了,这就导致了不兼容。

我之前看到一个回答解释的很形象:就像动车组车头带不了绿皮车厢一样,电气,挂钩啥的都不匹配。

新生代的 GC 如何避免全堆扫描?

在常见的分代 GC 中就是利用记忆集来实现的,记录可能存在的老年代中有新生代的引用的对象地址,来避免全堆扫描。

上图有个对象精度的,一个是卡精度的,卡精度的叫卡表。

把堆中分为很多块,每块 512 字节(卡页),用字节数组来中的一个元素来表示某一块,1表示脏块,里面存在跨代引用。

在 Hotspot 中的实现是卡表,是通过写后屏障维护的,伪代码如下。

cms 中需要记录老年代指向年轻代的引用,但是写屏障的实现并没有做任何条件的过滤。

即不判断当前对象是老年代对象且引用的是新生代对象才会标记对应的卡表为脏。

只要是引用赋值都会把对象的卡标记为脏,当然YGC扫描的时候只会扫老年代的卡表。

这样做是减少写屏障带来的消耗,毕竟引用的赋值非常的频繁。

那 cms 的记忆集和 G1 的记忆集有什么不一样?

cms 的记忆集的实现是卡表即 card table。

通常实现的记忆集是 points-out 的,我们知道记忆集是用来记录非收集区域指向收集区域的跨代引用,它的主语其实是非收集区域,所以是 points-out 的。

在 cms 中只有老年代指向年轻代的卡表,用于年轻代 gc。

而 G1 是基于 region 的,所以在 points-out 的卡表之上还加了个 points-into 的结构。

因为一个 region 需要知道有哪些别的 region 有指向自己的指针,然后还需要知道这些指针在哪些 card 中。

其实 G1 的记忆集就是个 hash table,key 就是别的 region 的起始地址,然后 value 是一个集合,里面存储这 card table 的 index。

我们来看下这个图就很清晰了。

像每次引用字段的赋值都需要维护记忆集开销很大,所以 G1 的实现利用了 logging write barrier(下文会介绍)。

也是异步思想,会先将修改记录到队列中,当队列超过一定阈值由后台线程取出遍历来更新记忆集。

为什么 G1 不维护年轻代到老年代的记忆集?

G1 分了 young GC 和 mixed gc。

young gc 会选取所有年轻代的 region 进行收集。

midex gc 会选取所有年轻代的 region 和一些收集收益高的老年代 region 进行收集。

所以年轻代的 region 都在收集范围内,所以不需要额外记录年轻代到老年代的跨代引用。

cms 和 G1 为了维持并发的正确性分别用了什么手段?

之前文章分析到了并发执行漏标的两个充分必要条件是:

  1. 将新对象插入已扫描完毕的对象中,即插入黑色对象到白色对象的引用。

  2. 删除了灰色对象到白色对象的引用。

cms 和 g1 分别通过增量更新和 SATB 来打破这两个充分必要条件,维持了 GC 线程与应用线程并发的正确性。

cms 用了增量更新(Incremental update),打破了第一个条件,通过写屏障将插入的白色对象标记成灰色,即加入到标记栈中,在 remark 阶段再扫描,防止漏标情况。

G1 用了 SATB(snapshot-at-the-beginning),打破了第二个条件,会通过写屏障把旧的引用关系记下来,之后再把旧引用关系再扫描过。

这个从英文名词来看就已经很清晰了。讲白了就是在 GC 开始时候如果对象是存活的就认为其存活,等于拍了个快照。

而且 gc 过程中新分配的对象也都认为是活的。每个 region 会维持 TAMS (top at mark start)指针,分别是 prevTAMS 和 nextTAMS 分别标记两次并发标记开始时候 Top 指针的位置。

Top 指针就是 region 中最新分配对象的位置,所以 nextTAMS 和 Top 之间区域的对象都是新分配的对象都认为其是存活的即可。

而利用增量更新的 cms 在 remark 阶段需要重新所有线程栈和整个年轻代,因为等于之前的根有新增,所以需要重新扫描过,如果年轻代的对象很多的话会比较耗时。

要注意这阶段是 STW 的,很关键,所以 CMS 也提供了一个 CMSScavengeBeforeRemark 参数,来强制 remark 阶段之前来一次 YGC。

而 g1 通过 SATB 的话在最终标记阶段只需要扫描 SATB 记录的旧引用即可,从这方面来说会比 cms 快,但是也因为这样浮动垃圾会比 cms 多。

什么是 logging write barrier ?

写屏障其实耗的是应用程序的性能,是在引用赋值的时候执行的逻辑,这个操作非常的频繁,因此就搞了个 logging write barrier。

把写屏障要执行的一些逻辑搬运到后台线程执行,来减轻对应用程序的影响。

在写屏障里只需要记录一个 log 信息到一个队列中,然后别的后台线程会从队列中取出信息来完成后续的操作,其实就是异步思想。

像 SATB write barrier ,每个 Java 线程有一个独立的、定长的 SATBMarkQueue,在写屏障里只把旧引用压入该队列中。满了之后会加到全局 SATBMarkQueueSet。

后台线程会扫描,如果超过一定阈值就会处理,开始 tracing。

在维护记忆集的写屏障也用了 logging write barrier 。

简单说下 G1 回收流程

G1 从大局上看分为两大阶段,分别是并发标记和对象拷贝。

并发标记是基于 STAB 的,可以分为四大阶段:

1、初始标记(initial marking),这个阶段是 STW 的,扫描根集合,标记根直接可达的对象即可。在G1中标记对象是利用外部的bitmap来记录,而不是对象头。

2、并发阶段(concurrent marking),这个阶段和应用线程并发,从上一步标记的根直接可达对象开始进行 tracing,递归扫描所有可达对象。STAB 也会在这个阶段记录着变更的引用。

3、最终标记(final marking), 这个阶段是 STW 的,处理 STAB 中的引用。

4、清理阶段(clenaup),这个阶段是 STW 的,根据标记的 bitmap 统计每个 region 存活对象的多少,如果有完全没存活的 region 则整体回收。

对象拷贝阶段(evacuation),这个阶段是 STW 的。

根据标记结果选择合适的 reigon 组成收集集合(collection set 即 CSet),然后将 CSet 存活对象拷贝到新 region 中。

G1 的瓶颈在于对象拷贝阶段,需要花较多的瓶颈来转移对象。

简单说下 cms 回收流程

其实从之前问题的 CollectorState 枚举可以得知几个流程了。

1、初始标记(initial mark),这个阶段是 STW 的,扫描根集合,标记根直接可达的对象即可。

2、并发标记(Concurrent marking),这个阶段和应用线程并发,从上一步标记的根直接可达对象开始进行 tracing,递归扫描所有可达对象。

3、并发预清理(Concurrent precleaning),这个阶段和应用线程并发,就是想帮重新标记阶段先做点工作,扫描一下卡表脏的区域和新晋升到老年代的对象等,因为重新标记是 STW 的,所以分担一点。

4、可中断的预清理阶段(AbortablePreclean),这个和上一个阶段基本上一致,就是为了分担重新标记标记的工作。

5、重新标记(remark),这个阶段是 STW 的,因为并发阶段引用关系会发生变化,所以要重新遍历一遍新生代对象、Gc Roots、卡表等,来修正标记。

6、并发清理(Concurrent sweeping),这个阶段和应用线程并发,用于清理垃圾。

7、并发重置(Concurrent reset),这个阶段和应用线程并发,重置 cms 内部状态。

cms 的瓶颈就在于重新标记阶段,需要较长花费时间来进行重新扫描。

cms 写屏障又是维护卡表,又得维护增量更新?

卡表其实只有一份,又得用来支持 YGC 又得支持 CMS 并发时的增量更新肯定是不够的。

每次 YGC 都会扫描重置卡表,这样增量更新的记录就被清理了。

所以还搞了个 mod-union table,在并发标记时,如果发生 YGC 需要重置卡表的记录时,就会更新  mod-union table 对应的位置。

这样 cms 重新标记阶段就能结合当时的卡表和  mod-union table 来处理增量更新,防止漏标对象了。

GC 调优的两大目标是啥?

分别是最短暂停时间和吞吐量。

最短暂停时间:因为 GC 会 STW 暂停所有应用线程,这时候对于用户而言就等于卡顿了,因此对于时延敏感的应用来说减少 STW 的时间是关键。

吞吐量:对于一些对时延不敏感的应用比如一些后台计算应用来说,吞吐量是关注的重点,它们不关注每次 GC 停顿的时间,只关注总的停顿时间少,吞吐量高。

举个例子:

方案一:每次 GC 停顿 100 ms,每秒停顿 5 次。

方案二:每次 GC 停顿 200 ms,每秒停顿 2 次。

两个方案相对而言第一个时延低,第二个吞吐高,基本上两者不可兼得。

所以调优时候需要明确应用的目标。

GC 如何调优

这个问题在面试中很容易问到,抓住核心回答。

现在都是分代 GC,调优的思路就是尽量让对象在新生代就被回收,防止过多的对象晋升到老年代,减少大对象的分配。

需要平衡分代的大小、垃圾回收的次数和停顿时间。

需要对 GC 进行完整的监控,监控各年代占用大小、YGC 触发频率、Full GC 触发频率,对象分配速率等等。

然后根据实际情况进行调优。

比如进行了莫名其妙的 Full GC,有可能是某个第三方库调了 System.gc。

Full GC 频繁可能是 CMS GC 触发内存阈值过低,导致对象分配不过来。

还有对象年龄晋升的阈值、survivor 过小等等,具体情况还是得具体分析,反正核心是不变的。

最后

其实还有关于 ZGC 的内容没有分析,别急, ZGC 的文章了一半了,之后会发。

有关 GC 的问题在面试中还是很常见的,其实来来回回就那么几样东西,记得我提到的抓住核心即可。

当然如果你有实际调优经历那更可,所以要抓住工作中的机会,如果发生异常情况请积极参与,然后勤加思考,这可都是实打实的实战经历。

当然如果你想知道更多的 GC 细节那就看源码吧,源码之中无秘密。

个人能力有限,如果有纰漏的地方请抓紧联系我,也欢迎加我好友,微信号:GG_Stone。

巨人的肩膀

https://segmentfault.com/a/1190000021394215?utm_source=tag-newest

https://blogs.oracle.com/jonthecollector/our-collectors

https://www.iteye.com/blog/user/rednaxelafx R大的博客

https://www.jianshu.com/u/90ab66c248e6 占小狼的博客


往期推荐

Java中不可或缺的59个小技巧,贼好用!


2万字长文包教包会 JVM 内存结构


2万字,看完这篇才敢说自己真的懂线程池!


关注我,每天陪你进步一点点!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545210.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2月第3周国内域名商TOP10:爱名网排名升至第八

IDC评述网(idcps.com)02月26日报道:根据WebHosting.info公布的最新数据显示,在2月第3周,国内域名商域名总量十强总体呈下降趋势。其中,降幅最大的是DNSPod,净减16,762个。另外,中国数…

Android JSON数据与实体类之间的相互转化-------GSON的简单用法

Android JSON数据与实体类之间的相互转化-------GSON的用法1_Gson的导入1.1_方法一:直接导入jar包1.2_方法二:引入依赖2_json形式的字符串互转实体对象2.1_json字符串与单个实体对象互转2.2_json与list互转3_遇到的问题3.1_前后端对象成员变量类型不一致…

5种SpringBoot热部署方式,你用哪种?

来源 | my.oschina.net/ruoli/blog/1590148Spring Boot 中 5 种热部署方式如下:1、模板热部署2、使用调试模式Debug实现热部署3、spring-boot-devtools4、Spring Loaded5、JRebel接下来我们分别来看。1、模板热部署在 Spring Boot 中,模板引擎的页面默认…

ContextMenu长按事件

/* ContextMenu菜单就是长按某一个组件,就会在屏幕的中间弹出ContextMenu,这里设置为长按文本框弹出ContextMenu菜单*/public class MyContextMenu extends AppCompatActivity {/** Called when the activity is first created. */final static int CONT…

熬夜都要看完的 Spring 干货!

在 Java 后端框架繁荣的今天,Spring 无疑是最最最火热,也是必不可少的开源框架,像腾讯、阿里、字节跳动等一线互联网公司都选择 Spring 作为基础的开发框架。而 Spring 生态圈里最让人兴奋的莫过于 Spring Boot 框架。他简化了使用 Spring 的…

2014值得期待的Erlang两本新书

在2014年的开头就有这样一个令人振奋的好消息,Erlang有一本新书即将出版 《The Erlang Runtime System》,其作者happi在2013年3月份发布了这本书的写作计划:"The plan is to have the book done by the end of 2013 and published early 2014. ",出版方是O’Reilly,依…

页面分栏LayoutInflater

/* 页面分栏*/ public class TabDemo extends TabActivity {/** Called when the activity is first created. */Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);TabHost tabHost getTabHost();LayoutInflater.from(this).inf…

这么简单的三目运算符,竟然这么多坑?

最近在一个业务改造中,使用三目运算符重构了业务代码,没想到测试的时候竟然发生 NPE 的问题。重构代码非常简单,代码如下:// 方法返回参数类型为 Integer // private Integer code; SimpleObj simpleObj new SimpleObj(); // 其…

用好MySQL的21个好习惯!

前言每一个好习惯都是一笔财富,本文分SQL后悔药, SQL性能优化,SQL规范优雅三个方向,分享写SQL的21个好习惯,谢谢阅读,加油哈~1. 写完SQL先explain查看执行计划(SQL性能优化)日常开发…

第四章图像增强

第四章图像增强1_图像增强的概念2_空间域增强2.1_图像增强的点运算2.1.1_灰度变换2.1.2_直方图均衡化2.1.3 直方图规定化1_图像增强的概念 什么是图像增强:图像增强是采用一系列技术去改善图像的视觉效果,或将图像转换成一种更适合于人或机器进行分析和处理的形式。…

springboot发送qq邮件

springboot发送qq邮件1_开启邮箱相关权限并获取邮箱授权码2_实现功能2.1_添加mail的依赖2.1.1_创建工程时添加2.1.2_在工程中添加2.2_配置文件application.properties配置相关信息2.3_实现代码1_开启邮箱相关权限并获取邮箱授权码 进入账户 开启POP3/SMTP服务并生成授权码 …

Spring Cloud Alibaba 深度解密!

说说吧,程序猿们,你们还有谁不是“单身G”?想要“赢取”白富美,当上CTO,走上人生巅峰,不努力怎么可以?别人疯狂购物,你疯狂学习,努力30天,向阿里P6迈进&#…

安卓连接真机调试

安卓连接真机调试一、打开开发者模式二、打开USB调试三、最后连接数据线这里使用荣耀20pro为例一、打开开发者模式 点击版本号,多点几下直到打开开发者模式 二、打开USB调试 系统与更新——>开发人员选项 三、最后连接数据线 连接数据线并选择MIDI模式

3W字!带你玩转「消息队列」

1. 消息队列解决了什么问题消息中间件是目前比较流行的一个中间件,其中RabbitMQ更是占有一定的市场份额,主要用来做异步处理、应用解耦、流量削峰、日志处理等等方面。1. 异步处理一个用户登陆网址注册,然后系统发短信跟邮件告知注册成功&…

okhttp_utils的使用以及与服务端springboot交互中遇到的问题

okhttp_utils的使用以及与服务端springboot交互中遇到的问题1_okhttp_utils在Android studio中的引入方法2_okhttputils的使用举例3_get和post的简单使用3_图片的上传3.1_单张图片的上传3.1.1_获取安卓本地图片问题3.1.2_okhttputils上传图片代码3.1.3_服务端接收图片3.2_单张图…

算法系列之图--DFS

深度优先搜索使用的策略是,只要与可能就在图中尽量“深入”。DFS总是对最近才发现的结点v出发边进行探索,知道该结点的所有出发边都被发现为止。一旦v的所有出发边都被发现了,搜索就回溯到v的前驱结点(v是经该结点才被发现的&…

这8种常见的SQL错误用法,你还在用吗?

来源 | yq.aliyun.com/articles/72501MySQL 在近几年仍然保持强劲的数据库流行度增长趋势。越来越多的客户将自己的应用建立在 MySQL 数据库之上,甚至是从 Oracle 迁移到 MySQL上来。但也存在部分客户在使用 MySQL 数据库的过程中遇到一些比如响应时间慢&#xff0c…

千万不要这样写代码!9种常见的OOM场景演示

《Java虚拟机规范》里规定除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生 OutOfMemoryError 异常的可能,我们本文就来演示一下这些错误的使用场景。一. StackOverflowError1.1 写个 bugpublic class StackOverflowErrorDemo {public static v…

MySQL数据库安装与配置详解

目录 一、概述 二、MySQL安装 三、安装成功验证 四、NavicatforMySQL下载及使用 一、概述 MySQL版本:5.7.17 下载地址:http://rj.baidu.com/soft/detail/12585.html?ald 客户端工具:NavicatforMySQL 绿色版下载地址:http://www.c…

求求你,不要再使用!=null判空了!

对于Java程序员来说,null是令人头痛的东西。时常会受到空指针异常(NPE)的骚扰。连Java的发明者都承认这是他的一项巨大失误。那么,有什么办法可以避免在代码中写大量的判空语句呢?有人说可以使用 JDK8提供的 Optional …